On Wednesday, 8 October 2014 at 11:25:25 UTC, Johannes Pfau wrote:
Am Tue, 07 Oct 2014 15:57:58 +0000
schrieb "Dmitry Olshansky" <[email protected]>:


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:

Awesome! This is exactly the kind of help I wanted.



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.

Hm... Probably we can create a template for this.


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.


Indeed, such code is fine in "user-space" but have no place in the library.

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.


I think this and (2) can be solved if we come up with solid support for RC-closures.

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.


Yeah, there are always outliers ;)

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

Interesting... it must be due to cURL callback-based API.
All in all, std.net.curl is a constant source of complaints, it may need some work to fix other issues anyway.

Reply via email to