On Phobos GC hunt

Johannes Pfau via Digitalmars-d digitalmars-d at puremagic.com
Wed Oct 8 04:25:21 PDT 2014


Am Tue, 07 Oct 2014 15:57:58 +0000
schrieb "Dmitry Olshansky" <dmitry.olsh at gmail.com>:

> 
> Instead we need to observe patterns and label it automatically 
> until the non-trivial subset remains. So everybody, please take 
> time and identify simple patterns and post back your ideas on 
> solution(s).
> 

I just had a look at all closure allocations and identified these
patterns:


1) Fixable by manually stack-allocating closure
   A delegate is passed to some function which stores this delegate and
   therefore correctly doesn't mark the parameter as scope. However,
   the lifetime of the stored delegate is still limited to the current
   function (e.g. it's stored in a struct instance, but on the stack).

   Can be fixed by creating a static struct{T... members; void
   doSomething(){access members}} instance on stack and passing
   &stackvar.doSomething as delegate.

2) Using delegates to add state to ranges
   ----
   return iota(dim).
     filter!(i => ptr[i])().
     map!(i => BitsSet!size_t(ptr[i], i * bitsPerSizeT))().
     joiner();
   ----
   This code adds state to ranges without declaring a new type: the ptr
   variable is not accessible and needs to be move into a closure.
   Declaring a custom range type is a solution, but not
   straightforward: If the ptr field is moved into the range a closure
   is not necessary. But if the range is copied, it's address changes
   and the delegate passed to map is now invalid.

3) Functions taking delegates as generic parameters
   receiveTimeout,receive,formattedWrite accept different types,
   including delegates. The delegates can all be scope to avoid the
   allocation but is void foo(T)(scope T) a good idea? The alternative
   is probably making an overload for delegates with scope attribute.

   (The result is that all functions calling receiveTimeout,... with a
   delegate allocate a closure)

4) Solvable with manual memory management
   Some specific functions can't be easily fixed, but the delegates
   they create have a well defined lifetime (for example spawn creates
   a delegate which is only needed at the startup of a new thread, it's
   never used again). These could be malloc+freed.

5) Design issue
   These functions generally create a delegate using variables passed
   in as parameters. There's no way to avoid closures here. Although
   manual allocation is an possible, the lifetime is undefined and can
   only be managed by the GC.

6) Other
   Two cases can be fixed by moving a buffer into a struct or moving a
   function out of a member function into it's surrounding class.


Also notable: 17 out of 35 cases are in std.net.curl. This is because
curl heavily uses delegates and wrapper delegates.


More information about the Digitalmars-d mailing list