June 28, 2015 Re: auto ref is on the docket | ||||
---|---|---|---|---|
| ||||
Posted in reply to rsw0x | On Sunday, 28 June 2015 at 13:08:48 UTC, rsw0x wrote:
> On Sunday, 28 June 2015 at 11:06:01 UTC, Marc Schütz wrote:
>> On Sunday, 28 June 2015 at 07:36:41 UTC, Jonathan M Davis wrote:
>>> It makes no sense to pass rvalues by ref. The ref has to refer to a memory location so that a pointer can be passed underneath the hood, and rvalues don't qualify for that.
>>
>> I strongly disagree with that. This is an implementation detail. `ref` describes a specific semantic, namely that no copying (observable by a postblit being called) takes place,
>
> no, it means pass by reference.
> this sounds like some convoluted C++ doublespeak definition.
Well, try experimenting with LDC with -O3 -output-s on some test programs. You will be surprised ;-) There will often be no "reference" in the resulting output, especially with inlining.
The definition I used is not necessarily the best one, but it needs to be based on observable behaviour, not implementation specifics, otherwise compilers would have no freedom in optimizing.
|
June 29, 2015 Re: auto ref is on the docket | ||||
---|---|---|---|---|
| ||||
Posted in reply to Marc Schütz | On Sunday, 28 June 2015 at 10:50:10 UTC, Marc Schütz wrote:
> Thank you Jonathan, I think I (like kinke in his post above) finally understand your stance. So I guess from your POV the following would be an ideal situation (disregarding the need to avoid breaking changes):
>
> 1) `auto ref` for non-templates is required to make a function accept rvalue and lvalue refs alike.
> 2) `auto ref` for templates is changed to mean the same thing as 1).
> 3) `scope ref` prevents escaping of the reference.
>
> Neither of `auto ref` and `scope ref` would imply the other, because you want to treat them as orthogonal. Do I understand you right?
>
> The main problems with this is that they aren't in fact orthogonal: `auto ref` without `scope ref` almost only makes sense if you want to deliberately break something. Furthermore, `auto ref` is already taken for something else, and I'm not sure Walter would be too happy if we wanted to change its meaning for templates.
We definitely don't want to lose the current auto ref that we have with templates. It's critical for forwarding refness, even if we got that ability by accident. IIRC, we don't quite manage perfect forwarding with what we have (though off the top of my head, I don't recall what we're missing), but without auto ref, we definitely don't get there. So, auto ref, as it stands has proven to be surprisingly useful completely aside from efficiency concerns. Also, the fact that it takes rvalues by value can actually be more efficient than taking them by ref, depending on the code, since it can just move the value in that case, whereas it's tied to a temporary variable in the case of what would be the non-templated auto ref, and that could require copying the value at some point when having it be passed by value could have avoided the copy - though obviously, all of that template bloat comes at a cost, so it's definitely a tradeoff. But we don't want to lose the current auto ref regardless.
So, if we want to have an attribute which accepts both lvalues and rvalues for non-templated functions and be able to use that same attribute for templated functions, a new attribute is required (even if originally, auto ref was intended to work for both templated and non-templated functions). Otherwise, if we're fine with the template bloat, we can just use the proposed auto ref with non-templated functions and not worry about adding a new attribute. So, ideally, at this point, we'd probably add a new attribute, simply because it's the most flexible, but that does come at the cost of adding a new attribute, so it may not be worth it.
Regardless, all of that is orthogonal to the escaping issue, because the escaping issue exists for all ref variables (not just those which refer to temporary variables that rvalues were assigned to). So, whether a ref parameter accepts both lvalues and rvalues is separate from whether they escape. And if scope really prevented the ref from escaping, then you couldn't even return it, and we need to be able to return by ref, so scope ref really doesn't make sense in the context of preventing ref parameters from existing longer than their arguments. It's related, but it would be too restrictive if it truly prevented all escaping.
And as far as escaping references to ref parameters by returning them by ref goes, the recently added return attribute already takes care of that. So, arguably, it overlaps with what scope conceivably could or should do, but it does what's required for solving the escape problem for ref, and a more general scope solution is not required for that. It also allows us to distinguish between ensuring that a ref parameter does not exist longer than is safe (i.e. that it's only returned by ref if it's safe to do so) and preventing a ref parameter from escaping the function at all. So, if we were to ever add scope ref, then presumably that would have to do with other types of escaping than simply returning by ref. And whether you wanted to prevent a ref parameter from escaping - either by being returned by ref or by other means - isn't necessarily related to whether it refers to a temporary variable that was an rvalue, much as it's clearly more dangerous to escape anything which refers to a ref parameters if it refers to a temporary variable that won't last beyond the statement that the function was called in.
- Jonathan M Davis
|
June 29, 2015 Re: auto ref is on the docket | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On Monday, 29 June 2015 at 17:19:48 UTC, Jonathan M Davis wrote: > On Sunday, 28 June 2015 at 10:50:10 UTC, Marc Schütz wrote: >> The main problems with this is that they aren't in fact orthogonal: `auto ref` without `scope ref` almost only makes sense if you want to deliberately break something. Furthermore, `auto ref` is already taken for something else, and I'm not sure Walter would be too happy if we wanted to change its meaning for templates. > > We definitely don't want to lose the current auto ref that we have with templates. It's critical for forwarding refness, even if we got that ability by accident. > - snip - For the record, to avoid further misunderstandings: When I talked about "auto ref" I meant the proposed rvalue refs, not the existing syntax for templates. I don't have such a strong opinion about the latter, except that it is - and should stay - distinct from rvalue refs. > Regardless, all of that is orthogonal to the escaping issue, because the escaping issue exists for all ref variables (not just those which refer to temporary variables that rvalues were assigned to). So, whether a ref parameter accepts both lvalues and rvalues is separate from whether they escape. And if scope really prevented the ref from escaping, then you couldn't even return it, and we need to be able to return by ref, so scope ref really doesn't make sense in the context of preventing ref parameters from existing longer than their arguments. It's related, but it would be too restrictive if it truly prevented all escaping. It's not as strict as that. Returning an rvalue ref is not per se unsafe, because the temporary that it refers to lives until the end of the current statement, which can be longer than the current call. It can be safely passed to another function as an rvalue ref, e.g. in a UFCS chain. This is what the `return` attribute enables: it's a means of telling the callee to losen the restrictions on a scoped reference a bit, because the caller is aware of it and can handle it in a safe way. > And as far as escaping references to ref parameters by returning them by ref goes, the recently added return attribute already takes care of that. So, arguably, it overlaps with what scope conceivably could or should do, but it does what's required for solving the escape problem for ref, and a more general scope solution is not required for that. It also allows us to distinguish between ensuring that a ref parameter does not exist longer than is safe (i.e. that it's only returned by ref if it's safe to do so) and preventing a ref parameter from escaping the function at all. So, if we were to ever add scope ref, then presumably that would have to do with other types of escaping than simply returning by ref. And whether you wanted to prevent a ref parameter from escaping - either by being returned by ref or by other means - isn't necessarily related to whether it refers to a temporary variable that was an rvalue, much as it's clearly more dangerous to escape anything which refers to a ref parameters if it refers to a temporary variable that won't last beyond the statement that the function was called in. I already addressed this point in the last section of http://forum.dlang.org/post/udnrkqmfqeunsfgwscnk@forum.dlang.org |
June 29, 2015 Re: auto ref is on the docket | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On Monday, 29 June 2015 at 17:19:48 UTC, Jonathan M Davis wrote: > On Sunday, 28 June 2015 at 10:50:10 UTC, Marc Schütz wrote: >> Thank you Jonathan, I think I (like kinke in his post above) finally understand your stance. So I guess from your POV the following would be an ideal situation (disregarding the need to avoid breaking changes): >> >> 1) `auto ref` for non-templates is required to make a function accept rvalue and lvalue refs alike. >> 2) `auto ref` for templates is changed to mean the same thing as 1). >> 3) `scope ref` prevents escaping of the reference. >> >> Neither of `auto ref` and `scope ref` would imply the other, because you want to treat them as orthogonal. Do I understand you right? >> >> The main problems with this is that they aren't in fact orthogonal: `auto ref` without `scope ref` almost only makes sense if you want to deliberately break something. Furthermore, `auto ref` is already taken for something else, and I'm not sure Walter would be too happy if we wanted to change its meaning for templates. > > We definitely don't want to lose the current auto ref that we have with templates. It's critical for forwarding refness, even if we got that ability by accident. IIRC, we don't quite manage perfect forwarding with what we have (though off the top of my head, I don't recall what we're missing), but without auto ref, we definitely don't get there. So, auto ref, as it stands has proven to be surprisingly useful completely aside from efficiency concerns. I thought perfect forwarding had everything to do with efficiency concerns. At least, that's the motivation for in in C++ AFAICT/AFAIR. If not, (since C++ const& can bind to rvalues), passing by value would be perfectly reasonable. It seems to me that there's a lot of confusion in this thread. I think I understand what you're saying but I'm not sure everyone else does. So correct me if I'm wrong: your stance (which is how I understand how things should be done in D) is that if a person wants to bind an rvalue to a ref parameter for performance, they shouldn't; they should pass it by value since it'll either get constructed in place or moved, not copied. Essentially, what this guy said about C++ (not the original I read, that's now a 404): http://m.blog.csdn.net/blog/CPP_CHEN/17528669 The reason rvalue references even exist in C++11/14 is because rvalues can bind to const&. I remember hearing/reading Andrei say that D doesn't need them precisely because in D, they can't. Pass by value and you get the C++ behaviour when rvalue references are used: a move. Atila |
June 30, 2015 Re: auto ref is on the docket | ||||
---|---|---|---|---|
| ||||
Posted in reply to Atila Neves | On Monday, 29 June 2015 at 19:10:07 UTC, Atila Neves wrote:
> It seems to me that there's a lot of confusion in this thread. I think I understand what you're saying but I'm not sure everyone else does. So correct me if I'm wrong: your stance (which is how I understand how things should be done in D) is that if a person wants to bind an rvalue to a ref parameter for performance, they shouldn't; they should pass it by value since it'll either get constructed in place or moved, not copied.
>
> Essentially, what this guy said about C++ (not the original I read, that's now a 404):
>
> http://m.blog.csdn.net/blog/CPP_CHEN/17528669
>
> The reason rvalue references even exist in C++11/14 is because rvalues can bind to const&. I remember hearing/reading Andrei say that D doesn't need them precisely because in D, they can't. Pass by value and you get the C++ behaviour when rvalue references are used: a move.
In some cases even better, in-place construction. That's perfect - for rvalues, as you said. But for lvalues, a copy must be made - afaik, you don't have the option to explicitly move an lvalue into a byval-param in D as you can in C++. And even moving can be expensive if the type is big.
So for efficiency, template auto-ref is a nice option, but requires a template and leads to code-bloat if both lvalues and rvalues are used as arguments. For these reasons, we want to have the additional option to pass both lvalues and rvalues by ref.
Jonathan wants a non-templated function to specify explicitly whether it wants to accept an rvalue by ref, apparently to discriminate between 'output parameters' (mutable `ref`, lvalues only) and another category of `auto ref` parameters which also accept rvalues via lowering (I don't know how one would describe that category - to me it seems completely arbitrary and adds complexity, not to mention different interpretations by different developers).
I'm solely interested in the safety of binding rvalues to refs, meaning that the compiler should prevent these rvalue refs from being escaped (I'm not talking about returning the ref via `return ref`, which is safe anyway when propagating the rvalue/lvalue origin, but storing derived pointers in external state). That's where `scope ref` (unescapable reference) comes in. But I prefer the compiler to infer scope-ness automatically in the future and would thus like to allow binding rvalues to `ref` in general - for not-so-performance-critical functions taking a value type by reference (for some value types, copying just doesn't make sense - streams, loggers etc.).
|
June 30, 2015 Re: auto ref is on the docket | ||||
---|---|---|---|---|
| ||||
Posted in reply to Atila Neves | On Monday, 29 June 2015 at 19:10:07 UTC, Atila Neves wrote: > On Monday, 29 June 2015 at 17:19:48 UTC, Jonathan M Davis wrote: >> On Sunday, 28 June 2015 at 10:50:10 UTC, Marc Schütz wrote: >>> Thank you Jonathan, I think I (like kinke in his post above) finally understand your stance. So I guess from your POV the following would be an ideal situation (disregarding the need to avoid breaking changes): >>> >>> 1) `auto ref` for non-templates is required to make a function accept rvalue and lvalue refs alike. >>> 2) `auto ref` for templates is changed to mean the same thing as 1). >>> 3) `scope ref` prevents escaping of the reference. >>> >>> Neither of `auto ref` and `scope ref` would imply the other, because you want to treat them as orthogonal. Do I understand you right? >>> >>> The main problems with this is that they aren't in fact orthogonal: `auto ref` without `scope ref` almost only makes sense if you want to deliberately break something. Furthermore, `auto ref` is already taken for something else, and I'm not sure Walter would be too happy if we wanted to change its meaning for templates. >> >> We definitely don't want to lose the current auto ref that we have with templates. It's critical for forwarding refness, even if we got that ability by accident. IIRC, we don't quite manage perfect forwarding with what we have (though off the top of my head, I don't recall what we're missing), but without auto ref, we definitely don't get there. So, auto ref, as it stands has proven to be surprisingly useful completely aside from efficiency concerns. > > I thought perfect forwarding had everything to do with efficiency concerns. At least, that's the motivation for in in C++ AFAICT/AFAIR. If not, (since C++ const& can bind to rvalues), passing by value would be perfectly reasonable. I don't remember. It's been a while since I looked at it, so I'd have to look it up again to know quite what it is or exactly why it's needed. I do remember that without auto ref though, we're definitely not there. > It seems to me that there's a lot of confusion in this thread. I think I understand what you're saying but I'm not sure everyone else does. So correct me if I'm wrong: your stance (which is how I understand how things should be done in D) is that if a person wants to bind an rvalue to a ref parameter for performance, they shouldn't; they should pass it by value since it'll either get constructed in place or moved, not copied. > > Essentially, what this guy said about C++ (not the original I read, that's now a 404): > > http://m.blog.csdn.net/blog/CPP_CHEN/17528669 > > The reason rvalue references even exist in C++11/14 is because rvalues can bind to const&. I remember hearing/reading Andrei say that D doesn't need them precisely because in D, they can't. Pass by value and you get the C++ behaviour when rvalue references are used: a move. Yeah. In general, it's more efficient to pass rvalues by value, because the compiler can avoid copying them and simply move them (and at least some of the time, it doesn't even need to move them, though I don't know enough about how stuff is implemented at that level to know whether it can always avoid even having to move rvalue arguments; it can certainly avoid copying them though). If you pass in an rvalue by reference (via const& in C++ or by the proposed non-templated auto ref in D), then you actually increase the odds that the argument will have to be copied. And if no copy is needed anywhere, then I believe that passing an rvalue by ref and passing it by value end up being more or less equivalent. So, the end result is that it's not actually beneficial to pass an rvalue by reference rather than by value. The problem that you run into is that if you want a parameter to accept both lvalues and rvalues, then the more efficient thing for rvalues is to pass by value, whereas the more efficient thing to do for lvalues is to pass by reference, and const& in C++ forces you to pass by ref, whereas passing by value... forces passing by value. The same would go with the non-templated auto ref. However, the templated auto ref avoids that by inferring the refness of the argument and thus automatically accepts rvalues by value and lvalues by reference. So, you end up passing arguments efficiently regardless of whether they're lvalues or rvalues; it's just that it costs you a lot of template bloat in the process. But a lot of programmers are used to simply using const& everywhere in C++, because prior to C++11, rvalues couldn't generally be moved, because there were no move constructors, and C++ doesn't disallow stuff like having a class/struct on the stack having a pointer to one of its members (which D does disallow - or at least assumes you won't do), and so the best way to avoid extra copies was to ensure that you passed by const& as much as possible. But with C++11, the situation becomes much more complicated, and figuring out what the most efficient thing to do is not as straightforward. And AFAIK, C++ has nothing like our templated auto ref that allows you to efficiently accept both lvalues and rvalues (but maybe that's where perfect forwarding comes in; I don't know; I really need to read up on it), leaving you to figure out whether having a function take its arguments by value or const& is more efficient based on what your function does. Ultimately, it really doesn't seem like there's a simple rule of them that is going to give you efficiency in general. You need to understand the various pros and cons of how arguments are passed if you want to eke out extra efficiency. Though in general, if you simply avoid having particularly large structs on the stack, I would think that just passing arguments by value as the default would be the best way to go and worrying about how to pass more efficiently when profiling shows that it's necessary. But having auto ref for non-templated functions will give us another tool in the toolbox for adjusting how function arguments are passed in code that cares. Of course, if you have large structs for whatever reason, that makes the situation more complicated, but the best way to handle that will likely depend on your code. The only real question I see is whether it's worth using a new attribute (as opposed to auto ref) so that we can use it with templated functions as well, allowing folks to pass rvalues by reference with templated functions if they need to avoid the template bloat or it actually turns out that that's more efficient in that case. As I understand it though, it's pretty much always more efficient to pass rvalues by value. And if that's indeed the case, the only real gain that I see by using a new attribute is that it allows us to avoid template bloat when it needs to be avoided. - Jonathan M Davis |
June 30, 2015 Re: auto ref is on the docket | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | This is semi-off-topic, but I was playing around... and line 12 broke my personal record for the most insane line ever written, maybe someone else finds it amusing also: [asm.dlang.org] http://goo.gl/ChdbwD |
July 02, 2015 Re: auto ref is on the docket | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Monday, 22 June 2015 at 04:11:41 UTC, Andrei Alexandrescu wrote:
> Walter and I discussed what auto ref for templates should look like and reached the conclusion that an approach based on lowering would be best. I added a proposed lowering to https://github.com/D-Programming-Language/dmd/pull/4717.
>
> Andrei
A question from a place of ignorance- from the thread people say normal function and template function auto ref have different rules. This seems like a really bad idea. Is this necessary and why should this be acceptable? We should surely have normal functions be a simple subset of templates.
|
Copyright © 1999-2021 by the D Language Foundation