www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - "ref const" parameters in functions

reply "L-MAN" <vangelisforever yandex.ru> writes:
Hello everybody!

I'm trying to use some function FN like this:

struct X
{
  protected double _x;  // double type for example
  public  property double X() const { return _x; }

  // ctor
  public this(double x) { _x = x; } // double type for example

  void FN(ref const(double) in_x) // double type for example
  {
    // ... do some operations with _x
  }

}

main(..)
{
  ....
  X x = X(20);

  x.FN(30); // getting an error
}


  why x.FN(20) gets me an error?
  the construction "ref const(double)" or "ref immutable(double)" 
must be an rvalue by default I think, but in FN parameter the 
compiler expects an lvalue...
  this strategy is the way to some unnecessary copy operations, 
when value 20 (or some big struct instead of it) will copy to the 
stack..

How can I resolve this problem?

Thanks!
Mar 31 2012
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, March 31, 2012 23:25:51 L-MAN wrote:
 Hello everybody!
 
 I'm trying to use some function FN like this:
 
 struct X
 {
   protected double _x;  // double type for example
   public  property double X() const { return _x; }
 
   // ctor
   public this(double x) { _x = x; } // double type for example
 
   void FN(ref const(double) in_x) // double type for example
   {
     // ... do some operations with _x
   }
 
 }
 
 main(..)
 {
   ....
   X x = X(20);
 
   x.FN(30); // getting an error
 }
 
 
   why x.FN(20) gets me an error?
   the construction "ref const(double)" or "ref immutable(double)"
 must be an rvalue by default I think, but in FN parameter the
 compiler expects an lvalue...
   this strategy is the way to some unnecessary copy operations,
 when value 20 (or some big struct instead of it) will copy to the
 stack..
 
 How can I resolve this problem?

Unlike C++, const ref _must_ be an lvalue just like ref. If you use auto ref instead of const ref, the compiler is supposed to choose between ref and non- ref based on which it thinks would be more efficient, but it currently only works with templated types. You can duplicate the function and have a version whose parameter is const ref and one which is not, but be careful if you try and make the non-const ref version call the const ref version (to avoid duplicating the function's body) and make sure that you don't get infinite recursion. It usually works, but I've run into cases before where I ended up with infinite recursion, so make sure that you have unit tests which check. Regardless, if you're dealing with a primitive type like double, don't bother with const ref. It's not going to be more efficient. It's with large structs that you can see a difference. - Jonathan M Davis
Mar 31 2012
prev sibling next sibling parent "L-MAN" <vangelisforever yandex.ru> writes:
On Saturday, 31 March 2012 at 21:42:05 UTC, Jonathan M Davis 
wrote:
 On Saturday, March 31, 2012 23:25:51 L-MAN wrote:
 Hello everybody!
 
 I'm trying to use some function FN like this:
 
 struct X
 {
   protected double _x;  // double type for example
   public  property double X() const { return _x; }
 
   // ctor
   public this(double x) { _x = x; } // double type for example
 
   void FN(ref const(double) in_x) // double type for example
   {
     // ... do some operations with _x
   }
 
 }
 
 main(..)
 {
   ....
   X x = X(20);
 
   x.FN(30); // getting an error
 }
 
 
   why x.FN(20) gets me an error?
   the construction "ref const(double)" or "ref 
 immutable(double)"
 must be an rvalue by default I think, but in FN parameter the
 compiler expects an lvalue...
   this strategy is the way to some unnecessary copy operations,
 when value 20 (or some big struct instead of it) will copy to 
 the
 stack..
 
 How can I resolve this problem?

Unlike C++, const ref _must_ be an lvalue just like ref. If you use auto ref instead of const ref, the compiler is supposed to choose between ref and non- ref based on which it thinks would be more efficient, but it currently only works with templated types. You can duplicate the function and have a version whose parameter is const ref and one which is not, but be careful if you try and make the non-const ref version call the const ref version (to avoid duplicating the function's body) and make sure that you don't get infinite recursion. It usually works, but I've run into cases before where I ended up with infinite recursion, so make sure that you have unit tests which check. Regardless, if you're dealing with a primitive type like double, don't bother with const ref. It's not going to be more efficient. It's with large structs that you can see a difference. - Jonathan M Davis

Thank you for reply, but how can I exclude some copy operations? Look at this sample: struct ABC // simple class { private double _d=0; property { double D() const { return _d; } double D(double val) { return _d=val; } } this(double d) { _d = d; writeln(" *** ctor ABC, d = ", _d); } ABC opAdd(ABC abc) { //ABC temp = ABC(this._d+abc._d); //return temp; return ABC(this._d+abc._d); } ABC opMul(ABC d) { //ABC temp = ABC(this._d*d); //return temp; return ABC(this._d*d); } ref ABC opAssign(ABC abc) { this._d = abc.D; return this; } ~this() { writeln(" *** dtor ABC, d = ", _d); } } struct F { private ABC _abc; property double D() const { return _abc.D; } public this(ABC abc) { _abc = abc; //abc.D=90; //pnt.X = 30; } } void main(string[] args) { ABC abc1 = ABC(10); ABC abc2 = ABC(20); F f = F(abc1+abc2*20.0); writeln(f.D); ....... } Operation abc1+abc2*20.0 consists of copy of ABC operations:
Mar 31 2012
prev sibling next sibling parent "L-MAN" <vangelisforever yandex.ru> writes:
On Saturday, 31 March 2012 at 21:42:05 UTC, Jonathan M Davis 
wrote:
 On Saturday, March 31, 2012 23:25:51 L-MAN wrote:
 Hello everybody!
 
 I'm trying to use some function FN like this:
 
 struct X
 {
   protected double _x;  // double type for example
   public  property double X() const { return _x; }
 
   // ctor
   public this(double x) { _x = x; } // double type for example
 
   void FN(ref const(double) in_x) // double type for example
   {
     // ... do some operations with _x
   }
 
 }
 
 main(..)
 {
   ....
   X x = X(20);
 
   x.FN(30); // getting an error
 }
 
 
   why x.FN(20) gets me an error?
   the construction "ref const(double)" or "ref 
 immutable(double)"
 must be an rvalue by default I think, but in FN parameter the
 compiler expects an lvalue...
   this strategy is the way to some unnecessary copy operations,
 when value 20 (or some big struct instead of it) will copy to 
 the
 stack..
 
 How can I resolve this problem?

Unlike C++, const ref _must_ be an lvalue just like ref. If you use auto ref instead of const ref, the compiler is supposed to choose between ref and non- ref based on which it thinks would be more efficient, but it currently only works with templated types. You can duplicate the function and have a version whose parameter is const ref and one which is not, but be careful if you try and make the non-const ref version call the const ref version (to avoid duplicating the function's body) and make sure that you don't get infinite recursion. It usually works, but I've run into cases before where I ended up with infinite recursion, so make sure that you have unit tests which check. Regardless, if you're dealing with a primitive type like double, don't bother with const ref. It's not going to be more efficient. It's with large structs that you can see a difference. - Jonathan M Davis

Thank you for reply, but how can I exclude some copy operations? Look at this sample: struct ABC // simple class { private double _d=0; property { double D() const { return _d; } double D(double val) { return _d=val; } } this(double d) { _d = d; writeln(" *** ctor ABC, d = ", _d); } ABC opAdd(ABC abc) { //ABC temp = ABC(this._d+abc._d); //return temp; return ABC(this._d+abc._d); } ABC opMul(double d) { //ABC temp = ABC(this._d*d); //return temp; return ABC(this._d*d); } ref ABC opAssign(ABC abc) { this._d = abc.D; return this; } ~this() { writeln(" *** dtor ABC, d = ", _d); } } struct F { private ABC _abc; property double D() const { return _abc.D; } public this(ABC abc) { _abc = abc; //abc.D=90; //pnt.X = 30; } } void main(string[] args) { ABC abc1 = ABC(10); ABC abc2 = ABC(20); F f = F(abc1+abc2*20.0); writeln(f.D); ....... } Operation abc1+abc2*20.0 consists of 2 ABC copy: 1. Copy result of abc2*20.0 temp value to opAdd function 2. Copy result of abc1+abc2*20.0 temp2 value to F() ctor two temp variables and two copy operations. But if I can use "ref const" rvalue operations 0 copies will be need: abc2*20.0 creates temp valiable $$$temp opAdd(ref const ABC abc) use a ref rvalue to $$$temp, is not a copy opAdd return a $$$temp2 that use as ref rvalue for F() ctor And try to imagine how many unnecessary copies of ABC will create a simple operation like this: ((abc1+abc2*20.0)/0.25) + (abc1/abc2)*(abc2-1).... it killing a time of application, especially if time is critical. The constructions "auto ref" in return functions values are not working well, if I use it in a "auto ref opAdd(ref const ABC)" "auto ref opMul(double d)" " F.this(ref const ABC)" to exclude some copy operations it gives me a strange result :-(( Thank you!!!
Apr 01 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, April 01, 2012 09:10:58 L-MAN wrote:
 On Saturday, 31 March 2012 at 21:42:05 UTC, Jonathan M Davis
 
 wrote:
 On Saturday, March 31, 2012 23:25:51 L-MAN wrote:
 Hello everybody!
 
 I'm trying to use some function FN like this:
 
 struct X
 {
 
   protected double _x;  // double type for example
   public  property double X() const { return _x; }
   
   // ctor
   public this(double x) { _x = x; } // double type for example
   
   void FN(ref const(double) in_x) // double type for example
   {
   
     // ... do some operations with _x
   
   }
 
 }
 
 main(..)
 {
 
   ....
   X x = X(20);
   
   x.FN(30); // getting an error
 
 }
 
   why x.FN(20) gets me an error?
   the construction "ref const(double)" or "ref
 
 immutable(double)"
 must be an rvalue by default I think, but in FN parameter the
 compiler expects an lvalue...
 
   this strategy is the way to some unnecessary copy operations,
 
 when value 20 (or some big struct instead of it) will copy to
 the
 stack..
 
 How can I resolve this problem?

Unlike C++, const ref _must_ be an lvalue just like ref. If you use auto ref instead of const ref, the compiler is supposed to choose between ref and non- ref based on which it thinks would be more efficient, but it currently only works with templated types. You can duplicate the function and have a version whose parameter is const ref and one which is not, but be careful if you try and make the non-const ref version call the const ref version (to avoid duplicating the function's body) and make sure that you don't get infinite recursion. It usually works, but I've run into cases before where I ended up with infinite recursion, so make sure that you have unit tests which check. Regardless, if you're dealing with a primitive type like double, don't bother with const ref. It's not going to be more efficient. It's with large structs that you can see a difference. - Jonathan M Davis

Thank you for reply, but how can I exclude some copy operations? Look at this sample: struct ABC // simple class { private double _d=0; property { double D() const { return _d; } double D(double val) { return _d=val; } } this(double d) { _d = d; writeln(" *** ctor ABC, d = ", _d); } ABC opAdd(ABC abc) { //ABC temp = ABC(this._d+abc._d); //return temp; return ABC(this._d+abc._d); } ABC opMul(double d) { //ABC temp = ABC(this._d*d); //return temp; return ABC(this._d*d); } ref ABC opAssign(ABC abc) { this._d = abc.D; return this; } ~this() { writeln(" *** dtor ABC, d = ", _d); } } struct F { private ABC _abc; property double D() const { return _abc.D; } public this(ABC abc) { _abc = abc; //abc.D=90; //pnt.X = 30; } } void main(string[] args) { ABC abc1 = ABC(10); ABC abc2 = ABC(20); F f = F(abc1+abc2*20.0); writeln(f.D); ....... } Operation abc1+abc2*20.0 consists of 2 ABC copy: 1. Copy result of abc2*20.0 temp value to opAdd function 2. Copy result of abc1+abc2*20.0 temp2 value to F() ctor two temp variables and two copy operations. But if I can use "ref const" rvalue operations 0 copies will be need: abc2*20.0 creates temp valiable $$$temp opAdd(ref const ABC abc) use a ref rvalue to $$$temp, is not a copy opAdd return a $$$temp2 that use as ref rvalue for F() ctor And try to imagine how many unnecessary copies of ABC will create a simple operation like this: ((abc1+abc2*20.0)/0.25) + (abc1/abc2)*(abc2-1).... it killing a time of application, especially if time is critical. The constructions "auto ref" in return functions values are not working well, if I use it in a "auto ref opAdd(ref const ABC)" "auto ref opMul(double d)" " F.this(ref const ABC)" to exclude some copy operations it gives me a strange result :-((

You misunderstood what I meant by auto ref. I meant that you use auto ref instead of const ref. e.g. void func(auto ref T param) {} At present, func would have to be templated to use auto ref void func(T)(auto ref T param) {} but eventually, auto ref shouldn't need the function to be templated. Returning auto ref is completely different. It says that the return type is inferred (just like with returning auto by itself) except that it's a reference to the type which is inferred. And that requires that the value being returned by an lvalue which is _not_ a local variable (because in all cases except for auto ref on a parameter, ref must refer to an lvalue, and if you return a ref to a local variable, you're going to get undefined behavior, because you're returned a reference to a variable which no longer exists). You can return ref from a function if you're returning the object's this pointer, but that's generally all. If you're really paranoid about copying, then you're going to need to either us auto ref on your parameters (which currently means that the functions must be templated), or you need to use const ref (which then requires that you use lvalues with those functions). Something like ((abc1+abc2*20.0)/0.25) + (abc1/abc2)*(abc2-1).... makes _no_ sense if you're trying to avoid copying. By definition, those operations must create temporaries, because they're returning new values. That's exactly what happens with the built-in types. +, *, /, etc. all create temporaries. You need to use +=, *=, /=, etc if you don't want to create temporaries. The same would be true in C++. But honestly, in your example, there is _no_ reason to worry about it. You have a struct that holds one double, and that's it. So, it's going to be about as efficient as if all of those operations were operating on a double rather than a struct. If your struct had ten different variables, then maybe you'd need to be worried about copying, but not with just one. Regardless, if you want to avoid creating temporaries, you can't use operators which return new values. You need to use the opOpAssign operators, which means that your code is going to look very different, because you won't be able to create expressions like the one above. By using auto ref and/or const ref, you can create functions which don't make copies when passed a variable, but as long as you're using functions/operators which return new values, you're still going to be creating temporaries. - Jonathan M Davis
Apr 01 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 04/02/2012 06:23 AM, Jonathan M Davis wrote:
 No. It's not. It's a temporary, and temporaries are almost always rvalues. The
 sole (and very bizarre) exception is struct literals (e.g. ABC(20) is
 currently considered an lvalue).

DMD 2.059head treats struct literals as rvalues.
Apr 02 2012
prev sibling next sibling parent "L-MAN" <vangelisforever yandex.ru> writes:
On Sunday, 1 April 2012 at 08:22:08 UTC, Jonathan M Davis wrote:
 On Sunday, April 01, 2012 09:10:58 L-MAN wrote:
 On Saturday, 31 March 2012 at 21:42:05 UTC, Jonathan M Davis
 
 wrote:
 On Saturday, March 31, 2012 23:25:51 L-MAN wrote:
 Hello everybody!
 
 I'm trying to use some function FN like this:
 
 struct X
 {
 
   protected double _x;  // double type for example
   public  property double X() const { return _x; }
 
   // ctor
   public this(double x) { _x = x; } // double type for 
 example
 
   void FN(ref const(double) in_x) // double type for example
   {
 
     // ... do some operations with _x
 
   }
 
 }
 
 main(..)
 {
 
   ....
   X x = X(20);
 
   x.FN(30); // getting an error
 
 }
 
   why x.FN(20) gets me an error?
   the construction "ref const(double)" or "ref
 
 immutable(double)"
 must be an rvalue by default I think, but in FN parameter 
 the
 compiler expects an lvalue...
 
   this strategy is the way to some unnecessary copy 
 operations,
 
 when value 20 (or some big struct instead of it) will copy 
 to
 the
 stack..
 
 How can I resolve this problem?

Unlike C++, const ref _must_ be an lvalue just like ref. If you use auto ref instead of const ref, the compiler is supposed to choose between ref and non- ref based on which it thinks would be more efficient, but it currently only works with templated types. You can duplicate the function and have a version whose parameter is const ref and one which is not, but be careful if you try and make the non-const ref version call the const ref version (to avoid duplicating the function's body) and make sure that you don't get infinite recursion. It usually works, but I've run into cases before where I ended up with infinite recursion, so make sure that you have unit tests which check. Regardless, if you're dealing with a primitive type like double, don't bother with const ref. It's not going to be more efficient. It's with large structs that you can see a difference. - Jonathan M Davis

Thank you for reply, but how can I exclude some copy operations? Look at this sample: struct ABC // simple class { private double _d=0; property { double D() const { return _d; } double D(double val) { return _d=val; } } this(double d) { _d = d; writeln(" *** ctor ABC, d = ", _d); } ABC opAdd(ABC abc) { //ABC temp = ABC(this._d+abc._d); //return temp; return ABC(this._d+abc._d); } ABC opMul(double d) { //ABC temp = ABC(this._d*d); //return temp; return ABC(this._d*d); } ref ABC opAssign(ABC abc) { this._d = abc.D; return this; } ~this() { writeln(" *** dtor ABC, d = ", _d); } } struct F { private ABC _abc; property double D() const { return _abc.D; } public this(ABC abc) { _abc = abc; //abc.D=90; //pnt.X = 30; } } void main(string[] args) { ABC abc1 = ABC(10); ABC abc2 = ABC(20); F f = F(abc1+abc2*20.0); writeln(f.D); ....... } Operation abc1+abc2*20.0 consists of 2 ABC copy: 1. Copy result of abc2*20.0 temp value to opAdd function 2. Copy result of abc1+abc2*20.0 temp2 value to F() ctor two temp variables and two copy operations. But if I can use "ref const" rvalue operations 0 copies will be need: abc2*20.0 creates temp valiable $$$temp opAdd(ref const ABC abc) use a ref rvalue to $$$temp, is not a copy opAdd return a $$$temp2 that use as ref rvalue for F() ctor And try to imagine how many unnecessary copies of ABC will create a simple operation like this: ((abc1+abc2*20.0)/0.25) + (abc1/abc2)*(abc2-1).... it killing a time of application, especially if time is critical. The constructions "auto ref" in return functions values are not working well, if I use it in a "auto ref opAdd(ref const ABC)" "auto ref opMul(double d)" " F.this(ref const ABC)" to exclude some copy operations it gives me a strange result :-((

You misunderstood what I meant by auto ref. I meant that you use auto ref instead of const ref. e.g. void func(auto ref T param) {} At present, func would have to be templated to use auto ref void func(T)(auto ref T param) {} but eventually, auto ref shouldn't need the function to be templated. Returning auto ref is completely different. It says that the return type is inferred (just like with returning auto by itself) except that it's a reference to the type which is inferred. And that requires that the value being returned by an lvalue which is _not_ a local variable (because in all cases except for auto ref on a parameter, ref must refer to an lvalue, and if you return a ref to a local variable, you're going to get undefined behavior, because you're returned a reference to a variable which no longer exists). You can return ref from a function if you're returning the object's this pointer, but that's generally all. If you're really paranoid about copying, then you're going to need to either us auto ref on your parameters (which currently means that the functions must be templated), or you need to use const ref (which then requires that you use lvalues with those functions). Something like ((abc1+abc2*20.0)/0.25) + (abc1/abc2)*(abc2-1).... makes _no_ sense if you're trying to avoid copying. By definition, those operations must create temporaries, because they're returning new values. That's exactly what happens with the built-in types. +, *, /, etc. all create temporaries. You need to use +=, *=, /=, etc if you don't want to create temporaries. The same would be true in C++. But honestly, in your example, there is _no_ reason to worry about it. You have a struct that holds one double, and that's it. So, it's going to be about as efficient as if all of those operations were operating on a double rather than a struct. If your struct had ten different variables, then maybe you'd need to be worried about copying, but not with just one. Regardless, if you want to avoid creating temporaries, you can't use operators which return new values. You need to use the opOpAssign operators, which means that your code is going to look very different, because you won't be able to create expressions like the one above. By using auto ref and/or const ref, you can create functions which don't make copies when passed a variable, but as long as you're using functions/operators which return new values, you're still going to be creating temporaries. - Jonathan M Davis

About temporaries in operators +-/*.. you're right, it is not a secret. references in other functions (not in operators) too. I can't understand one thing: ABC abc; // ABC is a struct that consists of some 4x4 matrix for example ABC abc2 = ABC(20); ABC abc3 = ABC(..); ......... abc+=abc2+abc3; operation "+" return the copy of it result to the internal temporary stack variable $temp (it creates automatically and it disable to use and invisible to see), but $temp is a variable and it must be an lvalue always. Expression "ref const.." in function parameter is an lvalue in D. Why $temp variable can't be passes as "ref const..." to the += operator or any other orepators)? I think, that the $temp in D is a rvalue, but expression "ref const.." is an lvalue.. some paradox, and out of understanding.. if "ref const.." in operators parameters would be an rvalue, the execution of the next cycle be a 2x faster: // "ref const.." is an lvalue. *********************** ABC sum; ..... for (int i = 0; i<100000000; i++) { sum = (abc1+abc2*20.0)+(abc1*abc2+abc1*20.0); //abc2.opMul(20.0) creates $temp1; //abc1.opAdd(copy of $temp1) creates $temp2 //abc1.opMul(copy of abc2) creates $temp3 //abc1.opMul(20.0) creates $temp4 //$temp3.opAdd(copy of $temp4) creates $temp5 //$temp2.opAdd(copy of $temp5) creates $temp6 //sum.opAssign(copy of $temp6) } RESULT: execution time 17.8 seconds 5 unnecessary copy operations: 1. copy of $temp1 2. copy of $abc2 3. copy of $temp4 4. copy of $temp5 5. copy of $temp6 // "ref const.." is an rvalue: ************************** ABC sum; ..... for (int i = 0; i<100000000; i++) { sum = (abc1+abc2*20.0)+(abc1*abc2+abc1*20.0); //abc2.opMul(20.0) creates $temp1; //abc1.opAdd(ref to $temp1) creates $temp2 //abc1.opMul(ref to abc2) creates $temp3 //abc1.opMul(20.0) creates $temp4 //$temp3.opAdd(ref to $temp4) creates $temp5 //$temp2.opAdd(ref to $temp5) creates $temp6 //sum.opAssign(ref to $temp6) } RESULT: execution time 9.2 seconds no unnecessary copy operations. when I draw to the screen 50000 graphical objects by OpenGL, it uses a matrices operation via D-language structures (uses all math operators and makes some matrices operations for every graphical object on the scene for each time), the unnecessary copyes of it(matrices) is very bad for engine performance. Thank you!
Apr 01 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, April 02, 2012 05:46:24 L-MAN wrote:
 About temporaries in operators +-/*.. you're right, it is not a
 secret.
 references in other functions (not in operators) too.
 
 
 I can't understand one thing:
   ABC abc; // ABC is a struct that consists of some 4x4 matrix for
 example
   ABC abc2 = ABC(20);
   ABC abc3 = ABC(..);
   .........
   abc+=abc2+abc3;
 
   operation "+" return the copy of it result to the internal
 temporary stack variable $temp (it creates automatically and it
 disable to use and invisible to see), but

 $temp is a variable and
 it must be an lvalue always.

No. It's not. It's a temporary, and temporaries are almost always rvalues. The sole (and very bizarre) exception is struct literals (e.g. ABC(20) is currently considered an lvalue). It results in the bizarre situation that a function which takes a const ref will work with ABC(20) but won't work with a function that returns ABC(20). It's a point of debate and may be changed, in which case ABC(20) would not be an lvalue anymore. But regardless, the result of operations such as + are _always_ rvalues. Normally, the only items that are lvalues are variables and return values which are ref. The result of a function or operator such as + is most definitely _not_ an lvalue, since you can't return their results by ref. It's a classic example on an rvalue. If you want to avoid making copies, then you're going to need to make your parameters auto ref (and hope that the compiler decides that making a copy is more expensive - I don't know how it decides that) or make them const ref and use lvalues - and using lvalues generally means creating explicit variables and _not_ using temporaries. So, your long expression with + and * would have to be changed to use += and *=.
 when I draw to the screen 50000 graphical objects by OpenGL, it
 uses a matrices operation via D-language structures (uses all
 math operators and makes some matrices operations for every
 graphical object on the scene for each time), the unnecessary
 copyes of it(matrices) is very bad for engine performance.

Sure, if you have large structs, making a lot of copies of them can be expensive. But to avoid that, you're going to have to avoid coding in a way which creates temporaries, and expressions like (abc1+abc2*20.0)+(abc1*abc2+abc1*20.0) will _always_ create temporaries. The classic, arithmetic operators are classic examples of functions which create temporaries (all of which are rvalues). So, you can't code that way and expect to avoid temporaries. - Jonathan M Davis
Apr 01 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, April 01, 2012 21:23:50 Jonathan M Davis wrote:
 On Monday, April 02, 2012 05:46:24 L-MAN wrote:
 Sure, if you have large structs, making a lot of copies of them can be
 expensive. But to avoid that, you're going to have to avoid coding in a way
 which creates temporaries, and expressions like
 
 (abc1+abc2*20.0)+(abc1*abc2+abc1*20.0)
 
 will _always_ create temporaries. The classic, arithmetic operators are
 classic examples of functions which create temporaries (all of which are
 rvalues). So, you can't code that way and expect to avoid temporaries.

I should point out that with optimizations turned on, in some cases the compiler can optimize out the copying of temporaries, though not generally the temporaries themselves. For instance auto a = abc1 + abc2 * 20.0 results in a temporary for the result of * and a temporary for the result of +. But the temporary for the result of + will be optimized out (probably even without turning on optimizations), because you're using that result to initialize a variable. And while the result of * will result in a temporary, the copy that gets made when it's passed to opBinary!"+" should be optimized out when compiled with optimizations turned on. So, the optimizer should be able to reduce the problem a fair bit if you compile with -O. -inline may be able to reduce it even further. So, with optimizations turned on, you may not get anywhere near as many copies as you expected. And using auto ref for the parameters (which is easy with the overloaded arithmetic operators, since they're already templated) should also help reduce the number of copies made, but it _is_ up to the compiler whether it makes a copy or not with auto ref, so that depends on the compiler. So, you should be able to reduce the number of copies simply by compiling with -O and -inline, but the temporaries themselves will exist regardless. - Jonathan M Davis
Apr 01 2012
prev sibling next sibling parent "L-MAN" <vangelisforever yandex.ru> writes:
On Monday, 2 April 2012 at 05:03:48 UTC, Jonathan M Davis wrote:
 On Sunday, April 01, 2012 21:23:50 Jonathan M Davis wrote:
 On Monday, April 02, 2012 05:46:24 L-MAN wrote:
 Sure, if you have large structs, making a lot of copies of 
 them can be
 expensive. But to avoid that, you're going to have to avoid 
 coding in a way
 which creates temporaries, and expressions like
 
 (abc1+abc2*20.0)+(abc1*abc2+abc1*20.0)
 
 will _always_ create temporaries. The classic, arithmetic 
 operators are
 classic examples of functions which create temporaries (all of 
 which are
 rvalues). So, you can't code that way and expect to avoid 
 temporaries.

I should point out that with optimizations turned on, in some cases the compiler can optimize out the copying of temporaries, though not generally the temporaries themselves. For instance auto a = abc1 + abc2 * 20.0 results in a temporary for the result of * and a temporary for the result of +. But the temporary for the result of + will be optimized out (probably even without turning on optimizations), because you're using that result to initialize a variable. And while the result of * will result in a temporary, the copy that gets made when it's passed to opBinary!"+" should be optimized out when compiled with optimizations turned on. So, the optimizer should be able to reduce the problem a fair bit if you compile with -O. -inline may be able to reduce it even further. So, with optimizations turned on, you may not get anywhere near as many copies as you expected. And using auto ref for the parameters (which is easy with the overloaded arithmetic operators, since they're already templated) should also help reduce the number of copies made, but it _is_ up to the compiler whether it makes a copy or not with auto ref, so that depends on the compiler. So, you should be able to reduce the number of copies simply by compiling with -O and -inline, but the temporaries themselves will exist regardless. - Jonathan M Davis

Apr 01 2012
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 02 Apr 2012 00:23:50 -0400, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:


 No. It's not. It's a temporary, and temporaries are almost always  
 rvalues. The
 sole (and very bizarre) exception is struct literals (e.g. ABC(20) is
 currently considered an lvalue). It results in the bizarre situation  
 that a
 function which takes a const ref will work with ABC(20) but won't work  
 with a
 function that returns ABC(20). It's a point of debate and may be  
 changed, in
 which case ABC(20) would not be an lvalue anymore. But regardless, the  
 result
 of operations such as + are _always_ rvalues.

Also, 'this' is passed as a reference, even though it could be an rvalue. This makes for some WTF cases. For example: struct S { int x; S opBinary(string op)(ref const S other) const { return S(other.x + x);} } void main() { auto s = S(1); auto s2 = S(2); auto s3 = (s + s2) + s2; // ok auto s4 = s2 + (s + s2); // error } I think the current state of affairs is far from ideal, and really should get some more attention, but it's quite low on the priority list. -Steve
 Normally, the only items that are lvalues are variables and return values
 which are ref. The result of a function or operator such as + is most
 definitely _not_ an lvalue, since you can't return their results by ref.  
 It's a
 classic example on an rvalue.

 If you want to avoid making copies, then you're going to need to make  
 your
 parameters auto ref (and hope that the compiler decides that making a  
 copy is
 more expensive - I don't know how it decides that) or make them const  
 ref and
 use lvalues - and using lvalues generally means creating explicit  
 variables
 and _not_ using temporaries. So, your long expression with + and * would  
 have
 to be changed to use += and *=.

 when I draw to the screen 50000 graphical objects by OpenGL, it
 uses a matrices operation via D-language structures (uses all
 math operators and makes some matrices operations for every
 graphical object on the scene for each time), the unnecessary
 copyes of it(matrices) is very bad for engine performance.

Sure, if you have large structs, making a lot of copies of them can be expensive. But to avoid that, you're going to have to avoid coding in a way which creates temporaries, and expressions like (abc1+abc2*20.0)+(abc1*abc2+abc1*20.0) will _always_ create temporaries. The classic, arithmetic operators are classic examples of functions which create temporaries (all of which are rvalues). So, you can't code that way and expect to avoid temporaries. - Jonathan M Davis

Apr 02 2012
prev sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, April 03, 2012 00:28:03 Timon Gehr wrote:
 On 04/02/2012 06:23 AM, Jonathan M Davis wrote:
 No. It's not. It's a temporary, and temporaries are almost always rvalues.
 The sole (and very bizarre) exception is struct literals (e.g. ABC(20) is
 currently considered an lvalue).

DMD 2.059head treats struct literals as rvalues.

Awesome! - Jonathan M Davis
Apr 02 2012