<

Go Premium for a chance to win a PS4. Enter to Win

x

Return Value Optimization techniques implemented by C++ compilers

Published on
17,941 Points
6,241 Views
7 Endorsements
Last Modified:
Awarded
evilrix
An expert in cross-platform ANSI C/C++ development, specialising in meta-template programming and low latency scalable architecture design.
In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. Advances in compiler optimizations have all but eliminated this concern thanks to a clever set of optimizations implemented by most modern compilers.

The C++ standard allows the omission of the call to the copy constructor and, thus, allows the compiler to create a return value in the stack-frame of the calling function. This has the effect of allowing the compiler to treat both objects (in the caller and the callee) as the same entity, thus eliminating the need to take a copy.

There are two versions of this optimization available, Named Return Value Optimization (NRVO) and Return Value Optimization (RVO). Although the end result is the same, the syntax and semantics of each is slightly different:

RVO: Return Value Optimization is carried out when an object is constructed in-line within the return statement of a function, which would normally result in a temporary object being created on the stack, which is then copied into the calling functions stack-frame. When RVO is performed the object is created within the stack-frame of the calling function, thus avoiding the creation and destruction of an unnecessary temporary and the invocation of a copy constructor.

// Example of RVO
Bar Foo()
{
	return Bar();
}

Open in new window


Without RVO

Items constructed: 2
Items destructed: 1
Copies taken : 1

With RVO

Items constructed: 1
Items destructed: 0
Copies taken : 0

NRVO: Named Return Value Optimization is carried out when an object is created with a name within the called function and is then returned by name, which would normally result in a temporary object being copied on the stack, which is then copied into the calling functions stack-frame. When NRVO is performed the named object is created within the stack-frame of the calling function, thus avoiding the creation and destruction of an unnecessary temporary and the invocation of, potentially, two copy constructors.

// Example of NRVO
Bar Foo()
{
	Bar bar;
	return bar;
}

Open in new window


Without NRVO

Items constructed: 3
Items destructed: 2
Copies taken : 2

With NRVO

Items constructed: 1
Items destructed: 0
Copies taken : 0

It should be obvious by now that when RVO or NRVO are used the copy-constructor on the returned object may not be called. For this reason it is very important that you do not write code that relies on the calling of a copy-constructor (such as instance counting, for example) since it may or may not be called depending upon the compiler, the optimization level and the way the function is written.

Each compiler implements support for RVN and NRVO to varying degrees so it is important to refer to your favourite compilers documentation to establish how well supported these two optimizations are.

It is not always possible for a compiler to carry out NRVO, code must be written to facilitate it. Again, this does vary from compiler to compiler but if there are multiple return paths you can be pretty sure NRVO will not take place.

// Example of N/RVO 
#include <iostream>
 
struct MyClass
{
	MyClass()
	{
		std::cout << "MyClass::c_tor()" << std::endl;
	}
	
	MyClass(MyClass const &)
	{
		std::cout << "MyClass::cc_tor()" << std::endl;
	}
	
	~MyClass()
	{
		std::cout << "MyClass::d_tor()" << std::endl;
	}
}; 
MyClass NRVO()
{
	std::cout << "Named Return value Optimization" << std::endl;
	
	MyClass myClass;
	return myClass;
};
 
MyClass RVO()
{
	std::cout << "Return value Optimization" << std::endl;
	
	return MyClass();
}; 
MyClass NoNRVO()
{
	std::cout << "** NO *** Named Return value Optimization -- this is unlikely to optimize" << std::endl;
	
	if(0)
	{
		MyClass myClass;
		return myClass;
	}
	else
	{
		MyClass myClass;
		return myClass;
	}
}; 
MyClass NoRVO()
{
	std::cout << "** NO *** Return value Optimization ??? -- this should still optimize" << std::endl;
	
	if(0)
	{
		return MyClass();
	}
	else
	{
		return MyClass();
	}
}; 
int main(void)
{
	std::cout << ">>> START >>>" << std::endl;
	
	MyClass myClass1 = NRVO();
	MyClass myClass2 = RVO();
	
	MyClass myClass3 = NoNRVO();
	MyClass myClass4 = NoRVO(); 
	std::cout << "<<< END <<<" << std::endl;
}

Open in new window

7
Comment
Author:evilrix
2 Comments
 
LVL 20

Expert Comment

by:ikework
In case somebody wonders how relevant this is for a C++ programmer.
It is highly relevant, not only when optimizing, also when designing your classes.

We use a lot of 3D math code, using points/vectors/matrices.
Without this kind of optimization, we could not simply write (without performance hit):

  point = matrix * point;

we would have to write something like:

  multiply(matrix, point_input, point_result);

taking a reference to point_result, so there is no temporary object and no unnecessary copy.
This latter version can get very ugly for long formulas.

So this really matters for C++ programmers, great article Rix :)
0
 
LVL 49

Expert Comment

by:DanRollins
Great Article.  You got my Yes vote!
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Join & Write a Comment

The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
Next Article:

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month