D2 weak references

Leandro Lucarella llucax at gmail.com
Wed Apr 22 12:48:19 PDT 2009


Leandro Lucarella, el 22 de abril a las 16:08 me escribiste:
> Jason House, el 22 de abril a las 13:31 me escribiste:
> > > > Unfortunately, that is not efficient. The state used by the GC is in
> > > > flux during the sweep phase, so there is no easy lockless way to access
> > > > that data. Having to acquire a lock on every weakref dereference
> > > > absolutely kills performance.
> > > 
> > > The sweep phase don't touch mark bits. I can't possible understand why
> > > you should synchronize access to a (temporarily) read-only variable.
> > 
> > I'm mostly regurgitating what Sean Kelly said. I know that if the sweep
> > deallocates at an object, the bits marking it become invalid. I don't
> > disagree that reading the bits without a lock should be possible, but
> > I also trust Sean's statement that it isn't easy. I'm trying to find
> > something that doesn't require reworking or rewriting large parts of
> > a garbage collector.
> 
> Sean's idea was, if I recall correctly, to move the sweep phase into the
> stop-the-world. My idea is different, and it doesn't need rewriting any
> large part of the collector. I think the change is pretty small (and can
> be easily applied to D1/Tango too).
> 
> I'm looking at the Tango code right now, and you know what? The unmarking
> is already done when all threads are paused, so all you need for this to
> work is a method to expose the mark bit. This can be really easily done by
> extending the BlkAttr defined values (or using a reserved value if this
> should not be a standard attribute) and ensuring that the mark bit is set
> when new pools/bins are allocated.
> 
> This patch (Tango) should do that (untested):
> ------>8------>8------>8------>8------>8------>8------>8------>8------>8------
> diff --git a/lib/gc/basic/gcx.d b/lib/gc/basic/gcx.d
> index a760465..fd21d44 100644
> --- a/lib/gc/basic/gcx.d
> +++ b/lib/gc/basic/gcx.d
> @@ -81,6 +81,7 @@ private
>          NO_SCAN  = 0b0000_0010,
>          NO_MOVE  = 0b0000_0100,
>          ALL_BITS = 0b1111_1111
> +        MARK_BIT = 0b1_0000_0000_0000_0000; // extension to query the mark bit
>      }
>  
>      extern (C) void* rt_stackBottom();
> @@ -519,7 +520,14 @@ class GC
>              Pool *pool = gcx.findPool(p);
>              assert(pool);
>  
> -            gcx.setBits(pool, cast(size_t)(p - pool.baseAddr) / 16, bits);
> +           uint biti = cast(size_t)(p - pool.baseAddr) / 16;
> +            gcx.setBits(pool, biti, bits);
> +           // Set the mark bit to maintain the invariant that live objects
> +           // always have the mark bit set, except when the collector is in
> +           // the mark phase (useful for weak reference implementations)
> +           // This is not done in setBits() to keep this bit read-only for
> +           // the user.
> +           pool.mark.set(biti)
>          }
>          return p;
>      }
> @@ -2576,6 +2584,8 @@ struct Gcx
>  //        if (pool.nomove.nbits &&
>  //            pool.nomove.test(biti))
>  //            bits |= BlkAttr.NO_MOVE;
> +        if (pool.mark.test(biti))
> +            bits |= BlkAttr.MARK_BIT;
>          return bits;
>      }
>  
> ------8<------8<------8<------8<------8<------8<------8<------8<------8<------

Woops! I already spotted a bug there =). The mark bit was only set if the
malloc was done passing bits. The solution has a performance penalty of a
"extra" pool lookup for malloc where attr == 0.

This is the fixed patch:

------>8------>8------>8------>8------>8------>8------>8------>8------>8------
diff --git a/lib/gc/basic/gcx.d b/lib/gc/basic/gcx.d
index a760465..940bc71 100644
--- a/lib/gc/basic/gcx.d
+++ b/lib/gc/basic/gcx.d
@@ -81,6 +81,7 @@ private
         NO_SCAN  = 0b0000_0010,
         NO_MOVE  = 0b0000_0100,
         ALL_BITS = 0b1111_1111
+        MARK_BIT = 0b1_0000_0000_0000_0000; // extension to query the mark bit
     }
 
     extern (C) void* rt_stackBottom();
@@ -514,13 +515,18 @@ class GC
         sentinel_init(p, size);
         gcx.log_malloc(p, size);
 
-        if (bits)
-        {
-            Pool *pool = gcx.findPool(p);
-            assert(pool);
+        Pool *pool = gcx.findPool(p);
+        assert(pool);
 
-            gcx.setBits(pool, cast(size_t)(p - pool.baseAddr) / 16, bits);
-        }
+        uint biti = cast(size_t)(p - pool.baseAddr) / 16;
+        if (bits)
+            gcx.setBits(pool, biti, bits);
+        // Set the mark bit to maintain the invariant that live objects
+        // always have the mark bit set, except when the collector is in
+        // the mark phase (useful for weak reference implementations)
+        // This is not done in setBits() to keep this bit read-only for
+        // the user.
+        pool.mark.set(biti)
         return p;
     }
 
@@ -2576,6 +2582,8 @@ struct Gcx
 //        if (pool.nomove.nbits &&
 //            pool.nomove.test(biti))
 //            bits |= BlkAttr.NO_MOVE;
+        if (pool.mark.test(biti))
+            bits |= BlkAttr.MARK_BIT;
         return bits;
     }
 
------8<------8<------8<------8<------8<------8<------8<------8<------8<------

-- 
Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/
----------------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------------
Bulletproof... I Wish I Was



More information about the Digitalmars-d mailing list