Skip to content

Instantly share code, notes, and snippets.

@Masstronaut
Last active February 29, 2016 07:37
Show Gist options
  • Save Masstronaut/91ba8711a293dd35596c to your computer and use it in GitHub Desktop.
Save Masstronaut/91ba8711a293dd35596c to your computer and use it in GitHub Desktop.
Test code for determining the behavior of Return Value Optimization in various situations
// This file is to test the behavior of various compilers' return value optimizations (RVO) in different scenarios
#include <iostream>
#include <cstdlib> // std::rand
#include <array>
#include <utility> // std::move
// This class is small enough to fit in a register, and should be a simple target for return value optimization
class SmallObject {
public:
static unsigned instance_count;
// default constructor
SmallObject() {
++instance_count;
std::cout << "SmallObject default constructed.\n";
}
// copy constructor
SmallObject(const SmallObject &rhs)
: m_var(rhs.m_var) {
++instance_count;
std::cout << "SmallObject copy constructed.\n";
}
// move constructor
SmallObject(SmallObject &&rhs)
: m_var(rhs.m_var) {
rhs.m_var = -1;
++instance_count;
std::cout << "SmallObject move constructed.\n";
}
// default destructor
~SmallObject() {
--instance_count;
std::cout << "SmallObject destructed.\n";
}
// assignment operator. We do nothing as both instances already existed
SmallObject& operator=(const SmallObject &rhs) {
m_var = rhs.m_var;
std::cout << "SmallObject assigned.\n";
}
// This function will perform transformations on the data in an attempt to confuse the optimizer
void do_something_complicated() {
m_var = std::rand() % ( RAND_MAX / 2 );
if ( m_var < 1000 )
m_var = 0;
else if (m_var < 10000)
m_var = 1;
}
private:
int m_var{0};
};
// This class is too large to fit in a register, and should require more work to have good RVO.
class LargeObject {
public:
static unsigned instance_count;
// default constructor
LargeObject() {
++instance_count;
std::cout << "LargeObject default constructed.\n";
}
// copy constructor
LargeObject(const LargeObject &rhs) {
++instance_count;
std::cout << "LargeObject copy constructed.\n";
}
// move constructor
LargeObject(LargeObject &&rhs)
: m_data(std::move(rhs.m_data)) {
++instance_count;
std::cout << "LargeObject move constructed.\n";
}
// default destructor
~LargeObject() {
--instance_count;
std::cout << "LargeObject destructed.\n";
}
// assignment operator. We do nothing as both instances already existed
LargeObject& operator=(const LargeObject &rhs) {
std::cout << "LargeObject assigned.\n";
}
// This function will perform transformations on the data in an attempt to confuse the optimizer
void do_something_complicated(){
for(auto &&it : m_data){
it = std::rand();
}
}
private:
std::array<unsigned, 256> m_data;
};
unsigned LargeObject::instance_count{ 0 };
unsigned SmallObject::instance_count{ 0 };
// Simple construction
SmallObject create_small_object_simple(){
return SmallObject();
}
void test_small_simple() {
SmallObject so = create_small_object_simple();
}
LargeObject create_large_object_simple(){
return LargeObject();
}
void test_large_simple() {
LargeObject lo = create_large_object_simple();
}
// Construction and manipulation
SmallObject create_small_object_complex(){
SmallObject so;
so.do_something_complicated();
return so;
}
void test_small_complex() {
SmallObject so = create_small_object_complex();
}
LargeObject create_large_object_complex(){
LargeObject lo;
lo.do_something_complicated();
return lo;
}
void test_large_complex() {
LargeObject lo = create_large_object_complex();
}
// Simple construction with forced move semantics
SmallObject move_small_object_simple() {
return std::move(SmallObject());
}
void test_small_simple_move() {
SmallObject so = move_small_object_simple();
}
LargeObject move_large_object_simple() {
return std::move(LargeObject());
}
void test_large_simple_move() {
LargeObject lo = move_large_object_simple();
}
// Construction and manipulation with forced move semantics
SmallObject move_small_object_complex() {
SmallObject so;
so.do_something_complicated();
return std::move(so);
}
void test_small_complex_move() {
SmallObject so = move_small_object_complex();
}
LargeObject move_large_object_complex() {
LargeObject lo;
lo.do_something_complicated();
return std::move(lo);
}
void test_large_complex_move() {
LargeObject lo = move_large_object_complex();
}
int main() {
std::cout << "Testing simple small object RVO: " << std::endl;
test_small_simple();
std::cout << std::endl;
std::cout << "Testing simple large object RVO: " << std::endl;
test_large_simple();
std::cout << std::endl;
std::cout << "Testing complex small object RVO: " << std::endl;
test_small_complex();
std::cout << std::endl;
std::cout << "Testing complex large object RVO: " << std::endl;
test_large_complex();
std::cout << std::endl;
std::cout << "Testing simple small object forced move semantics RVO: " << std::endl;
test_small_simple_move();
std::cout << std::endl;
std::cout << "Testing simple large object forced move semantics RVO: " << std::endl;
test_large_simple_move();
std::cout << std::endl;
std::cout << "Testing complex small object forced move semantics RVO: " << std::endl;
test_small_complex_move();
std::cout << std::endl;
std::cout << "Testing complex large object forced move semantics RVO: " << std::endl;
test_large_complex_move();
std::cout << std::endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment