c ++尝试通过替换测试来优化代码

时间:2013-02-02 01:20:01

标签: c++ optimization

我正在查看其他人编写的代码,它有很多类型为

的调试部分
if(0) { code }

if(1) { code }

if(false) { code }

甚至还有

#if(0)
#endif

(虽然它没有变成灰色 - 我认为应该这样)

我想知道,如果我用一些#if 0(或#ifdef _DEBUG)替换它们,是否可以优化代码? - 或 - 它没有任何区别?

我认为它可能有所帮助,因为我看到这些部分中的代码变灰了 - 而且我认为此代码已从Release可执行文件中删除...因此使其更快。这是真的吗?

我想到的代码是可以多次调用的函数内部......

编辑:我所指的代码正在运行数百万次。我知道if(0)的内容将被忽略......

我也意识到通过将测试从0切换到1来轻松调试问题的好处......

我的问题是,我添加数百万次测试的事实如果(0)不增加开销......我试图找出可以使这段代码花费更少时间的所有事情。

5 个答案:

答案 0 :(得分:2)

如果置于这些IF中的表达式在编译时是常量且可确定的,那么您可能几乎可以确定编译器已经为您删除了代码。

当然,如果你在Debug-Mode中编译,和/或你的优化级别设置为零,那么编译器可能会跳过它并离开那些测试 - 但是使用普通的零/一/真/假值它非常不可能。

对于编译时常量分支,您可以确定编译器删除了死的分支。

它甚至可以删除复杂外观,例如:

const int x = 5;

if( 3 * x * x < 10 ) // ~ 75 < 10
{
    doBlah(); // skipped
}

但是,如果没有X上的'const'标记,表达式的值可能无法在编译时确定,并且可能会“泄漏”到实际的最终产品中。

此外,以下代码中的表达式值不一定是编译时常量:

const int x = aFunction();

if( 3 * x * x < 10 ) // ~ 75 < 10
{
    doBlah(); // skipped
}

X是一个常量,但它是用函数的值初始化的。 X很可能在编译时无法确定。在运行时,函数可以返回任何值*),因此编译器必须假定X是未知的。

因此,如果您有可能,请使用预处理器。在无关紧要的情况下,因为编译器已经知道了。但案件并非总是微不足道,你会经常注意到这种变化。当优化器无法推导出值时,它会离开代码,即使它已经死了。另一方面,预处理器可以在编译和优化之前删除已禁用的部分。此外,使用预处理器至少可以加快编译速度:编译器/优化器不需要traceconstants / calculate / checkbranches等。

*)可以编写一个方法/函数,在编译和优化阶段实际上可以确定返回值:如果函数很简单并且内联,它的结果值可能会与一些分支一起优化..但即使你可以在某种程度上依赖于删除if-0子句,你也不能依赖于内联..

答案 1 :(得分:2)

如果您在if (0)块中有代码,则编译器生成的代码将与在任何合理编译器上不存在该块的代码相同。仍将检查代码是否存在编译时错误。 (假设你里面没有任何跳转标签或类似的东西。)

如果你在if (1)块中有代码,编译器生成的代码将与代码只是在大括号内的代码相同。这是为代码块提供自己的范围的常用方法,以便在需要时破坏局部变量。

如果您ifdef输出代码,则编译器会完全忽略它。代码可以是完全无意义的,包含语法错误,或者编译器不关心的任何内容。

答案 2 :(得分:2)

通常情况下,#if 0用于删除代码,同时仍然保留代码 - 例如,为了方便地与选项进行比较,我有时会这样做:

#if 1
   some sort of code 
#else
   some other code
#endif

这样,我可以快速切换两种选择。

在这种情况下,预处理器只会在代码中保留两个选项之一。

if(0)if(1)的构造类似 - 编译器几乎会删除if,而在0的情况下也会删除if语句的其余部分。

我认为将这类内容留在“已完成”的代码中相当草率,但它对于调试/开发非常有用。

比如说,你正在尝试一种新方法来做更快的事情:

if (1) 
{
   fast_function();
}
else
{
    slower_function();
}

现在,在您的一个测试用例中,结果显示错误。因此,您希望快速返回slower_funcion并查看结果是否相同。如果它是相同的,那么你必须看看自上次通过以来还有哪些变化。如果使用slow_function就可以了,那么你回过头来看看为什么fast_function()在这种情况下不能正常工作。

答案 3 :(得分:1)

这是真的(取决于你的构建设置和预处理器)。

将调试代码放在#ifdef _DEBUG(或类似)中是一种标准方法,可以将这些完全保留在发布版本之外。通常是调试构建#define,而版本构建则没有。

但是,通常情况下,如果给出了正确的优化标志,编译器也应该删除if (0)之类的代码,但这会给编译器和程序员带来额外的工作(现在你必须改变它们!)。我肯定会把它留给预处理器。

答案 4 :(得分:1)

你是对的。如果使用#define DEBUG 0进行编译,那么实际上您将在编译时删除所有#if DEBUG块。因此,代码将会少得多,而且运行速度会更快。

请确保在发布时{。}}后发布代码。

一个好的优化编译器(GCC,MSVC)将完全从代码中删除#define DEBUG 0if(0) ...机器代码的转换将不会测试这些条件......