www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - skinny delegates

reply Steven Schveighoffer <schveiguy gmail.com> writes:
Would it be a valid optimization to have D remove the requirement for 
allocation when it can determine that the entire data structure of the 
item in question is an rvalue, and would fit into the data pointer part 
of the delegate?

Here's what I'm looking at:

auto foo(int x)
{
    return { return x + 10; };
}

In this case, D allocates a pointer on the heap to hold "x", and then 
return a delegate which uses the pointer to read x, and then return that 
plus 10.

However, we could store x itself in the storage of the pointer of the 
delegate. This removes an indirection, and also saves the heap allocation.

Think of it like "automatic functors".

Does it make sense? Would it be feasible for the language to do this? 
The type system already casts the delegate pointer to a void *, so it 
can't make any assumptions, but this is a slight break of the type system.

The two requirements I can think of are:
1. The data in question must fit into a word
2. It must be guaranteed that the data is not going to be mutated 
(either via the function or any other function). Maybe it's best to 
require the state to be const/immutable.

I've had several cases where I was tempted to not use delegates because 
of the allocation cost, and simply return a specialized struct, but it's 
so annoying to do this compared to making a delegate. Plus something 
like this would be seamless with normal delegates as well (in case you 
do need a real delegate).

-Steve
Jul 30 2018
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
wrote:
 Would it be a valid optimization to have D remove the 
 requirement for allocation when it can determine that the 
 entire data structure of the item in question is an rvalue, and 
 would fit into the data pointer part of the delegate?
Don't do that. It's valid in simple cases. As soon as you want to chain delegate pointers it falls apart. Also the delegate might require heap allocation to be memory correct.
Aug 02 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/2/18 8:42 AM, Stefan Koch wrote:
 On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer wrote:
 Would it be a valid optimization to have D remove the requirement for 
 allocation when it can determine that the entire data structure of the 
 item in question is an rvalue, and would fit into the data pointer 
 part of the delegate?
Don't do that. It's valid in simple cases.
Those are the cases I'm referring to.
 As soon as you want to chain delegate pointers it falls apart.
And so, don't do the skinny delegate optimization in that case?
 Also the delegate might require heap allocation to be memory correct.
Does the "might" depend on the caller or on the delegate implementation itself? The former would squash this idea. -Steve
Aug 02 2018
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 2 August 2018 at 12:57:02 UTC, Steven Schveighoffer 
wrote:
 On 8/2/18 8:42 AM, Stefan Koch wrote:
 On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
 wrote:
 Would it be a valid optimization to have D remove the 
 requirement for allocation when it can determine that the 
 entire data structure of the item in question is an rvalue, 
 and would fit into the data pointer part of the delegate?
Don't do that. It's valid in simple cases.
Those are the cases I'm referring to.
I meant it seems valid in simple cases, and I doubt you can distinguish between cases that work and cases which don't work with 100% accuracy.
 As soon as you want to chain delegate pointers it falls apart.
And so, don't do the skinny delegate optimization in that case?
Again the question is whether you can tell the cases apart with local information.
 Also the delegate might require heap allocation to be memory 
 correct.
Does the "might" depend on the caller or on the delegate implementation itself? The former would squash this idea.
I am not sure about that, it's just a gut feeling that skinny delegates will be breaking some invariant, I may be wrong. Coming up with specific rules and throwing them at the wall until they don't break anymore, is fine for applications used in limited controlled circumstances, I would not want to do it with a compiler which is used by an unknown number of users.
Aug 02 2018
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/2/18 9:21 AM, Stefan Koch wrote:
 On Thursday, 2 August 2018 at 12:57:02 UTC, Steven Schveighoffer wrote:
 On 8/2/18 8:42 AM, Stefan Koch wrote:
 On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer wrote:
 Would it be a valid optimization to have D remove the requirement 
 for allocation when it can determine that the entire data structure 
 of the item in question is an rvalue, and would fit into the data 
 pointer part of the delegate?
Don't do that. It's valid in simple cases.
Those are the cases I'm referring to.
I meant it seems valid in simple cases, and I doubt you can distinguish between cases that work and cases which don't work with 100% accuracy.
When the data needed in the delegate is all effectively immutable (or maybe *actually* immutable), I can't see how using a pointer to it is different than using a copy of it.
 As soon as you want to chain delegate pointers it falls apart.
And so, don't do the skinny delegate optimization in that case?
Again the question is whether you can tell the cases apart with local information.
This idea requires cooperation from the compiler, all the way down to code generation -- it's not really a lowering but a change in how the data is fetched from the context. At a minimum, I would say anything that only depends on immutables can be a valid use case. In order to prove effective immutability (anything that could possibly change elsewhere can't qualify for this), you would need a lot of local information. So maybe this only works if the type is actually immutable.
 Also the delegate might require heap allocation to be memory correct.
Does the "might" depend on the caller or on the delegate implementation itself? The former would squash this idea.
I am not sure about that, it's just a gut feeling that skinny delegates will be breaking some invariant, I may be wrong. Coming up with specific rules and throwing them at the wall until they don't break anymore, is fine for applications used in limited controlled circumstances, I would not want to do it with a compiler which is used by an unknown number of users.
I wasn't planning on that method of proof. What I wanted to do was start with the tightest possible, but easily provable constraints -- all data must be immutable -- and then relax as it makes sense. What I don't know is the implications on the optimizer or semantic analysis -- does the context pointer being really a pointer have any affect on those pieces? I also don't know what you meant by "memory correct". -Steve
Aug 02 2018
prev sibling next sibling parent Mike Franklin <slavo5150 yahoo.com> writes:
On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
wrote:

 In this case, D allocates a pointer on the heap to hold "x", 
 and then return a delegate which uses the pointer to read x, 
 and then return that plus 10.
Yeah, that seems like such a disproportionately heavy cost.
 Does it make sense?
I haven't though through the details, but if it can be done, I think it would be nice. I like when programming tools are more intelligent about things like this because after you become familiar with it and build confidence in it, you can just code your ass off and know that the compiler is doing right on your behalf. Mike
Aug 02 2018
prev sibling next sibling parent Mike Franklin <slavo5150 yahoo.com> writes:
On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
wrote:

 Does it make sense?
It is also nice for domains like embedded systems. It is common for embedded systems to only dynamically allocate during system initialization (depending on the application of course). Avoiding the allocation would make the pattern you describe available to systems that have this restriction, and it's always nice to remove such limitations. Mike
Aug 02 2018
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/30/18 5:02 PM, Steven Schveighoffer wrote:
 Does it make sense? Would it be feasible for the language to do this? 
 The type system already casts the delegate pointer to a void *, so it 
 can't make any assumptions, but this is a slight break of the type system.
One possibility to avoid typesystem breakage is to internally treat a delegate structure like this: struct Delegate(RT, PARAMS...) { RT function(PARAMS params) funcptr; union { void *ptr; immutable ubyte[(void*).sizeof] context; } } Whereby the compiler is informed that the context could just be a bunch of bytes. -Steve
Aug 02 2018
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
I suppose it's mostly for mutability, so if it's const, it can be 
optimized based on type information only:

auto foo(in int x)
{
    return { return x + 10; };
}
Aug 02 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/2/18 11:00 AM, Kagamin wrote:
 I suppose it's mostly for mutability, so if it's const, it can be 
 optimized based on type information only:
 
 auto foo(in int x)
 {
     return { return x + 10; };
 }
I'm not sure what you mean here. -Steve
Aug 02 2018
parent reply kinke <noone nowhere.com> writes:
On Thursday, 2 August 2018 at 15:12:10 UTC, Steven Schveighoffer 
wrote:
 On 8/2/18 11:00 AM, Kagamin wrote:
 I suppose it's mostly for mutability, so if it's const, it can 
 be optimized based on type information only:
 
 auto foo(in int x)
 {
     return { return x + 10; };
 }
I'm not sure what you mean here.
I think he's saying that the check for immutability could simply consist in checking that all captured variables (well, not too much room for a lot of them ;)) have a const type. It's definitely an interesting idea, and the obvious benefit over a library solution is that you wouldn't have to think about this optimization when writing a delegate; if the captured stuff happens to be const and fit into a pointer, the GC won't be bothered, nice.
Aug 02 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/2/18 3:57 PM, kinke wrote:
 On Thursday, 2 August 2018 at 15:12:10 UTC, Steven Schveighoffer wrote:
 On 8/2/18 11:00 AM, Kagamin wrote:
 I suppose it's mostly for mutability, so if it's const, it can be 
 optimized based on type information only:

 auto foo(in int x)
 {
     return { return x + 10; };
 }
I'm not sure what you mean here.
I think he's saying that the check for immutability could simply consist in checking that all captured variables (well, not too much room for a lot of them ;)) have a const type.
OK, yes, that's what I was thinking as well. On a 64-bit system, you could stuff 2 ints in there, which is a common theme for my code :)
 It's definitely an interesting idea, and the obvious benefit over a 
 library solution is that you wouldn't have to think about this 
 optimization when writing a delegate; if the captured stuff happens to 
 be const and fit into a pointer, the GC won't be bothered, nice.
Yeah, a library solution is opt-in, whereas if the compiler does it as an optimization, it's seamless (mostly invisible). And works in nogc when possible. -Steve
Aug 02 2018
parent reply kinke <noone nowhere.com> writes:
Leaking may be an issue. This currently works:

```
static const(int)* global;

auto foo(const int param)
{
     return { global = &param; return param + 10; };
}

void main()
{
     {
         int arg = 42;
         auto dg = foo(42);
         auto r = dg();
         assert(r == 52);
     }
     assert(*global == 42);
}
```

`global` would be dangling as soon as the delegate `dg` goes out 
of scope.
Aug 02 2018
parent kinke <noone nowhere.com> writes:
On Thursday, 2 August 2018 at 21:28:27 UTC, kinke wrote:
 Leaking may be an issue.
Ah, I guess that's why you mentioned the use-as-rvalue requirement.
Aug 02 2018
prev sibling next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
wrote:
 Would it be a valid optimization to have D remove the 
 requirement for allocation when it can determine that the 
 entire data structure of the item in question is an rvalue, and 
 would fit into the data pointer part of the delegate?

 Here's what I'm looking at:

 auto foo(int x)
 {
    return { return x + 10; };
 }

 In this case, D allocates a pointer on the heap to hold "x", 
 and then return a delegate which uses the pointer to read x, 
 and then return that plus 10.

 However, we could store x itself in the storage of the pointer 
 of the delegate. This removes an indirection, and also saves 
 the heap allocation.

 Think of it like "automatic functors".

 Does it make sense? Would it be feasible for the language to do 
 this? The type system already casts the delegate pointer to a 
 void *, so it can't make any assumptions, but this is a slight 
 break of the type system.

 The two requirements I can think of are:
 1. The data in question must fit into a word
 2. It must be guaranteed that the data is not going to be 
 mutated (either via the function or any other function). Maybe 
 it's best to require the state to be const/immutable.

 I've had several cases where I was tempted to not use delegates 
 because of the allocation cost, and simply return a specialized 
 struct, but it's so annoying to do this compared to making a 
 delegate. Plus something like this would be seamless with 
 normal delegates as well (in case you do need a real delegate).

 -Steve
I think the number of cases where you could optimize this is very small. And the complexity of getting the compiler to analyze cases to determine when this is possible would be very large. In addition, a developer can already do this explicitly if they want, i.e. auto foo(int x) { static struct DummyStructToMakeFunctionWithDelegateAbi { int passthru() const { return cast(int)&this; } } DummyStructToMakeFunctionWithDelegateAbi dummyStruct; auto dg = &dummyStruct.passthru; dg.ptr = cast(void*)(x + 10); // treat the void* pointer as an int value return dg; } void main(string[] args) { auto dg = foo(32); import std.stdio; writefln("dg() = %s", dg()); } It's definitely ugly but it works. This will print the number "42" as expected. This would be a case where DIP1011 extern(delegate) would come in handy :) i.e. extern(delegate) int passthru(void* ptr) { return cast(int)ptr; } int delegate() foo2(int x) { return &(cast(void*)(x + 10)).passthru; }
Aug 02 2018
next sibling parent Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 2 August 2018 at 16:21:58 UTC, Jonathan Marler wrote:
 On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
 wrote:
 Would it be a valid optimization to have D remove the 
 requirement for allocation when it can determine that the 
 entire data structure of the item in question is an rvalue, 
 and would fit into the data pointer part of the delegate?

 Here's what I'm looking at:

 auto foo(int x)
 {
    return { return x + 10; };
 }

 In this case, D allocates a pointer on the heap to hold "x", 
 and then return a delegate which uses the pointer to read x, 
 and then return that plus 10.

 However, we could store x itself in the storage of the pointer 
 of the delegate. This removes an indirection, and also saves 
 the heap allocation.

 Think of it like "automatic functors".

 Does it make sense? Would it be feasible for the language to 
 do this? The type system already casts the delegate pointer to 
 a void *, so it can't make any assumptions, but this is a 
 slight break of the type system.

 The two requirements I can think of are:
 1. The data in question must fit into a word
 2. It must be guaranteed that the data is not going to be 
 mutated (either via the function or any other function). Maybe 
 it's best to require the state to be const/immutable.

 I've had several cases where I was tempted to not use 
 delegates because of the allocation cost, and simply return a 
 specialized struct, but it's so annoying to do this compared 
 to making a delegate. Plus something like this would be 
 seamless with normal delegates as well (in case you do need a 
 real delegate).

 -Steve
I think the number of cases where you could optimize this is very small. And the complexity of getting the compiler to analyze cases to determine when this is possible would be very large. In addition, a developer can already do this explicitly if they want, i.e. auto foo(int x) { static struct DummyStructToMakeFunctionWithDelegateAbi { int passthru() const { return cast(int)&this; } } DummyStructToMakeFunctionWithDelegateAbi dummyStruct; auto dg = &dummyStruct.passthru; dg.ptr = cast(void*)(x + 10); // treat the void* pointer as an int value return dg; } void main(string[] args) { auto dg = foo(32); import std.stdio; writefln("dg() = %s", dg()); } It's definitely ugly but it works. This will print the number "42" as expected. This would be a case where DIP1011 extern(delegate) would come in handy :) i.e. extern(delegate) int passthru(void* ptr) { return cast(int)ptr; } int delegate() foo2(int x) { return &(cast(void*)(x + 10)).passthru; }
Actually, I'll do you one better. Here's a potential library function for it. I'm calling these types of delegates "value pointer delegates". // Assume this is in a library somewhere auto makeValuePtrDelegate(string valueName, string funcBody, T)(T value) { static struct DummyStruct { auto method() const { mixin("auto " ~ valueName ~ " = cast(T)&this;"); mixin (funcBody); } } DummyStruct dummy; auto dg = &dummy.method; dg.ptr = cast(void*)value; return dg; } auto foo(int x) { return makeValuePtrDelegate!("val", q{ return val + 10; })(x); } void main(string[] args) { auto dg = foo(32); import std.stdio; writefln("dg() = %s", dg()); }
Aug 02 2018
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/2/18 12:21 PM, Jonathan Marler wrote:
 On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer wrote:
 Would it be a valid optimization to have D remove the requirement for 
 allocation when it can determine that the entire data structure of the 
 item in question is an rvalue, and would fit into the data pointer 
 part of the delegate?

 Here's what I'm looking at:

 auto foo(int x)
 {
    return { return x + 10; };
 }

 In this case, D allocates a pointer on the heap to hold "x", and then 
 return a delegate which uses the pointer to read x, and then return 
 that plus 10.

 However, we could store x itself in the storage of the pointer of the 
 delegate. This removes an indirection, and also saves the heap 
 allocation.

 Think of it like "automatic functors".

 Does it make sense? Would it be feasible for the language to do this? 
 The type system already casts the delegate pointer to a void *, so it 
 can't make any assumptions, but this is a slight break of the type 
 system.

 The two requirements I can think of are:
 1. The data in question must fit into a word
 2. It must be guaranteed that the data is not going to be mutated 
 (either via the function or any other function). Maybe it's best to 
 require the state to be const/immutable.

 I've had several cases where I was tempted to not use delegates 
 because of the allocation cost, and simply return a specialized 
 struct, but it's so annoying to do this compared to making a delegate. 
 Plus something like this would be seamless with normal delegates as 
 well (in case you do need a real delegate).
I think the number of cases where you could optimize this is very small.  And the complexity of getting the compiler to analyze cases to determine when this is possible would be very large.
It's not that complicated, you just have to analyze how much data is needed from the context inside the delegate. First iteration, all of the data has to be immutable, so it should be relatively straightforward.
 In addition, a developer can already do this explicitly if they want, i.e.
 
 auto foo(int x)
 {
      static struct DummyStructToMakeFunctionWithDelegateAbi
      {
          int passthru() const { return cast(int)&this; }
      }
      DummyStructToMakeFunctionWithDelegateAbi dummyStruct;
      auto dg = &dummyStruct.passthru;
      dg.ptr = cast(void*)(x + 10); // treat the void* pointer as an int 
 value
      return dg;
 }
Yep, just make that dummyStruct static or else it will allocate, and it should work. The concern I have with doing it this way is all the breakage of the type system. -Steve
Aug 02 2018
parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 2 August 2018 at 17:21:47 UTC, Steven Schveighoffer 
wrote:
 On 8/2/18 12:21 PM, Jonathan Marler wrote:
 On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
 wrote:
 Would it be a valid optimization to have D remove the 
 requirement for allocation when it can determine that the 
 entire data structure of the item in question is an rvalue, 
 and would fit into the data pointer part of the delegate?

 Here's what I'm looking at:

 auto foo(int x)
 {
    return { return x + 10; };
 }

 In this case, D allocates a pointer on the heap to hold "x", 
 and then return a delegate which uses the pointer to read x, 
 and then return that plus 10.

 However, we could store x itself in the storage of the 
 pointer of the delegate. This removes an indirection, and 
 also saves the heap allocation.

 Think of it like "automatic functors".

 Does it make sense? Would it be feasible for the language to 
 do this? The type system already casts the delegate pointer 
 to a void *, so it can't make any assumptions, but this is a 
 slight break of the type system.

 The two requirements I can think of are:
 1. The data in question must fit into a word
 2. It must be guaranteed that the data is not going to be 
 mutated (either via the function or any other function). 
 Maybe it's best to require the state to be const/immutable.

 I've had several cases where I was tempted to not use 
 delegates because of the allocation cost, and simply return a 
 specialized struct, but it's so annoying to do this compared 
 to making a delegate. Plus something like this would be 
 seamless with normal delegates as well (in case you do need a 
 real delegate).
I think the number of cases where you could optimize this is very small.  And the complexity of getting the compiler to analyze cases to determine when this is possible would be very large.
It's not that complicated, you just have to analyze how much data is needed from the context inside the delegate. First iteration, all of the data has to be immutable, so it should be relatively straightforward.
After thinking about it more I suppose it wouldn't be that complicated to implement. For delegate literals, you already need to gather a list of all the data you need to put on the heap, and if it can all fit inside a pointer, then you can just put it there instead. On that note, I think if a developer wants to be sure that this optimization occurs in their code, they should explicitly use a library solution like the one in Ocean or the one I gave. If a developer relies on the optimization, then when it doesn't work you won't get any information as to why it couldn't perform the optimization (i.e. some data was mutable or were not r-values). Depending on the code, this failure will either be ignored or break some dependency on the optimization like nogc. With a library solution, it explicitly copies the data into the pointer so you'll get an explicit error message if it doesn't fit or has some other issue. Something else to consider is this would cause some discrepancy with the nogc attribute based on the platform's pointer width. By making this an optimization that you don't have to "opt-in", the developer may be unaware that their code is depending on this optimization that won't work on other platforms. Their code could become platform-dependent without them knowing. However, I suppose the counter-argument is that code that uses delegate literals with nogc would probably we aware of this, but still something to consider. In the end, I think that most if not all use cases would be better off using the library solution if they want this optimization. This allows the developer to "opt-in" or "opt-out" of this optimization and enables the compiler to provide error messages when the "opt-in" with incompatible usage.
Aug 03 2018
parent reply kinke <kinke libero.it> writes:
On Friday, 3 August 2018 at 14:46:59 UTC, Jonathan Marler wrote:
 After thinking about it more I suppose it wouldn't be that 
 complicated to implement.  For delegate literals, you already 
 need to gather a list of all the data you need to put on the 
 heap, and if it can all fit inside a pointer, then you can just 
 put it there instead.
Nope, immutability (and no escaping) are additional requirements, as each delegate copy has its own context then, as opposed to a single shared GC closure.
 In the end, I think that most if not all use cases would be 
 better off using the library solution if they want this 
 optimization.
I disagree.
Aug 03 2018
parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Friday, 3 August 2018 at 16:19:04 UTC, kinke wrote:
 On Friday, 3 August 2018 at 14:46:59 UTC, Jonathan Marler wrote:
 After thinking about it more I suppose it wouldn't be that 
 complicated to implement.  For delegate literals, you already 
 need to gather a list of all the data you need to put on the 
 heap, and if it can all fit inside a pointer, then you can 
 just put it there instead.
Nope, immutability (and no escaping) are additional requirements, as each delegate copy has its own context then, as opposed to a single shared GC closure.
Maybe you could provide an example or 2 to demonstrate why these would be requirements...we may have 2 different ideas on how this would be implemented.
 In the end, I think that most if not all use cases would be 
 better off using the library solution if they want this 
 optimization.
I disagree.
I'm not sure which part you disagree with. I was saying that with a library solution, you get the ability "opt-in"/"opt-out" of the optimization, do you think it should always be on and the developer shouldn't need to or care to opt out of it? Also, what about the developers that want to guarantee that the optimization is occuring? If they just stick a nogc around it then how would they determine which requirement they are violating for the optimization to occur?
Aug 03 2018
parent reply kinke <noone nowhere.com> writes:
On Friday, 3 August 2018 at 16:46:53 UTC, Jonathan Marler wrote:
 Maybe you could provide an example or 2 to demonstrate why 
 these would be requirements...we may have 2 different ideas on 
 how this would be implemented.
auto foo(/*mutable*/ int x) { return { return ++x; }; } void main() { auto dg = foo(42); auto dg_copy = dg; // with the optimization, dg_copy would have its own context // in the ptr field, based on the current state in dg (42) const r1 = dg(); const r2 = dg_copy(); // would be 43 with optimization assert(r1 == 43 && r2 == 44); }
 do you think it should always be on and the developer shouldn't 
 need to or care to opt out of it?
Yes, by enforcing it in the language. No knowledge about this optimization necessary, no extra syntax, no extra dependency.
 Also, what about the developers that want to guarantee that the 
 optimization is occuring?
If they do know about this optimization, they probably aren't noobs and IMO should be able to have a look at LLVM IR or assembly to check whether it is optimized. The only reason for wanting to enforce it coming to my mind ad-hoc is GC-free code (-betterC, bare metal), where nogc should do. But there are also GC-using delegates which could be optimized this way.
Aug 03 2018
next sibling parent kinke <noone nowhere.com> writes:
A slightly more complex example, illustrating that it wouldn't be 
enough to check that the delegate body itself doesn't mutate the 
captured variable:

```
int delegate() increment;

auto foo(int x)
{
    increment = () => ++x;
    return () => x;
}

void main()
{
     auto dg = foo(42);
     auto dg_copy = dg;

     assert(dg() == 42);

     assert(increment() == 43);
	assert(dg() == 43);
     assert(dg_copy() == 43);
}
```

In the end, I think it really boils down to that the optimized 
state would be per-delegate (and tied to its lifetime) instead of 
shared (as we see above, even across lambdas) and GC-managed (and 
so can happily escape, see one of my earlier posts). So all use 
of it as lvalue (taking the address, assigning, passing by ref 
etc.) isn't allowed in the delegate body itself, and to make sure 
no other lambda mutates it, it needs to be const.

 But there are also GC-using delegates which could be optimized 
 this way.
This should read: lambdas in a non- nogc parent function are optimization candidates too, and the lambda bodies can use the GC as well.
Aug 03 2018
prev sibling next sibling parent Jonathan Marler <johnnymarler gmail.com> writes:
On Friday, 3 August 2018 at 17:34:47 UTC, kinke wrote:
 On Friday, 3 August 2018 at 16:46:53 UTC, Jonathan Marler wrote:
 [...]
[...]
You're right, thanks for elaborting.
Aug 03 2018
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/3/18 1:34 PM, kinke wrote:
 On Friday, 3 August 2018 at 16:46:53 UTC, Jonathan Marler wrote:
 Maybe you could provide an example or 2 to demonstrate why these would 
 be requirements...we may have 2 different ideas on how this would be 
 implemented.
auto foo(/*mutable*/ int x) {    return { return ++x; }; } void main() {     auto dg = foo(42);     auto dg_copy = dg;     // with the optimization, dg_copy would have its own context     // in the ptr field, based on the current state in dg (42)     const r1 = dg();     const r2 = dg_copy(); // would be 43 with optimization     assert(r1 == 43 && r2 == 44); }
You don't even need to make a copy to show problems, the context isn't passed by reference: const r1 = dg(); const r2 = dg(); assert(r1 == 43 && r2 == 44); // would fail with optimization. -Steve
Aug 04 2018
parent reply kinke <noone nowhere.com> writes:
On Saturday, 4 August 2018 at 12:21:18 UTC, Steven Schveighoffer 
wrote:
 You don't even need to make a copy to show problems, the 
 context isn't passed by reference:

 const r1 = dg();
 const r2 = dg();

 assert(r1 == 43 && r2 == 44); // would fail with optimization.

 -Steve
This depends on the implementation; assuming that captured `x` represents the `*cast(int*) context` lvalue, this example would work.
Aug 04 2018
next sibling parent kinke <noone nowhere.com> writes:
Argh, should read `*cast(int*) &context`.
Aug 04 2018
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/4/18 9:07 AM, kinke wrote:
 On Saturday, 4 August 2018 at 12:21:18 UTC, Steven Schveighoffer wrote:
 You don't even need to make a copy to show problems, the context isn't 
 passed by reference:

 const r1 = dg();
 const r2 = dg();

 assert(r1 == 43 && r2 == 44); // would fail with optimization.
This depends on the implementation; assuming that captured `x` represents the `*cast(int*) context` lvalue, this example would work.
No, it depends on and is dictated by D's delegate system. The delegate receives the context pointer by value. If you wanted it to accept the context by reference, you would have to have something other than a D delegate. -Steve
Aug 04 2018
parent kinke <noone nowhere.com> writes:
On Saturday, 4 August 2018 at 13:52:54 UTC, Steven Schveighoffer 
wrote:
 No, it depends on and is dictated by D's delegate system. The 
 delegate receives the context pointer by value.
Absolutely right, thx for clarifying.
Aug 04 2018
prev sibling next sibling parent Mathias Lang <pro.mathias.lang gmail.com> writes:
On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
wrote:
 Would it be a valid optimization to have D remove the 
 requirement for allocation when it can determine that the 
 entire data structure of the item in question is an rvalue, and 
 would fit into the data pointer part of the delegate?

 Here's what I'm looking at:

 auto foo(int x)
 {
    return { return x + 10; };
 }
This is something I've been wanting for a long time as well. It was also implemented in Ocean ( https://github.com/sociomantic-tsunami/ocean/blob/e53ac93fbf3bfa9b2dceec1a2b6dc4a0ec7f78b2/src/ocean/core/Typ Convert.d#L249-L311 ). AFAIK it should be possible, although not trivial to do.
Aug 02 2018
prev sibling parent =?UTF-8?B?c291bGHDr21hbg==?= <sahmi.soulaimane gmail.com> writes:
On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
wrote:
 Would it be a valid optimization ... is an rvalue, and would 
 fit into the data pointer part of the delegate?
 ...
 -Steve
If you want to optimize for rvalues then why not just do like some other languages, namely Java and Objective-C? They just make these rvalue/const variables part of the delegate's stack essentially they just make a copy at the delegate construction. Java doesn't have value types and it only allows const variables (they call them "final") to be used by delegates only to be able to make a copy like I said. Objective-C defaults to const refs for closures but and require explicit captures for mutable refs to allocates these on the heap instead of just copying them the closure's stack.
Oct 26 2018