digitalmars.D - GC finalizer optimization.
- Dave (71/71) Apr 11 2006 Comments? Like, how can this break things?
- kris (4/41) Apr 11 2006 Heh heh heh ;-)
- Dave (4/93) Apr 11 2006 Argh - I forgot about needing to release synchronization resources or
- Dave (32/128) Apr 11 2006 Maybe all is not lost, change gcx.setFinalizer to:
- Sean Kelly (21/49) Apr 11 2006 This will optimize the code path for single-threaded programs, but I
Comments? Like, how can this break things?
By changing line 129 of phobos/internal/gc/gc.d from:
_gc.setFinalizer(p, &new_finalizer);
to:
///_gc.setFinalizer(p, &new_finalizer);
///
ClassInfo c = ci;
do
{
if (c.destructor)
{
_gc.setFinalizer(p, &new_finalizer);
}
c = c.base;
} while (c);
///
gives me about 3x performance in allocating class objects w/o a dtor
using the following code.
Before:
D::~this
C::~this
C::~this
0.829
After:
D::~this
C::~this
C::~this
0.258
;---
import std.date, std.stdio;
void main()
{
C c = new C;
D d = new D;
E e;
F f;
d_time st = getUTCtime();
for(int i = 0; i < 1_000_000; i++)
{
e = new E;
f = new F;
}
d_time et = getUTCtime();
writefln((et - st) / cast(double)TicksPerSecond);
}
class C
{
int i;
~this()
{
printf("C::~this\n");
}
}
class D : C
{
int i;
~this()
{
printf("D::~this\n");
}
}
class E
{
int i;
}
class F : E
{
int i;
}
Thanks,
- Dave
Apr 11 2006
Dave wrote:
Comments? Like, how can this break things?
By changing line 129 of phobos/internal/gc/gc.d from:
_gc.setFinalizer(p, &new_finalizer);
to:
///_gc.setFinalizer(p, &new_finalizer);
///
ClassInfo c = ci;
do
{
if (c.destructor)
{
_gc.setFinalizer(p, &new_finalizer);
}
c = c.base;
} while (c);
///
gives me about 3x performance in allocating class objects w/o a dtor
using the following code.
Before:
D::~this
C::~this
C::~this
0.829
After:
D::~this
C::~this
C::~this
0.258
Heh heh heh ;-)
Then, to identify "leaking" resources, the collector only has to check
if there's a finalizer set :)
Apr 11 2006
Argh - I forgot about needing to release synchronization resources or
zeroing the vptr. regardless of if there is a dtor or not...
Damn.
Dave wrote:
Comments? Like, how can this break things?
By changing line 129 of phobos/internal/gc/gc.d from:
_gc.setFinalizer(p, &new_finalizer);
to:
///_gc.setFinalizer(p, &new_finalizer);
///
ClassInfo c = ci;
do
{
if (c.destructor)
{
_gc.setFinalizer(p, &new_finalizer);
}
c = c.base;
} while (c);
///
gives me about 3x performance in allocating class objects w/o a dtor
using the following code.
Before:
D::~this
C::~this
C::~this
0.829
After:
D::~this
C::~this
C::~this
0.258
;---
import std.date, std.stdio;
void main()
{
C c = new C;
D d = new D;
E e;
F f;
d_time st = getUTCtime();
for(int i = 0; i < 1_000_000; i++)
{
e = new E;
f = new F;
}
d_time et = getUTCtime();
writefln((et - st) / cast(double)TicksPerSecond);
}
class C
{
int i;
~this()
{
printf("C::~this\n");
}
}
class D : C
{
int i;
~this()
{
printf("D::~this\n");
}
}
class E
{
int i;
}
class F : E
{
int i;
}
Thanks,
- Dave
Apr 11 2006
Dave wrote:Argh - I forgot about needing to release synchronization resources or zeroing the vptr. regardless of if there is a dtor or not... Damn.Maybe all is not lost, change gcx.setFinalizer to: void setFinalizer(void *p, GC_FINALIZER pFn) { // should be thread-safe - Threads.nthreads is // mutex'd in std/thread.d if(Thread.nthreads > 1) { synchronized (gcLock) { gcx.finalizer = pFn; gcx.doFinalize(p); } } else { gcx.finalizer = pFn; gcx.doFinalize(p); } } Now you get: Before: D::~this C::~this C::~this 0.827 After: D::~this C::~this C::~this 0.466 - DaveDave wrote:Comments? Like, how can this break things? By changing line 129 of phobos/internal/gc/gc.d from: _gc.setFinalizer(p, &new_finalizer); to: ///_gc.setFinalizer(p, &new_finalizer); /// ClassInfo c = ci; do { if (c.destructor) { _gc.setFinalizer(p, &new_finalizer); } c = c.base; } while (c); /// gives me about 3x performance in allocating class objects w/o a dtor using the following code. Before: D::~this C::~this C::~this 0.829 After: D::~this C::~this C::~this 0.258 ;--- import std.date, std.stdio; void main() { C c = new C; D d = new D; E e; F f; d_time st = getUTCtime(); for(int i = 0; i < 1_000_000; i++) { e = new E; f = new F; } d_time et = getUTCtime(); writefln((et - st) / cast(double)TicksPerSecond); } class C { int i; ~this() { printf("C::~this\n"); } } class D : C { int i; ~this() { printf("D::~this\n"); } } class E { int i; } class F : E { int i; } Thanks, - Dave
Apr 11 2006
Dave wrote:Dave wrote:This will optimize the code path for single-threaded programs, but I would advise against ever calling setFinalizer from user code. So far as I can tell, the setFinalizer call is an artifact of the days before D classes could have dtors. While you'd think setFinalizer sets a per-object finalizer pointer, it actually sets a bit flag indicating that p should be finalized and then sets a global pointer to the finalizer function. Thus: gc_setFinalizer( p, null ); gc_fullCollect(); will actually cause all orphaned objects to not be finalized during the collection, as the GC's finalizer pointer will be null. I think the GC should be restructured so that setting the global finalizer function is available as a separate option from the function that indicates p should be finalized. In fact, the finalizer should probably either be an established extern (C) function or set via gc_init, assuming it doesn't live inside the GC code. For Ares, I've decided to make the finalizer a named C function (rt_finalize), and GC allocator functions now accept a parameter to indicate whether the block should be finalized on deletion/collection. SeanArgh - I forgot about needing to release synchronization resources or zeroing the vptr. regardless of if there is a dtor or not... Damn.Maybe all is not lost, change gcx.setFinalizer to: void setFinalizer(void *p, GC_FINALIZER pFn) { // should be thread-safe - Threads.nthreads is // mutex'd in std/thread.d if(Thread.nthreads > 1) { synchronized (gcLock) { gcx.finalizer = pFn; gcx.doFinalize(p); } } else { gcx.finalizer = pFn; gcx.doFinalize(p); } }
Apr 11 2006









kris <foo bar.com> 