• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • Visual C++ 2005中的命名返回值優化

    發表于:2007-05-25來源:作者:點擊數: 標簽:返回命名C++visual2005
    多年來,Microsoft Visual C++編譯器一直在努力尋求更新的技術與優化方式,以求最大可能地提高程序的 性能 。此文描述了Visual C++編譯器在不同情況下,是怎樣消除多余的復制構造函數和析構函數的。 通常來說,當方法返回對象的一個實例時,會創建一個臨時對
     多年來,Microsoft Visual C++編譯器一直在努力尋求更新的技術與優化方式,以求最大可能地提高程序的性能。此文描述了Visual C++編譯器在不同情況下,是怎樣消除多余的復制構造函數和析構函數的。

       通常來說,當方法返回對象的一個實例時,會創建一個臨時對象,并通過復制構造函數復制到目標對象中。在C++標準中,允許省略復制構造函數(哪怕會導致不同的程序行為),但這有一個副作用,就是編譯器可能會把兩個對象當成一個。Visual C++ 8.0(Visual C++ 2005)充分利用了C++標準的可伸縮性,加入了一些新的特性——命名返回值優化(NRVO)。NRVO消除了基于堆棧返回值的復制構造函數和析構函數,并去除了對多余復制構造函數和析構函數的調用,從而全面地提高了程序的性能。但要注意到,優化和未優化的代碼,可能會有不同的程序行為表現。

      而在某些情況下,NRVO不會進行優化(參見優化的局限性一節),以下是一些常見的情況:

      ·不同的路徑返回不同的命名對象

      ·引入EH狀態的多重返回路徑(甚至所有的路徑中返回相同的命名對象)

      ·由內聯匯編語句引用的命名返回對象

      NRVO優化概述

      以下是一個簡單的示例,演示了優化是怎樣被實現的:

    clearcase/" target="_blank" >cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    A MyMethod (B &var)
    {
     A retVal;
     retVal.member = var.value + bar(var);
     return retVal;
    }

      使用上述函數的程序可能會有一個像如下所示的構造函數:

    valA = MyMethod(valB);

      由MyMethod返回的值會創建在內存空間中,并通過隱藏的參數指向ValA。以下是函數中帶有隱藏參數,并清晰地寫明構造和析構函數時樣子:

    A MyMethod (A &_hiddenArg, B &var)
    {
     A retVal;
     retVal.A::A(); // retVal的構造函數
     retVal.member = var.value + bar(var);
     _hiddenArg.A::A(retVal); // A的復制構造函數
     return;
     retVal.A::~A(); // retVal的析構函數
    }

      從以上代碼中,很明顯可看出有一些可以優化的地方。最基本的想法是消除基于堆棧的臨時值(retVal),并使用隱藏參數,從而消除那些基于堆棧值的復制構造函數和析構函數。以下是NRVO優化過的代碼:

    A MyMethod(A &_hiddenArg, B &var)
    {
     _hiddenArg.A::A();
     _hiddenArg.member = var.value + bar(var);
     Return
    }

      示例代碼

      Sample1.cpp:比較簡單的示例代碼

    #include <stdio.h>
    class RVO
    {
     public:
      RVO(){printf("I am in constructor\n");}
      RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
      ~RVO(){printf ("I am in destructor\n");}
     int mem_var;
    };

    RVO MyMethod (int i)
    {
     RVO rvo;
     rvo.mem_var = i;
     return (rvo);
    }

    int main()
    {
     RVO rvo;
     rvo=MyMethod(5);
    }

      打開或關閉NRVO編譯sample1.cpp,將會產生不同的程序行為。

      不帶NRVO編譯(cl /Od sample1.cpp),下面是輸出內容:

      I am in constructor
      I am in constructor
      I am in copy constructor
      I am in destructor
      I am in destructor
      I am in destructor

      用NRVO選項編譯(cl /O2 sample1.cpp),以下是輸出內容:

      I am in constructor
      I am in constructor
      I am in destructor
      I am in destructor

      Sample2.cpp:較復雜一點的代碼

    #include <stdio.h>
    class A {
     public:
      A() {printf ("A: I am in constructor\n");i = 1;}
      ~A() { printf ("A: I am in destructor\n"); i = 0;}
      A(const A& a) {printf ("A: I am in copy constructor\n"); i = a.i;}
      int i, x, w;
    };

    class B {
     public:
      A a;
      B() { printf ("B: I am in constructor\n");}
      ~B() { printf ("B: I am in destructor\n");}
      B(const B& b) { printf ("B: I am in copy constructor\n");}
    };

    A MyMethod()
    {
     B* b = new B();
     A a = b->a;
     delete b;
     return (a);
    }

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

      不帶NRVO(cl /Od sample2.cpp)的輸出如下:

    A: I am in constructor
    A: I am in constructor
    B: I am in constructor
    A: I am in copy constructor
    B: I am in destructor
    A: I am in destructor
    A: I am in copy constructor
    A: I am in destructor
    A: I am in destructor
    A: I am in destructor

      當打開NRVO優化時,輸出如下:

    A: I am in constructor
    A: I am in constructor
    B: I am in constructor
    A: I am in copy constructor
    B: I am in destructor
    A: I am in destructor
    A: I am in destructor
    A: I am in destructor
      優化的局限性

      在某些情況下,NRVO優化不會起作用,以下是存在優化局限性的一些示例程序。

      Sample3.cpp:含有例外的代碼

      在例外(Exception)情況中,隱藏參數必須在它被替換的臨時范圍內析構。

    //RVO類定義在sample1.cpp中

    #include <stdio.h>

    RVO MyMethod (int i)
    {
     RVO rvo;
     rvo.mem_var = i;
     throw "I am throwing an exception!";
     return (rvo);
    }

    int main()
    {
     RVO rvo;
     try
     {
      rvo=MyMethod(5);
     }
     catch (char* str)
     {
      printf ("I caught the exception\n");
     }
    }

      不帶NRVO編譯(cl /Od /EHsc sample3.cpp),輸出如下:

    I am in constructor
    I am in constructor
    I am in destructor
    I caught the exception
    I am in destructor

      如果注釋掉“throw”語句,輸出將會如下如示:

    I am in constructor
    I am in constructor
    I am in copy constructor
    I am in destructor
    I am in destructor
    I am in destructor

      現在,如果注釋掉“throw”語句,并且用NRVO編譯,程序輸出如下:

    I am in constructor
    I am in constructor
    I am in destructor
    I am in destructor

      這就是說,不管打開或關閉NRVO選項,sample3.cpp的程序行為都一樣。

      Sample4.cpp:不同的命名對象

      想要充分利用優化,所有的返回路徑必須都返回相同的命名對象,請看如下示例代碼:

    #include <stdio.h>

    class RVO
    {
     public:
      RVO(){printf("I am in constructor\n");}
      RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
     int mem_var;
    };

    RVO MyMethod (int i)
    {
     RVO rvo;
     rvo.mem_var = i;
     if (rvo.mem_var == 10)
      return (RVO());
     return (rvo);
    }

    int main()
    {
     RVO rvo;
     rvo=MyMethod(5);
    }

      優化打開時(cl /O2 sample4.cpp)的輸出,與沒有進行任何優化時(cl /Od sample.cpp)的輸出是一樣的。因為不是所有的返回路徑都返回同一命名對象,所以NRVO此時不起任何作用。

    I am in constructor
    I am in constructor
    I am in copy constructor

      如果把上述代碼的所有返回路徑都改為返回rvo(如下例Sample4_modified.cpp),此時優化就會消除多余的復制構造函數。

      經過修改的Sample4_Modified.cpp,以利用NRVO。

    #include <stdio.h>

    class RVO
    {
     public:
      RVO(){printf("I am in constructor\n");}
      RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
     int mem_var;
    };

    RVO MyMethod (int i)
    {
     RVO rvo;
     if (i==10)
      return (rvo);
     rvo.mem_var = i;
     return (rvo);
    }

    int main()
    {
     RVO rvo;
     rvo=MyMethod(5);
    }

      此時(cl /O2 Sample4_Modified.cpp)的輸出:

    I am in constructor
    I am in constructor

      Sample5.cpp:EH限制

      以下的Sample5與Sample4基本一致,除了增加了RVO類的析構函數,并具有多重返回路徑,且引入的析構函數在函數中創建了一個EH狀態。由于編譯器跟蹤的復雜性,此類對象通常需要被析構,但它阻止了返回值優化,這也是Visual C++ 2005在將來需要改進的地方。

    //RVO類定義在sample1.cpp中

    #include <stdio.h>
    RVO MyMethod (int i)
    {
     RVO rvo;
     if (i==10)
      return (rvo);
     rvo.mem_var = i;
     return (rvo);
    }

    int main()
    {
     RVO rvo;
     rvo=MyMethod(5);
    }

      不論打開或關閉優化,Sample5.cpp都會產生相同的結果。

    I am in constructor
    I am in constructor
    I am in copy constructor
    I am in destructor
    I am in destructor
    I am in destructor

      要想打開NRVO優化,必須消除多重返回點,可以像如下所示修改MyMethod:

    RVO MyMethod (int i)
    {
     RVO rvo;
     if (i!=10)
      rvo.mem_var = i;
     return(rvo);
    }

      Sample6.cpp:內聯匯編限制

      當命名返回對象被內聯匯編語句所引用時,編譯器不會進行NRVO優化,請看下例代碼:

    #include <stdio.h>
    // RVO類定義在sample1.cpp中

    RVO MyMethod (int i)
    {
     RVO rvo;
     __asm {
      mov eax,rvo //可以注釋掉此行
      mov rvo,eax //可以注釋掉此行
     }
     return (rvo);
    }

    int main()
    {
     RVO rvo;
     rvo=MyMethod(5);
    }

      即使打開優化選項(cl /O2 sample6.cpp)來編譯sample6.cpp,NRVO也不會起作用。這是因為內聯匯編語句中引用了返回對象,因此,打開或關閉優化選項,輸出都會像如下所示:

    I am in constructor
    I am in constructor
    I am in copy constructor
    I am in destructor
    I am in destructor
    I am in destructor

      從以上輸出,可清楚地看到,復制構造函數和析構函數并沒有被消除。但如果注釋掉匯編語句,優化將會消除掉這些函數調用。
     優化的副作用

      程序員必須意識到,如此之類的優化將會影響到程序的流程,以下的示例代碼演示了優化所帶來的影響。

      Sample7.cpp
     
    #include <stdio.h>

    int NumConsCalls=0;

    int NumCpyConsCalls=0;

    class RVO

    {

    public:

    RVO(){NumConsCalls++;}

    RVO (const RVO& c_RVO) {NumCpyConsCalls++;}

    };

    RVO MyMethod ()

    {

    RVO rvo;

    return (rvo);

    }

    void main()

    {

    RVO rvo;

    rvo=MyMethod();

    int Division = NumConsCalls / NumCpyConsCalls;

    printf ("Constructor calls / Copy constructor calls = %d\n",Division);

    }

      不帶優化選項編譯sample7.cpp(cl /Od sample7.cpp),程序的輸出是在意料之中的,構造函數被調用了兩次,而復制構造函數被調用了一次,因此,輸出的結果為2。

    Constructor calls / Copy constructor calls = 2

      另一方面,如果打開優化選項編譯上述代碼(cl /O2 sample7.cpp),NRVO將會起作用,并消除掉復制構造函數,因此NumCpyConsCalls結果為零,從而引發程序的除零錯誤。如果像sample7.cpp中那樣沒有很好地處理這種例外錯誤,將會導致程序崩潰。

      從以上可看出,命名返回值優化(NRVO)消除了多余的函數調用,從而在一定程度上提高了程序的速度,需記住的是,優化有時也會有副作用,請謹慎使用。

    原文轉自:http://www.kjueaiud.com

    評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>