www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Any way to track memory allocations?

reply wade Shen <swadenator gmail.com> writes:
I'm writing some performance sensitive code (real time processing) for which
I've tried to minimize the number of memory allocations by using preallocated
objects pools.  Unfortunately, during the course of running, it seems that lots
of memory is still getting allocated even though all explicit "new" and array
slicing operations have been moved out of the main processing loops.  As a
result the program is quite slow when garbage collection is enabled (but very
fast otherwise).

Is there a way to track down where memory is being allocated if I'm using
phobos? Any recommendations would be appreciated.

thanks,
wade
Feb 24 2009
parent reply Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Tue, Feb 24, 2009 at 12:00 PM, wade Shen <swadenator gmail.com> wrote:
 I'm writing some performance sensitive code (real time processing) for wh=
ich I've tried to minimize the number of memory allocations by using preall= ocated objects pools. =A0Unfortunately, during the course of running, it se= ems that lots of memory is still getting allocated even though all explicit= "new" and array slicing operations have been moved out of the main process= ing loops. =A0As a result the program is quite slow when garbage collection= is enabled (but very fast otherwise). Array slicing does not cause the GC to run. It is essentially free, which is a wonderful thing.
 Is there a way to track down where memory is being allocated if I'm using=
phobos? Any recommendations would be appreciated. This is something I have *always* wanted. Unfortunately I don't think it's possible to add such instrumentation without modifying and recompiling Phobos. Even if you're using D2, which uses druntime, there's no such thing. Hm, actually.. If you *are* using D2, you might be able to replace the GC interface with a "debug" interface that just forwards calls to the normal GC. This is a capability druntime inherited from the Tango runtime - the GC is separated from the rest of the standard library and can actually be replaced at link-time. I don't know if D2 links all three parts of druntime into a single library or not, though. Here are some other things that could be causing allocation: - Array appending (~=3D) can cause it; array concatenation (~) is guaranteed to cause it. - Array .dup. - Adding items to associative arrays. - In D2, nested functions/anonymous delegates can allocate memory secretly, unless you're careful and only pass them to "scope" delegate parameters.
Feb 24 2009
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Jarrett,


 Hm, actually..
 
 If you *are* using D2, you might be able to replace the GC interface
 with a "debug" interface that just forwards calls to the normal GC.
 This is a capability druntime inherited from the Tango runtime - the
 GC is separated from the rest of the standard library and can actually
 be replaced at link-time.  I don't know if D2 links all three parts of
 druntime into a single library or not, though.
 
Someone should make such a GC shell. You could add a BanAlloc(bool) option that would turn on an assert on any allocation. This could be set and cleared around the RT parts of the code.
Feb 24 2009
parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 24 Feb 2009 20:40:13 +0300, BCS <ao pathlink.com> wrote:

 Reply to Jarrett,


 Hm, actually..
  If you *are* using D2, you might be able to replace the GC interface
 with a "debug" interface that just forwards calls to the normal GC.
 This is a capability druntime inherited from the Tango runtime - the
 GC is separated from the rest of the standard library and can actually
 be replaced at link-time.  I don't know if D2 links all three parts of
 druntime into a single library or not, though.
Someone should make such a GC shell. You could add a BanAlloc(bool) option that would turn on an assert on any allocation. This could be set and cleared around the RT parts of the code.
Good idea!
Feb 24 2009
parent reply wade <swadenator gmail.com> writes:
Yes,
this would be a great idea, especially for RT production software.

wade

Denis Koroskin Wrote:

 On Tue, 24 Feb 2009 20:40:13 +0300, BCS <ao pathlink.com> wrote:
 
 Reply to Jarrett,


 Hm, actually..
  If you *are* using D2, you might be able to replace the GC interface
 with a "debug" interface that just forwards calls to the normal GC.
 This is a capability druntime inherited from the Tango runtime - the
 GC is separated from the rest of the standard library and can actually
 be replaced at link-time.  I don't know if D2 links all three parts of
 druntime into a single library or not, though.
Someone should make such a GC shell. You could add a BanAlloc(bool) option that would turn on an assert on any allocation. This could be set and cleared around the RT parts of the code.
Good idea!
Feb 24 2009
parent BCS <ao pathlink.com> writes:
Reply to wade,

 Yes,
 this would be a great idea, especially for RT production software.
 wade
 
 Denis Koroskin Wrote:
 
For production software it would get tricky. Are you better of doing the alloc or just failing?
Feb 24 2009
prev sibling next sibling parent wade <swadenator gmail.com> writes:
Thanks,

That's helpful.  I'm using D 1.0.  I've eliminated all array concats, dups,
etc. but there's still a significant amount of memory being allocated that I
can't seem to detect.

Perhaps, I can recompile an instrumented version of phobos...

wade

Jarrett Billingsley Wrote:

 On Tue, Feb 24, 2009 at 12:00 PM, wade Shen <swadenator gmail.com> wrote:
 I'm writing some performance sensitive code (real time processing) for which
I've tried to minimize the number of memory allocations by using preallocated
objects pools.  Unfortunately, during the course of running, it seems that lots
of memory is still getting allocated even though all explicit "new" and array
slicing operations have been moved out of the main processing loops.  As a
result the program is quite slow when garbage collection is enabled (but very
fast otherwise).
Array slicing does not cause the GC to run. It is essentially free, which is a wonderful thing.
 Is there a way to track down where memory is being allocated if I'm using
phobos? Any recommendations would be appreciated.
This is something I have *always* wanted. Unfortunately I don't think it's possible to add such instrumentation without modifying and recompiling Phobos. Even if you're using D2, which uses druntime, there's no such thing. Hm, actually.. If you *are* using D2, you might be able to replace the GC interface with a "debug" interface that just forwards calls to the normal GC. This is a capability druntime inherited from the Tango runtime - the GC is separated from the rest of the standard library and can actually be replaced at link-time. I don't know if D2 links all three parts of druntime into a single library or not, though. Here are some other things that could be causing allocation: - Array appending (~=) can cause it; array concatenation (~) is guaranteed to cause it. - Array .dup. - Adding items to associative arrays. - In D2, nested functions/anonymous delegates can allocate memory secretly, unless you're careful and only pass them to "scope" delegate parameters.
Feb 24 2009
prev sibling next sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Jarrett Billingsley wrote:

 On Tue, Feb 24, 2009 at 12:00 PM, wade Shen <swadenator gmail.com> wrote:
 I'm writing some performance sensitive code (real time processing) for 
which I've tried to minimize the number of memory allocations by using preallocated objects pools.  Unfortunately, during the course of running, it seems that lots of memory is still getting allocated even though all explicit "new" and array slicing operations have been moved out of the main processing loops.  As a result the program is quite slow when garbage collection is enabled (but very fast otherwise).
 
 Array slicing does not cause the GC to run.  It is essentially free,
 which is a wonderful thing.
 
 Is there a way to track down where memory is being allocated if I'm using 
phobos? Any recommendations would be appreciated.
 
 This is something I have *always* wanted.  Unfortunately I don't think
 it's possible to add such instrumentation without modifying and
 recompiling Phobos.  Even if you're using D2, which uses druntime,
 there's no such thing.
Might it be possible to use tangobos for this purpose and just add asserts in the GC code? Last time I looked it was pretty straightforward to recompile Tango.
Feb 24 2009
parent reply wade <swadenator gmail.com> writes:
Thanks again for all the help.  I found what the problem was and it wasn't
obvious (at least to me):

float[uint] arr;

foreach (uint k; arr.keys)
{
 ...
}

Changing this to:

foreach (uint k, float v; arr)
{
...
}

fixes the leak.  I guess the keys array is constructed on the fly?

wade

Lutger Wrote:

 Jarrett Billingsley wrote:
 
 On Tue, Feb 24, 2009 at 12:00 PM, wade Shen <swadenator gmail.com> wrote:
 I'm writing some performance sensitive code (real time processing) for 
which I've tried to minimize the number of memory allocations by using preallocated objects pools.  Unfortunately, during the course of running, it seems that lots of memory is still getting allocated even though all explicit "new" and array slicing operations have been moved out of the main processing loops.  As a result the program is quite slow when garbage collection is enabled (but very fast otherwise).
 
 Array slicing does not cause the GC to run.  It is essentially free,
 which is a wonderful thing.
 
 Is there a way to track down where memory is being allocated if I'm using 
phobos? Any recommendations would be appreciated.
 
 This is something I have *always* wanted.  Unfortunately I don't think
 it's possible to add such instrumentation without modifying and
 recompiling Phobos.  Even if you're using D2, which uses druntime,
 there's no such thing.
Might it be possible to use tangobos for this purpose and just add asserts in the GC code? Last time I looked it was pretty straightforward to recompile Tango.
Feb 24 2009
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
wade wrote:
 Thanks again for all the help.  I found what the problem was and it wasn't
obvious (at least to me):
 
 float[uint] arr;
 
 foreach (uint k; arr.keys)
 {
  ...
 }
 
 Changing this to:
 
 foreach (uint k, float v; arr)
 {
 ....
 }
 
 fixes the leak.  I guess the keys array is constructed on the fly?
 
 wade
Yup. It's a pity that we don't have, oh I don't know, some sort of efficient iterable interface that doesn't cause a heap allocation that the runtime could use instead *cough*hint*cough*andrei*cough*ranges*cough*. -- Daniel
Feb 24 2009
parent reply Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Tue, Feb 24, 2009 at 8:23 PM, Daniel Keep
<daniel.keep.lists gmail.com> wrote:

 Yup.

 It's a pity that we don't have, oh I don't know, some sort of efficient
 iterable interface that doesn't cause a heap allocation that the runtime
 could use instead *cough*hint*cough*andrei*cough*ranges*cough*.
You can use "foreach(k; aa)" and "foreach(_, v; aa)". The thing is they're not always substitutes for .keys and .values. For example, if you want to modify the AA during the foreach loop, you have to use something like "foreach(k; aa.keys)" since you need a "snapshot" of the keys as they were before you started modifying it.
Feb 24 2009
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Jarrett Billingsley wrote:
 On Tue, Feb 24, 2009 at 8:23 PM, Daniel Keep
 <daniel.keep.lists gmail.com> wrote:
 
 Yup.

 It's a pity that we don't have, oh I don't know, some sort of efficient
 iterable interface that doesn't cause a heap allocation that the runtime
 could use instead *cough*hint*cough*andrei*cough*ranges*cough*.
You can use "foreach(k; aa)" and "foreach(_, v; aa)". The thing is they're not always substitutes for .keys and .values. For example, if you want to modify the AA during the foreach loop, you have to use something like "foreach(k; aa.keys)" since you need a "snapshot" of the keys as they were before you started modifying it.
True; although I'm one of those people who stays up at night, plotting the gruesome demise of invisible allocations, so I tend to do manually create and destroy the storage for the keys. In a perfect world, we'd have extension methods, and then we could just do this: RangeType!(T)[] toArray(T)(ref T this) if isRange!(T) { ... } foreach( k ; aa.keys.toArray ) ... My own policy is that if you have to choose between two designs, one where you always allocate and the other where you can optionally allocate, go for the second. -- Daniel
Feb 24 2009
prev sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Tue, Feb 24, 2009 at 8:04 PM, wade <swadenator gmail.com> wrote:

 fixes the leak. =A0I guess the keys array is constructed on the fly?
Oh, yes X| .keys and .values of AAs are constructed whenever you use them.
Feb 24 2009
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
Jarrett Billingsley wrote:
 On Tue, Feb 24, 2009 at 12:00 PM, wade Shen <swadenator gmail.com> wrote:
 
 Is there a way to track down where memory is being allocated if I'm using
phobos? Any recommendations would be appreciated.
This is something I have *always* wanted. Unfortunately I don't think it's possible to add such instrumentation without modifying and recompiling Phobos. Even if you're using D2, which uses druntime, there's no such thing.
This has been on my "to do" list forever. One of these days I'll actually get to it :-)
 Hm, actually..
 
 If you *are* using D2, you might be able to replace the GC interface
 with a "debug" interface that just forwards calls to the normal GC.
 This is a capability druntime inherited from the Tango runtime - the
 GC is separated from the rest of the standard library and can actually
 be replaced at link-time.  I don't know if D2 links all three parts of
 druntime into a single library or not, though.
It does. Partially because the three-library approach seems to have problems on Linux (there are link errors even though everything needed is actually present). But the GC is still easily replaceable. In fact, D2 provides an undocumented way to redirect GC calls, but I don't think it will currently work for this form of interception--you'd end up with an endless loop. Sean
Feb 25 2009