www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - delegates and heap usage

reply Franciszek Czekala <home valentimex.com> writes:
What part of stack frame is exactly copied when a delegate is used? For
example what is the heap usage due to delegates usage in the following code?
The library reference speaks about the enclosing function but I do not think
that it suffices to copy the stack frame of the directly enclosing function.
Is the whole stack copied? What happens in the case of recursive calls?

import std.stdio;

void main(){
  int x = 0;
  alias void delegate () del_t ;

  auto dlg = new del_t[100] ;
  void f(int n){
    if (n<0) return;
    int y = n;
    int[1000] arr;
    void g(){
      x++;
      void h(){
        writeln(y,' ',x);
      }
      dlg[n] = &h;
      writeln("assigned, ", n);
    }
    g();
    f(n-1);
  }
  f(99);
  for(int i = 0; i<100; i++) dlg[i]();
}
Dec 01 2010
parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Franciszek Czekala <home valentimex.com> wrote:

 What part of stack frame is exactly copied when a delegate is used?
Generally, only the used variables. You can check the size of the closure's stack copy using GC.sizeOf(dg.ptr).
 For example what is the heap usage due to delegates usage in the
 following code?
Adding this code to the end of it: int n = 0; void* ptr = null; foreach ( e; dlg ) { if ( e.ptr != ptr ) { ptr = e.ptr; n += GC.sizeOf( e.ptr ); } } writeln( n ); We can see that 1600 bytes are used.
 The library reference speaks about the enclosing function but I do not  
 think
 that it suffices to copy the stack frame of the directly enclosing  
 function.
 Is the whole stack copied?
All enclosing functions need to have their stack frames copied. That is, not the whole stack, but any functions for which the delegate would be a nested function.
 What happens in the case of recursive calls?
int recurse( int delegate() dg ) { int n = 1; return GC.sizeOf( dg.ptr ) + ( dg() ? recurse( { return dg() - n; } ) : 0 ); } void main(){ writeln( recurse( { return 100; } ) ); } Outputs 1600. -- Simen
Dec 01 2010
parent reply Franciszek Czekala <home valentimex.com> writes:
I am not sure that this is a correct answer. 1600 bytes is just 100x2x8 bytes.
Since delegates are fat (double) pointers this is probably just the memory
occupied by dlg variables themselves, not the memory to which they point. Indeed
adding an extra z variable in my code to the g() function and using it in
writeln
does not change the output: it is still 1600 bytes.
Dec 01 2010
parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Franciszek Czekala <home valentimex.com> wrote:

 I am not sure that this is a correct answer. 1600 bytes is just 100x2x8  
 bytes.
 Since delegates are fat (double) pointers this is probably just the  
 memory
 occupied by dlg variables themselves, not the memory to which they  
 point. Indeed
 adding an extra z variable in my code to the g() function and using it  
 in writeln
 does not change the output: it is still 1600 bytes.
Wrong. The delegates would be 100x2x4 bytes, as DMD only produces 32-bit exes. The 16 bytes per delegate is because that's the smallest block the GC will allocate. Proof: long recurse( long delegate() dg ) { long n = 1; long m = 0; long r = 0; long s = 0; long t = 0; return GC.sizeOf( dg.ptr ) + ( dg() ? recurse( { return dg() - n + m - r + s + t; } ) : 0 ); } void main(){ writeln( recurse( { return 100L; } ) ); } Outputs 6400. -- Simen
Dec 01 2010