簡體   English   中英

局部變量與 Class 變量編譯器優化; 有效與無效

[英]Local Variables vs. Class Variables Compiler Optimization; Works vs. Doesn't Work

我有一個代碼示例,當結構為 class 變量時,直接優化不起作用,但用作局部變量; 我想知道:為什么 class 變量公式沒有發生優化?

我的示例代碼的目的是讓 class 在構造時啟用或禁用,並且可能在其生命周期內更改。 我希望,當 object 在其整個生命周期內被禁用時,編譯器會優化掉所有在啟用 object 時有條件地執行的代碼。

具體來說,我有一個 std::ofstream ,我只想在“啟用”時寫入。 禁用時,我希望跳過所有格式化輸出。 (我真正的 class 是自己的,非平凡的消息格式。)

我發現當我將其表述為 class 時,我沒有得到我期望的優化。 但是,如果我將代碼全部復制為局部變量,我確實會看到預期的行為。

此外,我發現如果我不在示例類的方法主體的任何地方進行 std::ofstream 調用,如“open”、“exceptions”或“clear”,我也會得到預期的優化。 (但是,我的設計需要在 std::ofstream 上進行此類調用,所以對我來說這是一個有爭議的問題。)下面的代碼使用 MACRO DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR 來允許嘗試這種情況。

我的示例代碼使用“asm”表達式將注釋插入到生成的匯編代碼中。 如果在匯編中檢查編譯器的 output,我希望在“禁用測試”注釋之間沒有匯編。 我正在觀察“class disabled-test”注釋之間的組裝,但“locals disabled-test”注釋之間沒有組裝。

輸入 C++ 代碼:

#include <fstream> // ofstream

#define DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR 0

class Test_Ofstream
{
public:
    Test_Ofstream( const char a_filename[],
                   bool a_b_enabled )
    #if DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR
        : m_ofstream( a_filename ),
          m_b_enabled( a_b_enabled )
    {
    }
    #else
        : m_ofstream(),
          m_b_enabled( a_b_enabled )
    {
        m_ofstream.open( a_filename );
    }
    #endif

    void write_test()
    {
        if( m_b_enabled )
        {
            m_ofstream << "Some text.\n";
        }
    }

private:
    std::ofstream m_ofstream;
    bool m_b_enabled;
};

int main( int argc, char* argv[] )
{
    {
        Test_Ofstream test_ofstream( "test.txt", true );
        asm( "# BEGIN class enabled-test" );
        test_ofstream.write_test();
        asm( "# END class enabled-test" );
    }

    {
        Test_Ofstream test_ofstream( "test.txt", false );
        asm( "# BEGIN class disabled-test" );
        test_ofstream.write_test();
        asm( "# END class disabled-test" );
    }

    {
        bool b_enabled = true;
        #if DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR
        std::ofstream test_ofstream( "test.txt" );
        #else
        std::ofstream test_ofstream;
        test_ofstream.open( "test.txt" );
        #endif
        asm( "# BEGIN locals enabled-test" );
        if( b_enabled )
        {
            test_ofstream << "Some text.\n";
        }
        asm( "# END locals enabled-test" );
    }

    {
        bool b_enabled = false;
        #if DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR
        std::ofstream test_ofstream( "test.txt" );
        #else
        std::ofstream test_ofstream;
        test_ofstream.open( "test.txt" );
        #endif
        asm( "# BEGIN locals disabled-test" );
        if( b_enabled )
        {
            test_ofstream << "Some text.\n";
        }
        asm( "# END locals disabled-test" );
    }

    return 0;
}

output 匯編代碼:

##### Cut here. #####
#APP
# 53 "test_ofstream_optimization.cpp" 1
        # BEGIN class disabled-test
# 0 "" 2
#NO_APP
        cmpb        $0, 596(%esp)
        je  .L22
        movl        $.LC1, 4(%esp)
        movl        %ebx, (%esp)
.LEHB9:
        call        _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
.LEHE9:
.L22:
#APP
# 55 "test_ofstream_optimization.cpp" 1
        # END class disabled-test
# 0 "" 2
#NO_APP
##### Cut here. #####
#APP
# 116 "test_ofstream_optimization.cpp" 1
        # BEGIN locals disabled-test
# 0 "" 2
# 121 "test_ofstream_optimization.cpp" 1
        # END locals disabled-test
# 0 "" 2
#NO_APP
##### Cut here. #####

我意識到這可能與我正在使用的編譯器有關,即:g++-4.6 (Debian 4.6.1-4) 4.6.1; 編譯器標志:-Wall -S -O2。 然而,這似乎是一個如此簡單的優化,我很難相信它可能是編譯器搞砸了。

非常感謝任何幫助、見解或指導。

很簡單。 當您直接將代碼編寫為局部變量時,代碼會被內聯,編譯器會執行常量折疊。 當您在 class scope 中時,代碼未內聯且m_b_enabled的值未知,因此編譯器必須執行調用。 為了證明代碼在語義上是相等的並執行此優化,不僅必須內聯該調用,而且每次訪問 class。 編譯器很可能認為內聯 class 不會產生足夠的好處。 編譯器也可以選擇不內聯代碼,因為他們不知道如何內聯,而內聯asm表達式正是可能導致他們這樣做的事情,因為編譯器不知道如何處理您的匯編代碼。

通常,您會放置一個斷點並檢查反匯編。 無論如何,這就是我在 Visual Studio 中所做的。 任何類型的內聯匯編器都可能對優化器造成如此大的破壞。

當我刪除匯編器表達式時,Visual Studio 內聯了代碼,並且立即沒有執行優化。 堆疊優化通道的問題是您永遠無法獲得正確的順序來找到所有潛在的優化。

正如你所說,這將取決於編譯器。 但我的猜測是:

優化器可以證明沒有其他代碼可以修改 object bool b_enabled ,因為它是本地的,您永遠不會獲取它的地址或綁定對它的引用。 本地版本很容易優化。

DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR為真時, Test_Ofstream構造函數:

  • 調用ofstream(const char*)的構造函數
  • 初始化成員m_b_enabled

由於初始化test_ofstream.m_b_enabled和測試之間沒有任何操作,所以這個優化只是有點棘手,但聽起來 g++ 仍然管理它。

DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR為 false 時, Test_Ofstream構造函數:

  • 調用ofstream默認構造函數
  • 初始化成員m_b_enabled
  • 調用m_ofstream.open(const char*)

不允許優化器假設ofstream::open不會改變test_ofstream.m_b_enabled 我們知道它不應該,但理論上非內聯庫 function 可以找出包含其“this”參數的完整 object test_ofstream ,並以這種方式修改它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM