Re: DIP 1016 and const ref parameters

2019-06-20 Thread Nicholas Wilson via Digitalmars-d-learn
On Wednesday, 19 June 2019 at 19:25:59 UTC, Jonathan M Davis 
wrote:


Aside from looking through the newsgroup/forum for discussions 
on DIPs, that's pretty much all you're going to find on that. 
Andrei's talk is the most up-to-date information that we have 
about this particular DIP.



The preliminary implementation is the most practicable up to date 
info there: https://github.com/dlang/dmd/pull/9817


Re: DIP 1016 and const ref parameters

2019-06-19 Thread XavierAP via Digitalmars-d-learn

On Thursday, 20 June 2019 at 00:30:35 UTC, Jonathan M Davis wrote:


Ultimately, if you want a function to accept both rvalues and 
lvalues as efficiently as posible, just templatize it and use 
auto ref.


I'm aware of auto ref, and I've used it to solve this same 
problem when I had a template, but as you say it works with 
templates only, not plain non-templated functions.


Or another possible optimization: in case the function is 
declared with const parameters (not ref), lvalues can be 
accessed by ref as an optimization; and rvalues can use the 
same trick they do now. As a consequence, no unnecessary 
copies ever -- thanks to const. This is even better because I 
don't have to type so many ref, which were never part of the 
algorithm but baby instructions for the dumb compiler (const & 
everywhere is one of the ugliest and most annoying things in 
C++).


Either way, const has nothing to do with any of this. You're 
free to mark a ref or auto ref parameter as const, but it has 
nothing to do with whether rvalues are accepted, and it will 
never have anything to do with whether rvalues are accepted. 
D's const is far too restrictive for it to make any sense to 
base rvalue stuff on it like they do in C++. The DIP has 
nothing do with const and everything to do with ref.

[...]
Regardless, the refness of a parameter is part of its type, and 
I'd be very surprised if it were ever changed so that any 
parameter that was not marked with ref was ever ref.


I know. That's why I look to the general solution to bind ref 
rvalues as a solution to bind const ref in particular. By the way 
it looks to me that most of the demand in the D community to bind 
to ref is for chain return ref functions. I wonder why no one in 
D is bothered about not being able to use const ref parameters 
easily, while in C++ everyone is bothered to do it, and passing a 
read-only struct/class by value won't pass by the least alert 
reviewer. My guess is that in D it's very often ranges that get 
passed, and these are passed as slices, which are by nature refs 
that don't reallocate, and can also be decorated const. Still the 
const ref concern stays more or less. Rust has a solution too (&) 
of course.


My new idea about const only would be a compiler optimization, 
not part of the language/ABI. The same way as RVO, which under 
the hood (but not at the ABI level) is implemented as changing 
the function signature completely. This const optimization would 
not change the function's ABI either.


However I see a practical problem to implement this optimization 
idea. RVO changes the function signature under the hood, but in 
the same way for every occurrence/call. This const optimization 
would need to change the signature, but only in the occurrences 
where the function is called with lvalues instead,not rvalues. So 
in practice turning every function with const struct parameters 
as a template; but under the hood, maintaining a single plan 
signature in the ABI. I wonder if this is as easy or feasible as 
RVO.


Re: DIP 1016 and const ref parameters

2019-06-19 Thread Jonathan M Davis via Digitalmars-d-learn
On Wednesday, June 19, 2019 4:45:04 PM MDT XavierAP via Digitalmars-d-learn 
wrote:
> On Wednesday, 19 June 2019 at 21:06:48 UTC, XavierAP wrote:
> > Now with an rvalue returned from get, interesting, no copy.
> > Still, I wonder what really happened. Again, moving between
> > stacks would still be work. And a different optimization can
> > explain this non copy, for example inlining.
>
> My guess as to what may be happening (I've never used a
> disassembler and I wasn't planning on starting today yet) is
> simple. The rvalue returned by get() is possibly not popped out
> from the stack, but rather left in the same place as sum() is
> called on it. This is actually optimum, but hardly an
> optimization, rather it's the easiest and least effort for the
> compiler. Again this would mean no moving -- which is good,
> because moving is work.
>
> And also, this doesn't work in the general case. If parameters
> are by value, everything works perfect when I pass rvalues (but
> we already knew that, not answering my question); however if I
> pass lvalues they will be copied every time.
>
> So my question is open... what about const ref rvalue parameters?
>
> Or another possible optimization: in case the function is
> declared with const parameters (not ref), lvalues can be accessed
> by ref as an optimization; and rvalues can use the same trick
> they do now. As a consequence, no unnecessary copies ever --
> thanks to const. This is even better because I don't have to type
> so many ref, which were never part of the algorithm but baby
> instructions for the dumb compiler (const & everywhere is one of
> the ugliest and most annoying things in C++).

If you want to know the current state of passing rvalues by ref, watch
Andrei's talk. If you've already watched it, then you know what the current
plan is, and I don't understand what you're trying to find out. Until the
DIP is implemented, if you want a function to accept lvalues by ref and
still accept rvalues, then use auto ref. e.g.

void foo(T)(auto ref T t) {...}

The refness of the parameter will be inferred based on whether it's given an
lvalue or rvalue. If a templated function doesn't fit the bill, then you
have to overload the function based on ref. e.g.

void foo(ref T t) {...}

void foo(T t) {...}

Either way, const has nothing to do with any of this. You're free to mark a
ref or auto ref parameter as const, but it has nothing to do with whether
rvalues are accepted, and it will never have anything to do with whether
rvalues are accepted. D's const is far too restrictive for it to make any
sense to base rvalue stuff on it like they do in C++. The DIP has nothing do
with const and everything to do with ref.

Regardless, the refness of a parameter is part of its type, and I'd be very
surprised if it were ever changed so that any parameter that was not marked
with ref was ever ref. Certainly, it wouldn't work for the refness to change
based on the argument, because the refness is part of the function's type
and determines how the generated code passes the argument. The closest that
we have and likely ever will have to the same function accepting by ref and
by value is auto ref, and that only works, because it's a template, and
different versions of the function are generated based on what's passed.
It's never the case that the exact same function takes some values by ref
and some not. Even once ref parameters accept rvalues, they're going to have
to be turned into lvalues underneath the hood for that to work. It just
won't require you to do it explicitly anymore, and the lifetime of the
generated lvalue will be the same as any temporary.

Ultimately, if you want a function to accept both rvalues and lvalues as
efficiently as posible, just templatize it and use auto ref.

- Jonathan M Davis





Re: DIP 1016 and const ref parameters

2019-06-19 Thread Les De Ridder via Digitalmars-d-learn

On Wednesday, 19 June 2019 at 20:18:58 UTC, Max Haughton wrote:
On Wednesday, 19 June 2019 at 19:25:59 UTC, Jonathan M Davis 
wrote:
On Wednesday, June 19, 2019 12:28:12 PM MDT XavierAP via 
Digitalmars-d-learn wrote:

[...]


The DIPs are here: https://github.com/dlang/DIPs

[...]


DIP1014 has not been implemented in DMD or druntime yet, AFAIK


I have two open PRs for the druntime[1] and Phobos[2] 
implementations,

but no dmd yet.

[1] https://github.com/dlang/druntime/pull/2638
[2] https://github.com/dlang/phobos/pull/7075


Re: DIP 1016 and const ref parameters

2019-06-19 Thread XavierAP via Digitalmars-d-learn

On Wednesday, 19 June 2019 at 21:06:48 UTC, XavierAP wrote:


Now with an rvalue returned from get, interesting, no copy. 
Still, I wonder what really happened. Again, moving between 
stacks would still be work. And a different optimization can 
explain this non copy, for example inlining.


My guess as to what may be happening (I've never used a 
disassembler and I wasn't planning on starting today yet) is 
simple. The rvalue returned by get() is possibly not popped out 
from the stack, but rather left in the same place as sum() is 
called on it. This is actually optimum, but hardly an 
optimization, rather it's the easiest and least effort for the 
compiler. Again this would mean no moving -- which is good, 
because moving is work.


And also, this doesn't work in the general case. If parameters 
are by value, everything works perfect when I pass rvalues (but 
we already knew that, not answering my question); however if I 
pass lvalues they will be copied every time.


So my question is open... what about const ref rvalue parameters?

Or another possible optimization: in case the function is 
declared with const parameters (not ref), lvalues can be accessed 
by ref as an optimization; and rvalues can use the same trick 
they do now. As a consequence, no unnecessary copies ever -- 
thanks to const. This is even better because I don't have to type 
so many ref, which were never part of the algorithm but baby 
instructions for the dumb compiler (const & everywhere is one of 
the ugliest and most annoying things in C++).


Re: DIP 1016 and const ref parameters

2019-06-19 Thread XavierAP via Digitalmars-d-learn
Hmmm I know about move semantics, and C++11 etc. I just don't 
know how related all that is to my original question. :)


On Wednesday, 19 June 2019 at 19:25:59 UTC, Jonathan M Davis 
wrote:
though if I understand correctly with RVO, it may just place 
the return value outside of the function in the first place to 
avoid needing to move.


Indeed, unrelated:
https://dlang.org/glossary.html#nrvo


func(foo(), bar(42), baz("hello"));

assuming that none of these functions return by ref, func is 
taking temporaries from several functions, and the spec 
actually guarantees that they will not be copied.


Where does the spec say this?? (This case is actually my 
question.)


However, please understand that naive moving is not an answer for 
me. Moving is still work! It would still be more efficient if 
foo's parameters were references/pointers -- if D functions were 
able to bind rvalues as such.


Theoretically a compiler could optimize by realizing that if a 
value parameter is not modified by the function (and it doesn't 
fit in a register etc), it can be read at its original 
location/address in the caller's stack, i.e. by 
reference/pointer. Again, nothing to do with moving. But I really 
doubt the D compilers do this, first because C++ probably don't, 
or else all C++ programmers are wasting their fingers and screen 
real state typing const & zillions of times; and second because D 
would not be able to bind as ref in case the argument happened to 
be an rvalue.

__

OK so I try to experiment myself:

/**/
struct XY
{
int x, y;
~this() { writeln("Destroyed!"); }
}

int sum(XY p) { return p.x + p.y; }

void main()
{
XY p;
p.sum;
}
/**/

Destroyed!
Destroyed!

Note that the compiler didn't realize that p isn't needed after 
the last statement of main, as you thought.


/**/

XY get()
{
XY p;
return p;
}
void main()
{
get.sum;
}
/**/

Destroyed!

Now with an rvalue returned from get, interesting, no copy. 
Still, I wonder what really happened. Again, moving between 
stacks would still be work. And a different optimization can 
explain this non copy, for example inlining.

__

Again, does the spec really mention any of this moving or 
eliding? I have found nothing.


Re: DIP 1016 and const ref parameters

2019-06-19 Thread Max Haughton via Digitalmars-d-learn
On Wednesday, 19 June 2019 at 19:25:59 UTC, Jonathan M Davis 
wrote:
On Wednesday, June 19, 2019 12:28:12 PM MDT XavierAP via 
Digitalmars-d-learn wrote:

[...]


The DIPs are here: https://github.com/dlang/DIPs

[...]


DIP1014 has not been implemented in DMD or druntime yet, AFAIK


Re: DIP 1016 and const ref parameters

2019-06-19 Thread Jonathan M Davis via Digitalmars-d-learn
On Wednesday, June 19, 2019 12:28:12 PM MDT XavierAP via Digitalmars-d-learn 
wrote:
> On Wednesday, 19 June 2019 at 12:55:09 UTC, Jonathan M Davis
>
> wrote:
> > Even in C++, using const ref is not as good a practice as it
> > once was, because they added move constructors, finally making
> > object moveable. The result is that in many cases, it's
> > actually more efficient to just copy values in C++ rather than
> > use const &, but which is better does depend on the code.
> >
> > As for D, unless you're dealing with large objects, odds are
> > that worrying about passing by value is pointless. D classes
> > are reference types, and D structs have move semantics
> > built-in. So, you don't get as many copies as you would in
> > C++98, and the situation is probably better than newer versions
> > of C++, since IIRC, C++ classes aren't moveable by default,
> > whereas D structs are. In general, you're probably better off
> > just passing by value unless you find that a particular piece
> > of code is inefficient when benchmarking. Either way, you don't
> > want to be slapping const on everything the way you would in
> > C++, because D's const is far more restrictive. So, while it
> > still can be quite useful, odds are that if you start using it
> > heavily, you're going to run into problems fast - especially
> > since casting away const and mutating an object is undefined
> > behavior in D. D's const has no back doors. If something is
> > const, then you can't mutate it unless you also have a mutable
> > reference to the same data. And because const is transitive,
> > you pretty much can't get mutable stuff from const stuff like
> > you frequently can in C++ (e.g. in C++, it's possible to have a
> > const container of mutable objects, wherein D, once part of
> > something is const, everything within that part is const).
> >
> > As for the DIP, I'd suggest watching Andrei's recent dconf talk
> > on the subject:
> >
> > https://www.youtube.com/watch?v=aRvu2JGGn6E=youtu.be
> >
> > - Jonathan M Davis
>
> I am not talking about cases that would be candidate for moving,
> or where const would be any problem. If you want an example for
> the sake of argument:
>
> struct Matrix3D
> {
> Matrix3D opBinary(string op)(const ref Matrix3D rhs) const;
> }
> unittest
> {
>   auto a = Matrix3D.random;
>   assert(a == a * Matrix3D.identity);
>   assert(a == a + Matrix3D.zeros);
> }
>
> I did watch Andrei's talk, actually this is where I started and
> learned about the DIP(s), then I was confused that 1016 had been
> rejected, and smelling that it may be "reopening" I was not sure
> where I can find the "index" of DIPs under discussion or
> whatever... :)

The DIPs are here: https://github.com/dlang/DIPs

Aside from looking through the newsgroup/forum for discussions on DIPs,
that's pretty much all you're going to find on that. Andrei's talk is the
most up-to-date information that we have about this particular DIP.

> > IIRC, C++ classes aren't moveable by default, whereas D structs
> > are.
>
> What do you mean that structs are movable? I know about RVO (in
> both D and C++, supposedly guaranteed by all compilers in
> practice, but not by language spec -- why not D?), but what about
> passing up the stack as here?

I mean that the object is moved from one place in memory to another rather
than copied. There are cases where the compiler needs to take an object and
put it somewhere else. RVO may be one of those, though if I understand
correctly with RVO, it may just place the return value outside of the
function in the first place to avoid needing to move. But regardless of
whether RVO causes a move, there are cases where the compiler can move an
object rather than copying it. A very simple case of where a move could
theoretically happen (though I'm not sure how much this happens in practice)
would be with code like

void foo()
{
MyStruct m;
bar(m);
}

void bar(MyStruct m)
{
...
}

bar takes its argument by value. So, it can't take a reference to avoid a
copy. However, because when bar is called, it's the last time that m is used
inside of foo, rather than copying m to where bar would use it as a
parameter, the compiler could choose to move the object to where bar expects
its parameter to be rather than copying it - so it could just blit the bits
over. In some cases, it may be able to just place m in the right place that
bar will get it without it being copied, but with more complicated code,
that's not possible. A far more likely case where objects would be moved is
with temporaries. e.g. with this code

func(foo(), bar(42), baz("hello"));

assuming that none of these functions return by ref, func is taking
temporaries from several functions, and the spec actually guarantees that
they will not be copied. So, if the compiler needs to move them to put them
in the right place for func to get them as parameters, it will.

In C++98, it really isn't legal for the compiler to move objects. This is
because it's legal to 

Re: DIP 1016 and const ref parameters

2019-06-19 Thread XavierAP via Digitalmars-d-learn
On Wednesday, 19 June 2019 at 12:55:09 UTC, Jonathan M Davis 
wrote:


Even in C++, using const ref is not as good a practice as it 
once was, because they added move constructors, finally making 
object moveable. The result is that in many cases, it's 
actually more efficient to just copy values in C++ rather than 
use const &, but which is better does depend on the code.


As for D, unless you're dealing with large objects, odds are 
that worrying about passing by value is pointless. D classes 
are reference types, and D structs have move semantics 
built-in. So, you don't get as many copies as you would in 
C++98, and the situation is probably better than newer versions 
of C++, since IIRC, C++ classes aren't moveable by default, 
whereas D structs are. In general, you're probably better off 
just passing by value unless you find that a particular piece 
of code is inefficient when benchmarking. Either way, you don't 
want to be slapping const on everything the way you would in 
C++, because D's const is far more restrictive. So, while it 
still can be quite useful, odds are that if you start using it 
heavily, you're going to run into problems fast - especially 
since casting away const and mutating an object is undefined 
behavior in D. D's const has no back doors. If something is 
const, then you can't mutate it unless you also have a mutable 
reference to the same data. And because const is transitive, 
you pretty much can't get mutable stuff from const stuff like 
you frequently can in C++ (e.g. in C++, it's possible to have a 
const container of mutable objects, wherein D, once part of 
something is const, everything within that part is const).


As for the DIP, I'd suggest watching Andrei's recent dconf talk 
on the subject:


https://www.youtube.com/watch?v=aRvu2JGGn6E=youtu.be

- Jonathan M Davis


I am not talking about cases that would be candidate for moving, 
or where const would be any problem. If you want an example for 
the sake of argument:


struct Matrix3D
{
Matrix3D opBinary(string op)(const ref Matrix3D rhs) const;
}
unittest
{
auto a = Matrix3D.random;
assert(a == a * Matrix3D.identity);
assert(a == a + Matrix3D.zeros);
}

I did watch Andrei's talk, actually this is where I started and 
learned about the DIP(s), then I was confused that 1016 had been 
rejected, and smelling that it may be "reopening" I was not sure 
where I can find the "index" of DIPs under discussion or 
whatever... :)


IIRC, C++ classes aren't moveable by default, whereas D structs 
are.


What do you mean that structs are movable? I know about RVO (in 
both D and C++, supposedly guaranteed by all compilers in 
practice, but not by language spec -- why not D?), but what about 
passing up the stack as here?


Re: DIP 1016 and const ref parameters

2019-06-19 Thread Jonathan M Davis via Digitalmars-d-learn
On Wednesday, June 19, 2019 6:33:44 AM MDT XavierAP via Digitalmars-d-learn 
wrote:
> I often use a pattern of having const ref struct parameters (as
> in C++) but this doesn't work in the case of rvalues. The
> workaround of defining an overload that calls its own name is
> terrible. I understand there was a DIP 1016 by Manu asking for
> this case to work. As far as I can tell, this was rejected, but
> later reconsidered, and now Andrei is starting up a new one[1]?
> Apologies but I'm not sure where these discussions are
> centralized. But if anyone has any idea or guess how seriously
> and in what kind of time this could be expected, that would be my
> first side question.
>
> My main learning question is whether the const ref parameter
> pattern is good in D? In C++ I see it everywhere, but are there
> better alternatives, in particular in D, or is there no point
> because some copy elision optimization may be guaranteed? In
> short am I right in writing const ref parameters, or am I doing
> something silly (and as important as this DIP may otherwise be,
> it wouldn't affect me as much as I think)??
>
> As far as I can see, this DIP would be helpful for two use cases:
> const ref, and return ref with method chains. Are there others?
> __
> [1]
> https://forum.dlang.org/post/d90a7424-a986-66f1-e889-a9abd55e0e65@erdani.o
> rg

Even in C++, using const ref is not as good a practice as it once was,
because they added move constructors, finally making object moveable. The
result is that in many cases, it's actually more efficient to just copy
values in C++ rather than use const &, but which is better does depend on
the code.

As for D, unless you're dealing with large objects, odds are that worrying
about passing by value is pointless. D classes are reference types, and D
structs have move semantics built-in. So, you don't get as many copies as
you would in C++98, and the situation is probably better than newer versions
of C++, since IIRC, C++ classes aren't moveable by default, whereas D
structs are. In general, you're probably better off just passing by value
unless you find that a particular piece of code is inefficient when
benchmarking. Either way, you don't want to be slapping const on everything
the way you would in C++, because D's const is far more restrictive. So,
while it still can be quite useful, odds are that if you start using it
heavily, you're going to run into problems fast - especially since casting
away const and mutating an object is undefined behavior in D. D's const has
no back doors. If something is const, then you can't mutate it unless you
also have a mutable reference to the same data. And because const is
transitive, you pretty much can't get mutable stuff from const stuff like
you frequently can in C++ (e.g. in C++, it's possible to have a const
container of mutable objects, wherein D, once part of something is const,
everything within that part is const).

As for the DIP, I'd suggest watching Andrei's recent dconf talk on the
subject:

https://www.youtube.com/watch?v=aRvu2JGGn6E=youtu.be

- Jonathan M Davis