RVO

198 views
Skip to first unread message

Jeffrey Baker

unread,
Jan 13, 2008, 6:50:17 AM1/13/08
to
I was reading Post subject "class returning member function", Dated -12/
27-28 /07 and found it not to be of value.
Return Value Optimization RVO seems to do no better than a bitwise
assignment. Except the bitwise is more efficient if that is what RVO is to
accomplish. What is the value or need of RVO?

Jeff

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Daniel Krügler

unread,
Jan 13, 2008, 4:01:27 PM1/13/08
to
On 13 Jan., 12:50, "Jeffrey Baker" <tbird...@earthlink.net> wrote:
> I was reading Post subject "class returning member function", Dated -12/
> 27-28 /07 and found it not to be of value.
> Return Value Optimization RVO seems to do no better than a bitwise
> assignment. Except the bitwise is more efficient if that is what RVO is to
> accomplish. What is the value or need of RVO?

RVO is special grant for optimization, see
several answers of the more recent thread

"Objects returned by value don't use copy constructors!"

found at

http://preview.tinyurl.com/yveqrx

which should answer your question.
If not, please refine your question a bit.

Greetings from Bremen,

Daniel Krügler

Juha Nieminen

unread,
Jan 13, 2008, 4:01:59 PM1/13/08
to
Jeffrey Baker wrote:
> I was reading Post subject "class returning member function", Dated -12/
> 27-28 /07 and found it not to be of value.
> Return Value Optimization RVO seems to do no better than a bitwise
> assignment. Except the bitwise is more efficient if that is what RVO is to
> accomplish. What is the value or need of RVO?

Without return value optimization the function would have to construct
a temporary object (which will be used as the return value) and then the
caller of that function would have to make a copy constructor call in
order to copy the contents of that temporary object to the destination
object.

With return value optimization the return value is constructed
directly onto the destination object. No temporary object is created nor
the copy constructor is called.

This has obvious advantages if making a copy constructor call is a
heavy operation (eg. if the object is an enormous deep-copying array),
plus it saves memory (during the copy constructor call there has to be
two objects taking that much memory).

(Note, however, that you can't *trust* that RVO is always used. There
are certain situations where the compiler cannot do it and the temporary
object is created and the copy constructor called.)

Carl Barron

unread,
Jan 13, 2008, 4:12:35 PM1/13/08
to
In article <13ojbel...@corp.supernews.com>, Jeffrey Baker
<tbir...@earthlink.net> wrote:

> I was reading Post subject "class returning member function", Dated -12/
> 27-28 /07 and found it not to be of value.
> Return Value Optimization RVO seems to do no better than a bitwise
> assignment. Except the bitwise is more efficient if that is what RVO is to
> accomplish. What is the value or need of RVO?
>

RVO uses the fact the compiler knows where it will store a tempory
returned from the function and the function always returns a copy
of the same instance of the class returned, so it creates that object
where it would be copied to, and avoids one copy and destruct.
This is not bitwise copy. There is no requirement that it be
performed, even if it is possible to do so.

RVO eliminates a copy
std::string foo()
{
std::string a('a',1000000);
return a;
}

void test()
{
std::string b = foo();
// use b
}


RVO here eliminates a copy of at least 1000000 chars, surely
different than just copying internal pointers to the heap and then
deleting the ptrs passing the ptrs to deallocated memory [ub] back to
the caller of the function.

Ulrich Eckhardt

unread,
Jan 13, 2008, 4:11:34 PM1/13/08
to
Jeffrey Baker wrote:
> I was reading Post subject "class returning member function", Dated -12/
> 27-28 /07 and found it not to be of value.
> Return Value Optimization RVO seems to do no better than a bitwise
> assignment. Except the bitwise is more efficient if that is what RVO is to
> accomplish. What is the value or need of RVO?

That's not it. Consider following code being executed:

object foo() {
object o;
...
return o;
}

object x = foo();

One could imagine that this code causes an object to be constructed, then
being returned from a function (which creates a temporary, i.e. a copy of
the original). This temporary is then used to create the local object 'x'
in the calling code, i.e. again copied.

RVO now has several possible effects to this. Firstly, the temporary value
returned from the function and the local object inside the function can be
the same. This work because the lifetime of one ends when the others begins
and because one is a copy of the other. Since the memory where this object
is passed can be known at the start of the function and also that always
the same object is returned, it is conceivable that the local object is
already created in that place. Secondly, also the temporary returned from
the function and the local object 'x' can be the same for exactly the same
reasons, just that it's a transfer of the temporary to 'x'.

So, in fact there is no copying going on, not even bitwise copying. Bitwise
copying wouldn't work anyway, because objects can contain backpointers to
themselves. Also, it is so much more simple to avoid copying than
optimising it. ;)

Uli

int...@gmail.com

unread,
Jan 13, 2008, 4:12:58 PM1/13/08
to
On Jan 13, 2:50 pm, "Jeffrey Baker" <tbird...@earthlink.net> wrote:
> I was reading Post subject "class returning member function", Dated -12/
> 27-28 /07 and found it not to be of value.
> Return Value Optimization RVO seems to do no better than a bitwise
> assignment. Except the bitwise is more efficient if that is what RVO is to
> accomplish. What is the value or need of RVO?

To avoid invoking a (potentially expensive) copy constructor. Consider
returning a large vector from a function. Without RVO, it will
allocate memory for itself inside the function, then the copy
constructor gets called upon return, allocates the same amount of
memory, memcpy's all the data, and deallocates the original memory
block. For a vector of several hundred thousand elements, this can be
a very expensive operation.

p.y...@googlemail.com

unread,
Jan 14, 2008, 3:38:07 PM1/14/08
to
Excuse me, could someone clear me following.
What if copy ctor and dtor has critical semantics, for example the
object has counter, and when the counter touches 0, the dtor is
called.
now imagine, that we pass the object to the function and copy ctor is
in game, and when we out of the function the RVO happens and no
destructor has been called.

we have a problem.

Mathias Gaunard

unread,
Jan 14, 2008, 8:27:54 PM1/14/08
to
On 14 jan, 21:38, p.yu...@googlemail.com wrote:

> now imagine, that we pass the object to the function and copy ctor is
> in game, and when we out of the function the RVO happens and no
> destructor has been called.
>
> we have a problem.

Are you thinking of

Thing foo(const Thing& t)
{
return t;
}

?

No NRVO can happen here.

NRVO can only happen in this case:

Thing foo()
{
Thing t;
return t;
}

Thing t = foo();

Basically, it can only elide copies when it is safe and logic to do
so...

peter koch larsen

unread,
Jan 14, 2008, 8:33:00 PM1/14/08
to
On 14 Jan., 21:38, p.yu...@googlemail.com wrote:
> Excuse me, could someone clear me following.
> What if copy ctor and dtor has critical semantics, for example the
> object has counter, and when the counter touches 0, the dtor is
> called.
> now imagine, that we pass the object to the function and copy ctor is
> in game, and when we out of the function the RVO happens and no
> destructor has been called.
>
> we have a problem.

No (N)RVO is only for returning local objects. It does not apply in
the case where you return an object passed as a parameter.

Carlos Moreno

unread,
Jan 14, 2008, 8:32:48 PM1/14/08
to

> Return Value Optimization RVO seems to do no better than a bitwise
> assignment. Except the bitwise is more efficient if that is what RVO is to
> accomplish. What is the value or need of RVO?

I would like to emphasize one detail that I feel has been neglected
in the replies so far --- they have explained why RVO is good.

I would point out that the comparison between RVO and bitwise
assignment is entirely meaningless, since one has absolutely
nothing to do with the other one.

RVO is an optimization technique that respects all the rules of
the language --- bitwise assignment is not, and as a general rule,
is an operation that does not produce the correct behaviour.

RVO, as you can see from the other replies, is simply the compiler
being clever and noticing that the behaviour of the program is the
same whether a copy of an object is created or not --- in the
context of a function returning an object, of course. There's a
subtlety involved (observable behaviour of copy constructors), but
no need to go there for the purpose of this discussion.

Bitwise assignment is, as a general rule, *the wrong thing to do*
and would lead to situations that we would describe as a bug in
the program.

There simply is no comparison possible between RVO and bitwise
assignment.

Carlos
--

Sean Hunt

unread,
Jan 15, 2008, 8:51:31 AM1/15/08
to
On Jan 14, 1:38 pm, p.yu...@googlemail.com wrote:
> Excuse me, could someone clear me following.
> What if copy ctor and dtor has critical semantics, for example the
> object has counter, and when the counter touches 0, the dtor is
> called.
> now imagine, that we pass the object to the function and copy ctor is
> in game, and when we out of the function the RVO happens and no
> destructor has been called.
>
> we have a problem.

The standard's take: "Too bad, so sad." RVO is allowed to occur
regardless of any side-effects that might happen. Even if it is
critical. The lesson is that you should be careful. Fortunately, it's
hard to design a program that will fail when RVO is used, because if
the object doesn't change at all, then it might have well been
constructed within the body of the calling function and the call
skipped. If you do manage, that's just bad coding.

Martin Bonner

unread,
Jan 15, 2008, 4:31:15 PM1/15/08
to
On Jan 14, 8:38 pm, p.yu...@googlemail.com wrote:
> Excuse me, could someone clear me following.
> What if copy ctor and dtor has critical semantics, for example the
> object has counter, and when the counter touches 0, the dtor is
> called.
> now imagine, that we pass the object to the function and copy ctor is
> in game, and when we out of the function the RVO happens and no
> destructor has been called.
>
> we have a problem.

There are two answers to this:
1. The optimizations will always involve the ellision of a copy-
constructor and a destructor *as a pair*. If the copy constructor
increments and the destructor decrements, the two effects cancel out
so everything is pretty much as before.

2. It is your responsibility as a programmer to make sure that this
doesn't cause you a problem. The point is that if the copy
constructor and the destructor are doing some expensive operations
(increment and decrement might involve grabbing a mutex; copying a big
vector will involve allocating a lot of memory and a lot of copying),
then the optimization will have a significant benefit, and all /
reasonable/ uses of the copy constructor / destructor won't be
affected by eliding them in pairs.

Jeffrey Baker

unread,
Jan 18, 2008, 4:47:13 AM1/18/08
to
Thanks, there is one question on the practical side. The code is below. If
I understand it then it would be the function test() that is called to
directly place object a into object b without a copy being created. This
reduces memory usage and creates faster code.

Is this the same whether it is in object form or in C form? Class vs.
main()?

Jeff

RVO eliminates a copy
std::string foo()
{
std::string a('a',1000000);
return a;
}

void test()
{
std::string b = foo();
// use b
}


"Jeffrey Baker" <tbir...@earthlink.net> wrote in message
news:13ojbel...@corp.supernews.com...
: Jeff
:
:
:
: --
:

Alberto Ganesh Barbati

unread,
Jan 18, 2008, 6:05:33 PM1/18/08
to
Jeffrey Baker ha scritto:

> Thanks, there is one question on the practical side. The code is below. If
> I understand it then it would be the function test() that is called to
> directly place object a into object b without a copy being created. This
> reduces memory usage and creates faster code.

The standard *allows* the compiler to elide the copy, but it does not
require it. So the copy might or might not be elided. As a rule of
thumb, the better/newer the compiler, the greater the chance that the
copy is elided.

> Is this the same whether it is in object form or in C form? Class vs.
> main()?

I'm sorry, but I couldn't find any sense in this sentence...

> RVO eliminates a copy
> std::string foo()
> {
> std::string a('a',1000000);
> return a;
> }

Actually, that is called NRVO (Named Return Value Optimization). An
example of RVO is the following:

std::string foo()
{
return std::string('a',1000000);
}

(the difference is that the returned expression is constructed as part
of the return statement, rather than in a separate statement)

A lot of compilers can optimize RVO, while a smaller number actually
optimizes NRVO, despite the similarities. Hopefully, that number is
growing steadily.

HTH,

Ganesh

Carl Barron

unread,
Jan 18, 2008, 8:21:59 PM1/18/08
to
Jeffrey Baker <tbir...@earthlink.net> wrote:

> Thanks, there is one question on the practical side. The code is below. If
> I understand it then it would be the function test() that is called to
> directly place object a into object b without a copy being created. This
> reduces memory usage and creates faster code.
>

> Is this the same whether it is in object form or in C form? Class vs.
> main()?
>
> Jeff
>
> RVO eliminates a copy
> std::string foo()
> {
> std::string a('a',1000000);
> return a;
> }
>
> void test()
> {
> std::string b = foo();
> // use b
> }
>

At least one copy is avoided. If the requirements of RVO are not met,
the compiler must create a stack copy , copies it someplace the caller
[test] can access, and return this copy. With RVO it can CREATE a where
the return statement would copy it,before returning control to test.
This is the copy that is eliminated.

bool some_test();

std::string fudge()


{
std::string a('a',1000000);

std:;string b('b',100);

if(some_test())
return a;
else
return b;
}
void foo()


{
std::string a('a',1000000);
return a;
}

note fudge must create a copy of a or b to pass back to calling routine,
in foo() it knows a is always returned so it can create a in this 'pass
back' area, avoiding one copy to this 'pass back' area.

If RVO is avaliable it is this copy to the 'pass back' area that is
avoided, not the copy from the 'pass back' area.

Jeffrey Baker

unread,
Jan 18, 2008, 8:24:48 PM1/18/08
to

>> RVO eliminates a copy
>> std::string foo()
>> {
>> std::string a('a',1000000);
>> return a;
>> }
>
> Actually, that is called NRVO (Named Return Value Optimization). An
> example of RVO is the following:
>
> std::string foo()
> {
> return std::string('a',1000000);
> }
>
> (the difference is that the returned expression is constructed as part
> of the return statement, rather than in a separate statement)
>
> A lot of compilers can optimize RVO, while a smaller number actually
> optimizes NRVO, despite the similarities. Hopefully, that number is
> growing steadily.
I am curious if the code below can be known in a compilers tools to know
how it is working. I am using Microsofts Visual C++ 2003 package. Is
there a
way to determined how true a compiler is to not creating a copy of the
object and creating a constructior?

#include <iostream>
using std::cout;
using std::endl;
#include <string>
using std::string;
class RVO
{
public:
string foo()
{
string a("about now is time for all");
return a;
}
string test()
{
string b = foo();
cout << b << endl;
return b;
}
private:
};
int main()
{
RVO o;
string c;
c = o.test();
cout << c << endl;

return 0;
}

Best regards,

Jeff

Sebastian Herbst

unread,
Jan 19, 2008, 2:17:13 AM1/19/08
to
Jeffrey Baker wrote:
> I am curious if the code below can be known in a compilers tools to know
> how it is working. I am using Microsofts Visual C++ 2003 package. Is
> there a
> way to determined how true a compiler is to not creating a copy of the
> object and creating a constructior?
>
If you remove line 3 of your code and insert your own string class,
and can make the the calls visible. I might be possible, that the
compiler does not remove the operator= to preserve these side-effects.
I do not know the standard well enough to answer that question.


#include <iostream>
using std::cout;
using std::endl;
#include <string>

class string : public std::string {
public:
string() {
cout << "default c'tor" << endl;
}
string(const char *c) : std::string(c) {
cout << "cstring c'tor" << endl;
}
string(const string &s) : std::string(s) {
cout << "copy c'tor" << endl;
}
const string &operator=(const string &s) {
static_cast<std::string &>(*this) = static_cast<const std::string &>(s);
cout << "op=" << endl;
return *this;


}
};
class RVO
{
public:
string foo()
{
string a("about now is time for all");
return a;
}
string test()
{
string b = foo();
cout << b << endl;
return b;
}
private:
};
int main()
{
RVO o;
string c;
c = o.test();
cout << c << endl;

return 0;
}

Regards,
Sebastian Herbst

Carl Barron

unread,
Jan 19, 2008, 5:15:01 AM1/19/08
to
Jeffrey Baker <tbir...@earthlink.net> wrote:

> Is
> there a
> way to determined how true a compiler is to not creating a copy of the
> object and creating a constructior?

have a no trival copy ctor such as
// std headers ommitted for brevity.
class foo
{
int i;
public:
foo(int x = 0):i(x){}
foo(const foo &x)

{ std::cout << "cpy ctr called\n";}
foo & operator ++()
{
++i;
return *this;
}
};

foo bar()
{
foo x;
++x;
return x;
}

int main()
{
foo y = bar();
}

This should give a trial of cpy ctor calls enough to count them.

Reply all
Reply to author
Forward
0 new messages