局部变量与类变量编译器优化;工作与不工作

时间:2011-07-17 17:45:31

标签: c++ class optimization variables local

我有一个代码示例,当结构化为类变量时,直接优化不起作用,但作为局部变量起作用;我想知道:为什么优化不会发生在类变量公式上?

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

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

我发现当我把它表示为一个类时,我没有得到我期望的优化。但是,如果我将代码全部复制为局部变量,我确实看到了预期的行为。

此外,我发现如果我不在示例类的方法体中的任何地方进行std :: ofstream调用,如'open','exceptions'或'clear',我也会得到预期的优化。 (但是,我的设计需要在std :: ofstream上进行这样的调用,所以对我来说这是一个没有实际意义的点。)下面的代码使用MACRO DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR来允许我们尝试这种情况。

我的示例代码使用'asm'表达式将注释插入到生成的汇编代码中。如果在程序集中检查编译器的输出,我希望在'disabled-test'注释之间没有汇编。我正在观察'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;
}

输出汇编代码:

##### 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。然而,这似乎是一个简单的优化,我发现很难相信这可能是编译器弄乱了。

非常感谢任何帮助,见解或指导。

2 个答案:

答案 0 :(得分:5)

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

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

当我删除汇编程序表达式时,Visual Studio会内联代码 - 并且无论如何都不会立即执行优化。堆叠优化过程的问题在于,您永远无法获得正确的顺序来查找所有可能的优化。

答案 1 :(得分:0)

正如你所说,这将取决于编译器。但我的猜测是:

优化器可以证明没有其他代码可以修改对象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。我们知道它不应该,但理论上非内联库函数可以找出包含其'this'参数的完整对象test_ofstream,并以这种方式修改它。