www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal: ClassInfo hasFinalizer field

reply dsimcha <dsimcha yahoo.com> writes:
This post was inspired by my recent work optimizing D's garbage 
collector and the Port A Benchmark thread.

The current implementation of druntime flags **ALL** classes as having 
finalizers when allocated via the new operator, even if they don't.  The 
GC then calls an empty finalizer pointed to by class's vtable.  In 
certain GC benchmarks, calling all these empty finalizers actually adds 
up to a substantial (~15-20%) performance penalty.

For example, the tree benchmark provided by Bearophile back when I was 
working on GC optimizations (available at 
https://github.com/dsimcha/druntime/blob/master/gcBench/tree1.d) runs in 
about 44-45 seconds on my machine using stock DMD 2.053.  It creates 
tons of tiny class objects with no finalizers.  When I temporarily 
disable calling finalizers in the GC code, the runtime for this 
benchmark is cut to 36-37 seconds.  If this isn't low-hanging fruit, I 
don't know what is.

What we need is to include a hasFinalizer field in ClassInfo that is 
true iff a class or any of its ancestors has a non-empty finalizer. 
Then, the finalize flag in the GC can be set on allocating a class using 
the new operator only if this flag is true.  This would save the time 
spent calling empty finalizers in places where tons of small, 
finalizer-free classes are allocated, and probably result in a decent 
speedup.

I've filed an enhancement request to get a hasFinalizer field in 
Classinfo.  (http://d.puremagic.com/issues/show_bug.cgi?id=6103)  I am 
not familiar enough with the DMD codebase to do this myself, but I 
imagine it's a pretty simple enhancement for someone who is.  If someone 
else takes care of the compiler, I'll take care of the corresponding 
druntime changes.
Jun 04 2011
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/4/2011 9:58 AM, dsimcha wrote:
 The current implementation of druntime flags **ALL** classes as having
 finalizers when allocated via the new operator, even if they don't.

One issue is that if someone derives from a class and adds a finalizer.
Jun 04 2011
next sibling parent dsimcha <dsimcha yahoo.com> writes:
On 6/4/2011 11:45 PM, Walter Bright wrote:
 One issue is that if someone derives from a class and adds a finalizer.

But when a class is allocated via new, the classinfo for the exact type being allocated is passed. At least, that's the way I think it works.
Jun 04 2011
prev sibling next sibling parent dsimcha <dsimcha yahoo.com> writes:
On 6/4/2011 11:45 PM, Walter Bright wrote:
 One issue is that if someone derives from a class and adds a finalizer.

One issue I did forget, though, is that these "empty" finalizers do deallocate the object's monitor. (For those not familiar with these details, D classes contain a hidden monitor field that is null initially. When you do synchronized(someClass), the monitor is lazily initialized.) I guess we could get around this by setting the finalize bit in the monitor initialization code rather than upfront, for classes that don't do any other finalization. This would almost always be more efficient than the status quo since very few classes use monitors.
Jun 04 2011
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Sun, 05 Jun 2011 00:28:58 -0400, dsimcha <dsimcha yahoo.com> wrote:

 On 6/4/2011 11:45 PM, Walter Bright wrote:
 One issue is that if someone derives from a class and adds a finalizer.

One issue I did forget, though, is that these "empty" finalizers do deallocate the object's monitor. (For those not familiar with these details, D classes contain a hidden monitor field that is null initially. When you do synchronized(someClass), the monitor is lazily initialized.) I guess we could get around this by setting the finalize bit in the monitor initialization code rather than upfront, for classes that don't do any other finalization. This would almost always be more efficient than the status quo since very few classes use monitors.

They shouldn't. Monitors can be shared across objects.
Jun 04 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 05 Jun 2011 00:28:58 -0400, dsimcha <dsimcha yahoo.com> wrote:

 On 6/4/2011 11:45 PM, Walter Bright wrote:
 One issue is that if someone derives from a class and adds a finalizer.

One issue I did forget, though, is that these "empty" finalizers do deallocate the object's monitor. (For those not familiar with these details, D classes contain a hidden monitor field that is null initially. When you do synchronized(someClass), the monitor is lazily initialized.) I guess we could get around this by setting the finalize bit in the monitor initialization code rather than upfront, for classes that don't do any other finalization. This would almost always be more efficient than the status quo since very few classes use monitors.

What about this idea: Store the bit saying whether the class instance has any finalizers in the monitor field. That is, you still set the hasFinalizer bit in the GC block, but you check the monitor area. For example, if the monitor's lsb is set to 1 (not possible for a pointer), then the optimal case of no monitor, no finalizer is registered as true. What is the big slowdown in rt_finalize? The loop moving up the dtors? Or is it simply calling rt_finalize at all? -Steve
Jun 06 2011
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 05 Jun 2011 01:22:30 -0400, Robert Jacques <sandford jhu.edu>  
wrote:

 On Sun, 05 Jun 2011 00:28:58 -0400, dsimcha <dsimcha yahoo.com> wrote:

 On 6/4/2011 11:45 PM, Walter Bright wrote:
 One issue is that if someone derives from a class and adds a finalizer.

One issue I did forget, though, is that these "empty" finalizers do deallocate the object's monitor. (For those not familiar with these details, D classes contain a hidden monitor field that is null initially. When you do synchronized(someClass), the monitor is lazily initialized.) I guess we could get around this by setting the finalize bit in the monitor initialization code rather than upfront, for classes that don't do any other finalization. This would almost always be more efficient than the status quo since very few classes use monitors.

They shouldn't. Monitors can be shared across objects.

The destruction actually calls object._d_monitordelete, which uses reference counting to ensure sanity. -Steve
Jun 06 2011