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!