我有一个代码示例,其中将直接优化结构构造为类变量时不起作用,但作为局部变量起作用;我想知道:为什么优化没有在类变量公式上进行?

我的示例代码的目的是要有一个在构造时启用或禁用的类,并可能在其生命周期内对其进行更改。我希望,当对象在整个生命周期中都被禁用时,编译器会优化掉启用该对象时有条件执行的所有代码。

具体来说,我有一个std :: ofstream我只想在“启用”时写入。禁用后,我希望跳过所有格式化输出。 (我的真实班级做的是自己的,平凡的消息格式。)

我发现当我将其表述为一个类时,没有得到我期望的优化。但是,如果我将代码全部复制为局部变量,则会看到预期的行为。

此外,我发现,如果我在示例类方法的主体中的任何位置都不进行std :: ofstream调用(例如“ open”,“ exceptions”或“ clear”),那么我也可以获得预期的优化。 (但是,我的设计需要在std :: ofstream上进行此类调用,所以对我来说这是一个有争议的问题。)下面的代码使用MACRO DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR允许尝试这种情况。

我的示例代码使用“ asm”表达式将注释插入到生成的汇编代码中。如果以汇编的形式检查编译器的输出,我希望在“ 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;
}


输出汇编代码:

##### 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。但是,这似乎是一个如此简单的优化,我很难相信这可能是编译器搞砸了。

任何帮助,见解或指导,我们将不胜感激。

最佳答案

很简单当直接将代码作为局部变量编写时,代码将内联,并且编译器将执行常量折叠。在类范围内时,代码不会内联,并且m_b_enabled的值未知,因此编译器必须执行调用。为了证明代码在语义上是相等的并执行此优化,不仅必须内联该调用,而且还必须对类进行每次访问。编译器很可能会决定对类进行内联不会产生足够的好处。编译器还可以选择不内联代码,因为他们不知道如何进行内联,内联asm表达式正是导致它们执行此操作的一种方式,因为编译器不知道如何处理您的汇编代码。

通常,您将放置一个断点并检查反汇编。无论如何,这就是我在Visual Studio中所做的。任何类型的内联汇编程序都可能对优化器造成损害。

当我删除汇编器表达式时,Visual Studio内联了代码,并立即没有执行优化。堆叠优化过程的问题在于,您永远无法获得找到所有潜在优化的正确顺序。

关于c++ - 局部变量与类变量的编译器优化;有效与无效,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6725611/

10-12 00:11
查看更多