我有一个代码示例,其中将直接优化结构构造为类变量时不起作用,但作为局部变量起作用;我想知道:为什么优化没有在类变量公式上进行?
我的示例代码的目的是要有一个在构造时启用或禁用的类,并可能在其生命周期内对其进行更改。我希望,当对象在整个生命周期中都被禁用时,编译器会优化掉启用该对象时有条件执行的所有代码。
具体来说,我有一个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/