Thread overview | |||||||
---|---|---|---|---|---|---|---|
|
April 11, 2006 GC finalizer optimization. | ||||
---|---|---|---|---|
| ||||
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 |
April 11, 2006 Re: GC finalizer optimization. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave | 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 :)
|
April 11, 2006 Re: GC finalizer optimization. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave |
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
|
April 11, 2006 Re: GC finalizer optimization. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave | 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 - Dave > 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 |
April 12, 2006 Re: GC finalizer optimization. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave | Dave wrote:
> 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);
> }
> }
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.
Sean
|
Copyright © 1999-2021 by the D Language Foundation