C++編譯器優化技術:RVO、NRVO和複製省略

現代編譯器缺省會使用RVO(return value optimization,返回值優化)、NRVO(named return value optimization、命名返回值優化)和複製省略(Copy elision)技術,來減小拷貝次數來提高代碼的運行效率c++

注1:vc六、vs沒有提供編譯選項來關閉該優化,不管是debug仍是release都會進行RVO和複製省略優化ide

注2:vc六、vs2005如下及vs2005+ Debug上不支持NRVO優化,vs2005+ Release支持NRVO優化函數

注3:g++支持這三種優化,而且可經過編譯選項:-fno-elide-constructors關閉優化優化

 

RVOthis

#include <stdio.h>
class A
{
public:
    A()
    {
        printf("%p construct\n", this);
    }
    A(const A& cp)
    {
        printf("%p copy construct\n", this);
    }
    ~A() 
    {
        printf("%p destruct\n", this);
    }
};

A GetA()
{
    return A();
}

int main()
{
    {
        A a = GetA();
    }

    return 0;
}

在g++和vc六、vs中,上述代碼僅僅只會調用一次構造函數和析構函數 ,輸出結果以下:spa

0x7ffe9d1edd0f construct
0x7ffe9d1edd0f destruct

在g++中,加上-fno-elide-constructors選項關閉優化後,輸出結果以下:debug

0x7ffc46947d4f construct  // 在函數GetA中,調用無參構造函數A()構造出一個臨時變量temp
0x7ffc46947d7f copy construct // 函數GetA return語句處,把臨時變量temp作爲參數傳入並調用拷貝構造函數A(const A& cp)將返回值ret構造出來
0x7ffc46947d4f destruct // 函數GetA執行完return語句後,臨時變量temp生命週期結束,調用其析構函數~A()
0x7ffc46947d7e copy construct // 函數GetA調用結束,返回上層main函數後,把返回值變量ret作爲參數傳入並調用拷貝構造函數A(const A& cp)將變量A a構造出來
0x7ffc46947d7f destruct // A a = GetA()語句結束後,返回值ret生命週期結束,調用其析構函數~A()
0x7ffc46947d7e destruct // A a要離開做用域,生命週期結束,調用其析構函數~A()

注:臨時變量temp、返回值ret均爲匿名變量code

下面用c++代碼模擬一下其優化行爲:blog

#include <new>
A& GetA(void* p)
{
    //因爲p的內存是從外部傳入的,函數返回後仍然有效,所以返回值可爲A&
    //vs中,如下代碼還能夠寫成:
    // A& o = *((A*)p);
    // o.A::A(); 
    // return o;
    return *new (p) A(); // placement new
}

int main()
{
    {
        char buf[sizeof(A)];
        A& a = GetA(buf);
        a.~A();
    }

    return 0;
}

 

NRVO生命週期

g++編譯器、vs2005+ Release(開啓/O2及以上優化開關)

修改上述代碼,將GetA的實現修改爲:

A GetA()
{
    A o;
    return o;
}

 

在g++、vs2005+ Release中,上述代碼也僅僅只會調用一次構造函數和析構函數 ,輸出結果以下:

0x7ffe9d1edd0f construct
0x7ffe9d1edd0f destruct

g++加上-fno-elide-constructors選項關閉優化後,和上述結果同樣

0x7ffc46947d4f construct
0x7ffc46947d7f copy construct
0x7ffc46947d4f destruct
0x7ffc46947d7e copy construct
0x7ffc46947d7f destruct
0x7ffc46947d7e destruct

但在vc六、vs2005如下、vs2005+ Debug中,沒有進行NRVO優化,輸出結果爲:

18fec4 construct  // 在函數GetA中,調用無參構造函數A()構造出一個臨時變量o
18ff44 copy construct  // 函數GetA return語句處,把臨時變量o作爲參數傳入並調用拷貝構造函數A(const A& cp)將返回值ret構造出來
18fec4 destruct  // 函數GetA執行完return語句後,臨時變量o生命週期結束,調用其析構函數~A()
18ff44 destruct // A a要離開做用域,生命週期結束,調用其析構函數~A()

下面用c++代碼模擬一下vc六、vs2005如下、vs2005+ Debug上的行爲:

#include <new>
A& GetA(void* p)
{
    A o;
    //因爲p的內存是從外部傳入的,函數返回後仍然有效,所以返回值可爲A&
    //vs中,如下代碼還能夠寫成:
    // A& t = *((A*)p);
    // t.A::A(o); 
    // return t;
    return *new (p) A(o); // placement new
}

int main()
{
    {
        char buf[sizeof(A)];
        A& a = GetA(buf);
        a.~A();
    }

    return 0;
}

注:與g++、vs2005+ Release相比,vc六、vs2005如下、vs2005+ Debug只優化掉了返回值到變量a的拷貝,命名局部變量o沒有被優化掉,因此最後一共有2次構造和析構的調用

 

複製省略

典型狀況是:調用構造函數進行值類型傳參

void Func(A a) 
{
}

int main()
{
    {
        Func(A());
    }

    return 0;
}

在g++和vc六、vs中,上述代碼僅僅只會調用一次構造函數和析構函數 ,輸出結果以下:

0x7ffeb5148d0f construct
0x7ffeb5148d0f destruct

在g++中,加上-fno-elide-constructors選項關閉優化後,輸出結果以下: 

0x7ffc53c141ef construct   // 在main函數中,調用無參構造函數構造實參變量o
0x7ffc53c141ee copy construct // 調用Func函數後,將實參變量o作爲參數傳入並調用拷貝構造函數A(const A& cp)將形參變量a構造出來
0x7ffc53c141ee destruct // 函數Func執行完後,形參變量a生命週期結束,調用其析構函數~A()
0x7ffc53c141ef destruct // 返回main函數後,實參變量o要離開做用域,生命週期結束,調用其析構函數~A()

下面用c++代碼模擬一下其優化行爲:

void Func(const A& a) 
{
}

int main()
{
    {
        Func(A());
    }

    return 0;
}

 

優化失效的狀況

開啓g++優化,獲得如下各類失效狀況的輸出結果:

(1)根據不一樣的條件分支,返回不一樣變量

A GetA(bool bflag)
{
    A a1, a2;
    if (bflag)
        return a1;
    return a2;
}

int main()
{
    A a = GetA(true);

    return 0;
}
0x7ffc3cca324f construct
0x7ffc3cca324e construct
0x7ffc3cca327f copy construct
0x7ffc3cca324e destruct
0x7ffc3cca324f destruct
0x7ffc3cca327f destruct

注1:2次缺省構造函數調用:用於構造a一、a2

注2:1次拷貝構造函數調用:用於拷貝構造返回值

注3:這兒仍然用右值引用優化掉了一次拷貝函數調用:返回值賦值給a

(2)返回參數變量

(3)返回全局變量

(4)返回複合數據類型中的成員變量

(5)返回值賦值給已構造好的變量(此時會調用operator==賦值運算符)

 

參考

Return Value Optimization 

What are copy elision and return value optimization?

Copy elision(wiki)

C++ 命名返回值優化(NRVO)

Named Return Value Optimization in Visual C++ 2005 

相關文章
相關標籤/搜索