August 03, 2018
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?

August 03, 2018
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.
August 03, 2018
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.
August 03, 2018
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.
August 04, 2018
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
August 04, 2018
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.
August 04, 2018
Argh, should read `*cast(int*) &context`.
August 04, 2018
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
August 04, 2018
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.
October 26, 2018
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.
1 2 3
Next ›   Last »