<

Return Value Optimization techniques implemented by C++ compilers

Published on
18,163 Points
6,463 Views
7 Endorsements
Last Modified:
Awarded
Community Pick
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

Cloud Class® Course: MCSA MCSE Windows Server 2012

This course teaches how to install and configure Windows Server 2012 R2.  It is the first step on your path to becoming a Microsoft Certified Solutions Expert (MCSE).

Join & Write a Comment

Suggested Articles

The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
Next Article:

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month