January 15, 2013 Re: Function scope arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On 01/15/13 15:09, Timon Gehr wrote:
> On 01/15/2013 01:44 PM, Artur Skawina wrote:
>> On 01/15/13 12:48, deadalnix wrote:
>>> On Tuesday, 15 January 2013 at 10:58:17 UTC, Artur Skawina wrote:
>>>> Different problem - lifetime. One approach would be to disallow escaping them (which in this case includes returning them) unless the compiler is able to do the right - ie the body of the function is available. Somewhat unorthodox, but could work. (The problem are not the trivial cases; it's the ones where the compiler has no idea which ref is escaped/returned at runtime)
>>>>
>>>
>>> The compiler should assume they may escape unless scope is specified.
>>
>> This is about /avoiding/ "hidden" heap allocations as much as possible. Having functions with 'ref' and 'auto-ref' args always trigger them is not ideal.
>>
>> 'lazy' args are already problematic enough. (there's currently no way to mark them as non-escaping, the compiler has to assume that the do -> the result is that they /always/ cause heap allocations and you have to use explicit scoped delegates instead, losing the syntax advantages)
>
> Actually lazy args are implicitly 'scope' and never allocate.
I wish. :)
Seriously though, I don't.
They can be escaped and they do allocate. They have to. The problem is just
that there currently is no way to tell the compiler i-know-what-i'm-doing
and avoid the heap allocated closures.
[if the behavior changed in newer (than my old gdc) compiler versions then such a change is bogus, as it would mean that stack objects could be escaped]
artur
|
January 15, 2013 Re: Function scope arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Artur Skawina | On 01/15/2013 04:03 PM, Artur Skawina wrote: > On 01/15/13 15:09, Timon Gehr wrote: >> On 01/15/2013 01:44 PM, Artur Skawina wrote: >>> On 01/15/13 12:48, deadalnix wrote: >>>> On Tuesday, 15 January 2013 at 10:58:17 UTC, Artur Skawina wrote: >>>>> Different problem - lifetime. One approach would be to disallow escaping >>>>> them (which in this case includes returning them) unless the compiler is >>>>> able to do the right - ie the body of the function is available. Somewhat >>>>> unorthodox, but could work. (The problem are not the trivial cases; it's the >>>>> ones where the compiler has no idea which ref is escaped/returned at runtime) >>>>> >>>> >>>> The compiler should assume they may escape unless scope is specified. >>> >>> This is about /avoiding/ "hidden" heap allocations as much as possible. Having >>> functions with 'ref' and 'auto-ref' args always trigger them is not ideal. >>> >>> 'lazy' args are already problematic enough. (there's currently no way to mark >>> them as non-escaping, the compiler has to assume that the do -> the result is >>> that they /always/ cause heap allocations and you have to use explicit scoped >>> delegates instead, losing the syntax advantages) >> >> Actually lazy args are implicitly 'scope' and never allocate. > > I wish. :) > > Seriously though, I don't. Me neither, but that is what happens. > They can be escaped and they do allocate. They have to. The problem is just > that there currently is no way to tell the compiler i-know-what-i'm-doing > and avoid the heap allocated closures. > No, there is no way to get a heap allocated closure from a lazy parameter. > [if the behavior changed in newer (than my old gdc) compiler versions then such a > change is bogus, as it would mean that stack objects could be escaped] > > artur > I think the behaviour has always been the same (at least with DMD). import std.stdio; int delegate() foo(lazy int x){ return ()=>x; } int delegate() escape(int x){ return foo(x); } void trash(){ int[2] x=1337; } void main(){ auto dg = escape(2); trash(); writeln(dg()); } $ dmd -run tt.d 1337 $ gdmd -run tt.d -1430461920 If the behaviour was as you suggest, the output would be: 2 |
January 15, 2013 Re: Function scope arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On 01/15/13 17:12, Timon Gehr wrote:
> On 01/15/2013 04:03 PM, Artur Skawina wrote:
>> On 01/15/13 15:09, Timon Gehr wrote:
>>> On 01/15/2013 01:44 PM, Artur Skawina wrote:
>>>> On 01/15/13 12:48, deadalnix wrote:
>>>>> On Tuesday, 15 January 2013 at 10:58:17 UTC, Artur Skawina wrote:
>>>>>> Different problem - lifetime. One approach would be to disallow escaping them (which in this case includes returning them) unless the compiler is able to do the right - ie the body of the function is available. Somewhat unorthodox, but could work. (The problem are not the trivial cases; it's the ones where the compiler has no idea which ref is escaped/returned at runtime)
>>>>>>
>>>>>
>>>>> The compiler should assume they may escape unless scope is specified.
>>>>
>>>> This is about /avoiding/ "hidden" heap allocations as much as possible. Having functions with 'ref' and 'auto-ref' args always trigger them is not ideal.
>>>>
>>>> 'lazy' args are already problematic enough. (there's currently no way to mark them as non-escaping, the compiler has to assume that the do -> the result is that they /always/ cause heap allocations and you have to use explicit scoped delegates instead, losing the syntax advantages)
>>>
>>> Actually lazy args are implicitly 'scope' and never allocate.
>>
>> I wish. :)
>>
>> Seriously though, I don't.
>
> Me neither, but that is what happens.
>
>> They can be escaped and they do allocate. They have to. The problem is just that there currently is no way to tell the compiler i-know-what-i'm-doing and avoid the heap allocated closures.
>>
>
> No, there is no way to get a heap allocated closure from a lazy parameter.
>
>> [if the behavior changed in newer (than my old gdc) compiler versions then such a change is bogus, as it would mean that stack objects could be escaped]
>>
>> artur
>>
>
> I think the behaviour has always been the same (at least with DMD).
>
> import std.stdio;
>
> int delegate() foo(lazy int x){
> return ()=>x;
> }
>
> int delegate() escape(int x){
> return foo(x);
> }
>
> void trash(){
> int[2] x=1337;
> }
>
> void main(){
> auto dg = escape(2);
> trash();
> writeln(dg());
> }
>
>
> $ dmd -run tt.d
> 1337
>
> $ gdmd -run tt.d
> -1430461920
>
> If the behaviour was as you suggest, the output would be:
> 2
The output is actually "2" here.
But after taking a closer look at the generated code I've now realized that the allocations I am seeing are originating from (the equivalent of) 'foo', not 'main'. Inlining confused me.
Note to myself: never assume the D compiler behaves sanely.
You are right - lazy args currently do *not* cause allocations. Thanks for the correction.
Unfortunately this makes the situations much worse, as the problem isn't just a performance issue, but a potential source of nasty bugs.
The fix would be straightforward - make 'lazy' create closures properly and avoid the unnecessary 'foo' allocations. Then the only thing needed would be a way to avoid those allocations, as in my real code I was actually semi-escaping them, at least as far as the compiler could tell. Oh well, I'll get used to the extra pair of braces. :)
artur
|
January 15, 2013 Re: Function scope arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On Tuesday, 15 January 2013 at 16:12:41 UTC, Timon Gehr wrote:
> No, there is no way to get a heap allocated closure from a lazy parameter.
>
>[…]
>
> I think the behaviour has always been the same (at least with DMD).
This is a hole in SafeD – please file it as such if it isn't in Bugzilla already.
David
|
Copyright © 1999-2021 by the D Language Foundation