Efficient C ++ 第四章 RVO

转自
http://blog.chinaunix.net/uid-25872711-id-3018672.html

Efficient C ++ 第四章 RVO


返回值优化 Return Value  Optimization,简称RVO
  RVO是由编译器实现的,其优化原理如以下示例代码:
class Complex
{
    // Complex addition operator
    friend Complex operator+(const Complex&, const Complex&);
public:
    // Default constructor.
    // Value defaults to 0 unless otherwise specified.
    Complex (double r = 0.0, double i = 0.0) : real (r), imag (i) {}

    // Copy constructor
    Complex (const Complex& c) : real (c.real), imag (c.imag) {}

    // Assignment operator
    Complex& operator= (const Complex& c);

    ~Complex() {}
private:
    double real;
    double imag;
};

    看到有下面操作符的重载定义:
Complex operator+ (const Complex& a, const Complex& b)
{
    Complex retVal;

    retVal.real = a.real + b.real;
    retVal.imag = a.imag + b.imag;

    return retVal;
}

    假设c1、c2、c3都是Complex类型,我们执行如下操作:
c3 = c1 + c2;


    编译器是如何调用operator+的呢?大致伪码如下:
//创建一个临时__result对象,并做为参数传入如下函数原型
void Complex_Add (const Complex& __result,
                   const Complex& c1,
                   const Complex& c2)
{
    ...
}
//那么c3 = c1 + c2;实际执行了
struct Complex __tempResult;     // 临时对象,不构造
Complex_Add(__tempResult,c1,c2); // 传入参数
c3 = __tempResult;               // 把临时对象赋值给c3


    不做优化的情况下Complex_Add内部为:
void Complex_Add(const Complex& __tempResult,
              const Complex& c1,
              const Complex& c2)
{
    struct Complex retVal;
    retVal.Complex::Complex(); //调用构造函数

    retVal.real = a.real + b.real;
    retVal.imag = a.imag + b.imag;

    __tempResult.Complex::Complex(retVal);//拷贝构造__tempResult
    retVal.Complex::~Complex(); //析构retVal
    return;
}


    可以看的出来,这里存在retVal的构造和析构的浪费。编译器可以通过消除局部对象retVal来做优化,这就是返回值优化:
void Complex_Add (const Complex& __tempResult,
               const Complex& c1,
               const Complex& c2)
{
    __tempResult.Complex::Complex(); // Construct __tempResult
    __tempResult.real = a.real + b.real;
    __tempResult.imag = a.imag + b.imag;
    return;
}

    经过时间测试,发现使用RVO前后的执行时间相差50%左右。

  因为RVO是由编译器选择性使用,如存在多个返回分支的情况一般编译器不会使用RVO。如下:
Complex operator+ (const Complex& a, const Complex& b)
// operator+ version 1.
{
    Complex retVal;
    retVal.real = a.real + b.real;
    retVal.imag = a.imag + b.imag;
    return retVal;
}

Complex operator+ (const Complex& a, const Complex& b)
// operator+ version 2.
{
    double r = a.real + b.real;
    double i = a.imag + b.imag;

    return Complex (r,i);
}

    发现编译器对version 1没有使用RVO,version 2有使用。有些编译器对命名变量作为返回值的函数不做RVO,而不进行命名直接通过构造函数返回是可以的。
  我们应该了解编译器RVO开启条件,并且尽量使用可以进行优化的编码风格,如version 2。

猜你喜欢

转载自jacky-dai.iteye.com/blog/2307349