Jump to page: 1 2 3
Thread overview
Move semantics for D
Jul 13, 2012
Benjamin Thaut
Jul 13, 2012
monarch_dodra
Jul 13, 2012
Mehrdad
Jul 13, 2012
Benjamin Thaut
Jul 13, 2012
Jonathan M Davis
Jul 14, 2012
Mehrdad
Jul 14, 2012
Mehrdad
Jul 15, 2012
Ali Çehreli
Jul 13, 2012
Benjamin Thaut
Jul 13, 2012
Christophe Travert
Jul 13, 2012
Benjamin Thaut
Jul 13, 2012
Jonathan M Davis
Jul 13, 2012
monarch_dodra
Jul 13, 2012
Jonathan M Davis
Jul 13, 2012
monarch_dodra
Jul 13, 2012
Jonathan M Davis
Jul 13, 2012
monarch_dodra
Jul 13, 2012
monarch_dodra
Jul 13, 2012
Jonathan M Davis
Jul 14, 2012
monarch_dodra
Jul 13, 2012
Jonathan M Davis
July 13, 2012
Move semantics in C++0x are quite nice for optimization purposes. Thinking about it, it should be fairly easy to implement move semantics in D as structs don't have identity. Therefor a move constructor would not be required. You can already move value types for example within an array just by plain moving the data of the value around. With a little new keyword 'mov' or 'move' it would also be possible to move value types into and out of functions, something like this:

mov Range findNext(mov Range r)
{
  //do stuff here
}

With something like this it would not be neccessary to copy the range twice during the call of this function, the compiler could just plain copy the data and reinitialize the origin in case of the argument.
In case of the return value to only copying would be neccessary as the data goes out of scope anyway.

The only question would be if this causes any problems with out contracts.

The pre C++0x move trick, reserving a bit in the value representation for marking that the next copy is a move, is unfortunately not possible D because D does not have a copy constructor.

I for example have a range that iterates over a octree and thus needs to internally track which nodes it already visited and which ones are still left. This is done with a stack container. That needs to be copied everytime the range is copied, which causes quite some overhead.

Kind Regards
Benjamin Thaut
July 13, 2012
On Friday, 13 July 2012 at 06:50:02 UTC, Benjamin Thaut wrote:
> Move semantics in C++0x are quite nice for optimization purposes. Thinking about it, it should be fairly easy to implement move semantics in D as structs don't have identity. Therefor a move constructor would not be required. You can already move value types for example within an array just by plain moving the data of the value around. With a little new keyword 'mov' or 'move' it would also be possible to move value types into and out of functions, something like this:
>
> mov Range findNext(mov Range r)
> {
>   //do stuff here
> }
>
> With something like this it would not be neccessary to copy the range twice during the call of this function, the compiler could just plain copy the data and reinitialize the origin in case of the argument.
> In case of the return value to only copying would be neccessary as the data goes out of scope anyway.
>
> The only question would be if this causes any problems with out contracts.
>
> The pre C++0x move trick, reserving a bit in the value representation for marking that the next copy is a move, is unfortunately not possible D because D does not have a copy constructor.
>
> I for example have a range that iterates over a octree and thus needs to internally track which nodes it already visited and which ones are still left. This is done with a stack container. That needs to be copied everytime the range is copied, which causes quite some overhead.
>
> Kind Regards
> Benjamin Thaut

I'm pretty sure D already has it: Values types are "moved" in and out of function, implicitly, when possible, without any special semantics.

For "return by value", the value is simply "blit copied" (memcopy), when returning a local variable. Neither the source constructor is called, nor the target postblit constructor. Just binary bit copy.

For passing in by value, the compiler will do the same trick "if and when" it can detect you are never going to use said value again. If you are, you can always force a move using an explicit std.algorithm.move:
"myRange = findNext(move(myRange));"

You get pretty the same effect as in C++11, except:
a) the compiler will eagerly move stuff for you
b) you don't even have to define fun(T&&) (Whew)

Finally, if you want to remove the responsibility of the move from the caller to the callee, I *guess* you can always just pass by ref and do stuff there:

Range findNext(ref Range r)
{
   auto r2;
   move(r, r2); //move r into r2
   //do stuff here
   return r2; //move return
}

Note that while it might seem useless to move r into r2 (since you already have r by reference), you still have to move into a local temporary so that the compiler can "move" r2 out of findNext. The above function will make 0 calls to this(this) and 0 calls to ~this. There will be about two copies of this.init.

...of course, at this point, one could wonder why:
a) you don't just take a ref, and return void?
b) Use the safer just as efficient "myRange = findNext(move(myRange));"
July 13, 2012
On Friday, 13 July 2012 at 08:06:08 UTC, monarch_dodra wrote:
> Move semantics in C++0x are quite nice for optimization purposes.

That's not why they were introduced.


They were introduced for making things like unique_ptr possible.
July 13, 2012
Am 13.07.2012 10:06, schrieb monarch_dodra:
> On Friday, 13 July 2012 at 06:50:02 UTC, Benjamin Thaut wrote:
>> Move semantics in C++0x are quite nice for optimization purposes.
>> Thinking about it, it should be fairly easy to implement move
>> semantics in D as structs don't have identity. Therefor a move
>> constructor would not be required. You can already move value types
>> for example within an array just by plain moving the data of the value
>> around. With a little new keyword 'mov' or 'move' it would also be
>> possible to move value types into and out of functions, something like
>> this:
>>
>> mov Range findNext(mov Range r)
>> {
>>   //do stuff here
>> }
>>
>> With something like this it would not be neccessary to copy the range
>> twice during the call of this function, the compiler could just plain
>> copy the data and reinitialize the origin in case of the argument.
>> In case of the return value to only copying would be neccessary as the
>> data goes out of scope anyway.
>>
>> The only question would be if this causes any problems with out
>> contracts.
>>
>> The pre C++0x move trick, reserving a bit in the value representation
>> for marking that the next copy is a move, is unfortunately not
>> possible D because D does not have a copy constructor.
>>
>> I for example have a range that iterates over a octree and thus needs
>> to internally track which nodes it already visited and which ones are
>> still left. This is done with a stack container. That needs to be
>> copied everytime the range is copied, which causes quite some overhead.
>>
>> Kind Regards
>> Benjamin Thaut
>
> I'm pretty sure D already has it: Values types are "moved" in and out of
> function, implicitly, when possible, without any special semantics.
>
> For "return by value", the value is simply "blit copied" (memcopy), when
> returning a local variable. Neither the source constructor is called,
> nor the target postblit constructor. Just binary bit copy.
>
> For passing in by value, the compiler will do the same trick "if and
> when" it can detect you are never going to use said value again. If you
> are, you can always force a move using an explicit std.algorithm.move:
> "myRange = findNext(move(myRange));"
>
> You get pretty the same effect as in C++11, except:
> a) the compiler will eagerly move stuff for you
> b) you don't even have to define fun(T&&) (Whew)
>
> Finally, if you want to remove the responsibility of the move from the
> caller to the callee, I *guess* you can always just pass by ref and do
> stuff there:
>
> Range findNext(ref Range r)
> {
>     auto r2;
>     move(r, r2); //move r into r2
>     //do stuff here
>     return r2; //move return
> }
>
> Note that while it might seem useless to move r into r2 (since you
> already have r by reference), you still have to move into a local
> temporary so that the compiler can "move" r2 out of findNext. The above
> function will make 0 calls to this(this) and 0 calls to ~this. There
> will be about two copies of this.init.
>
> ...of course, at this point, one could wonder why:
> a) you don't just take a ref, and return void?
> b) Use the safer just as efficient "myRange = findNext(move(myRange));"

I didn't know know about the compiler already moving structs as return values. But not having to do the move for a argument manuall would be nice, espeically as ref does not work with non-lvalues

Kind Regards
Benjamin Thaut

July 13, 2012
Am 13.07.2012 10:19, schrieb Mehrdad:
> On Friday, 13 July 2012 at 08:06:08 UTC, monarch_dodra wrote:
>> Move semantics in C++0x are quite nice for optimization purposes.
>
> That's not why they were introduced.
>
>
> They were introduced for making things like unique_ptr possible.


Well thats your point of view. I'm coming from the gaming industry, and everyone I have talked to so far, is happy because they can use it to optimize better.

Kind Regards
Benjamin Thaut
July 13, 2012
Benjamin Thaut , dans le message (digitalmars.D:172207), a écrit :
> Move semantics in C++0x are quite nice for optimization purposes. Thinking about it, it should be fairly easy to implement move semantics in D as structs don't have identity. Therefor a move constructor would not be required. You can already move value types for example within an array just by plain moving the data of the value around. With a little new keyword 'mov' or 'move' it would also be possible to move value types into and out of functions, something like this:
> 
> mov Range findNext(mov Range r)
> {
>    //do stuff here
> }
> 
> With something like this it would not be neccessary to copy the range
> twice during the call of this function, the compiler could just plain
> copy the data and reinitialize the origin in case of the argument.
> In case of the return value to only copying would be neccessary as the
> data goes out of scope anyway.

If Range is a Rvalue, it will be moved, not copied.
It it's a Lvalue, your operation is dangerous, and does not bring you
much more than using ref (it may be faster to copy the range than to
take the reference, but that's an optimiser issue).

auto ref seems to be the solution.

> I for example have a range that iterates over a octree and thus needs to internally track which nodes it already visited and which ones are still left. This is done with a stack container. That needs to be copied everytime the range is copied, which causes quite some overhead.

I would share the tracking data between several instance of the range, making bitwise copy suitable. Tracking data would be duplicated only on call to save or opSlice(). You'd hit the issue of foreach not calling save when it should, but opSlice would solve this, and you could still overload opApply if you want to be sure.

-- 
Christophe
July 13, 2012
Am 13.07.2012 10:59, schrieb Christophe Travert:
> Benjamin Thaut , dans le message (digitalmars.D:172207), a écrit :
>> Move semantics in C++0x are quite nice for optimization purposes.
>> Thinking about it, it should be fairly easy to implement move semantics
>> in D as structs don't have identity. Therefor a move constructor would
>> not be required. You can already move value types for example within an
>> array just by plain moving the data of the value around. With a little
>> new keyword 'mov' or 'move' it would also be possible to move value
>> types into and out of functions, something like this:
>>
>> mov Range findNext(mov Range r)
>> {
>>     //do stuff here
>> }
>>
>> With something like this it would not be neccessary to copy the range
>> twice during the call of this function, the compiler could just plain
>> copy the data and reinitialize the origin in case of the argument.
>> In case of the return value to only copying would be neccessary as the
>> data goes out of scope anyway.
>
> If Range is a Rvalue, it will be moved, not copied.
> It it's a Lvalue, your operation is dangerous, and does not bring you
> much more than using ref (it may be faster to copy the range than to
> take the reference, but that's an optimiser issue).
>
> auto ref seems to be the solution.
>
>> I for example have a range that iterates over a octree and thus needs to
>> internally track which nodes it already visited and which ones are still
>> left. This is done with a stack container. That needs to be copied
>> everytime the range is copied, which causes quite some overhead.
>
> I would share the tracking data between several instance of the range,
> making bitwise copy suitable. Tracking data would be duplicated only on
> call to save or opSlice(). You'd hit the issue of foreach not calling
> save when it should, but opSlice would solve this, and you could still
> overload opApply if you want to be sure.
>

I couldn't find anything in the documentation about foreach calling save or opSlice(). So I assume foreach calls opSlice if aviable?

foreach(el; range) { ... }
translates to:

for(auto r = range[]; !r.empty(); r.popFront()
{
  auto el = r.front();
  ...
}

Kind Regards
Benjamin Thaut
July 13, 2012
On Friday, July 13, 2012 10:45:03 Benjamin Thaut wrote:
> Am 13.07.2012 10:19, schrieb Mehrdad:
> > On Friday, 13 July 2012 at 08:06:08 UTC, monarch_dodra wrote:
> >> Move semantics in C++0x are quite nice for optimization purposes.
> > 
> > That's not why they were introduced.
> > 
> > They were introduced for making things like unique_ptr possible.
> 
> Well thats your point of view. I'm coming from the gaming industry, and everyone I have talked to so far, is happy because they can use it to optimize better.

I expect that the reality of the matter is that it was introduced for both.

- Jonathan M Davis
July 13, 2012
On Friday, July 13, 2012 11:09:04 Benjamin Thaut wrote:
> Am 13.07.2012 10:59, schrieb Christophe Travert:
> > Benjamin Thaut , dans le message (digitalmars.D:172207), a écrit :
> >> Move semantics in C++0x are quite nice for optimization purposes. Thinking about it, it should be fairly easy to implement move semantics in D as structs don't have identity. Therefor a move constructor would not be required. You can already move value types for example within an array just by plain moving the data of the value around. With a little new keyword 'mov' or 'move' it would also be possible to move value types into and out of functions, something like this:
> >> 
> >> mov Range findNext(mov Range r)
> >> {
> >> 
> >>     //do stuff here
> >> 
> >> }
> >> 
> >> With something like this it would not be neccessary to copy the range
> >> twice during the call of this function, the compiler could just plain
> >> copy the data and reinitialize the origin in case of the argument.
> >> In case of the return value to only copying would be neccessary as the
> >> data goes out of scope anyway.
> > 
> > If Range is a Rvalue, it will be moved, not copied.
> > It it's a Lvalue, your operation is dangerous, and does not bring you
> > much more than using ref (it may be faster to copy the range than to
> > take the reference, but that's an optimiser issue).
> > 
> > auto ref seems to be the solution.
> > 
> >> I for example have a range that iterates over a octree and thus needs to internally track which nodes it already visited and which ones are still left. This is done with a stack container. That needs to be copied everytime the range is copied, which causes quite some overhead.
> > 
> > I would share the tracking data between several instance of the range, making bitwise copy suitable. Tracking data would be duplicated only on call to save or opSlice(). You'd hit the issue of foreach not calling save when it should, but opSlice would solve this, and you could still overload opApply if you want to be sure.
> 
> I couldn't find anything in the documentation about foreach calling save or opSlice(). So I assume foreach calls opSlice if aviable?

If you have

foreach(e; obj)
{
    ...
}

and obj is a range, then it becomes

for(auto __range = obj; !__range.empty; __range.popFront())
{
    auto e = __range.front;
    ...
}

If obj is not a range, but it does declare opSlice, then it's sliced, so you get something like

for(auto __range = obj[]; !__range.empty; __range.popFront())
{
    auto e = __range.front;
    ...
}

There's also opApply to consider though, and it probably gets precedence over opSlice if a type defines both. I don't know exactly how all of that's laid out right now though. However, there's a recent thread discussing it ("opApply not called for foeach(container)"), so you can read that, and I think that they give better details on precedence. I haven't read it in great detail though, so I don't know the details other than the fact that it sounds like the precedence rules for foreach probably neet do be ironed out better.

- Jonathan M Davis
July 13, 2012
On Friday, July 13, 2012 08:50:03 Benjamin Thaut wrote:
> Move semantics in C++0x are quite nice for optimization purposes. Thinking about it, it should be fairly easy to implement move semantics in D as structs don't have identity. Therefor a move constructor would not be required. You can already move value types for example within an array just by plain moving the data of the value around.

http://stackoverflow.com/questions/4200190/does-d-have-something-akin-to-c0xs- move-semantics

http://stackoverflow.com/questions/6884996/questions-about-postblit-and-move- semantics

- Jonathan M Davis
« First   ‹ Prev
1 2 3