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

c++ attempt to optimize code by replacing tests

本文关键字:测试 优化 代码 替换 C++      更新时间:2023-10-16

>我正在查看其他人编写的代码,它有很多调试部分,类型

if(0) { code }

if(1) { code }

if(false) { code }

甚至还有

#if(0)
#endif

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

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

我认为这可能会有所帮助,因为我已经看到这些部分中的代码显示为灰色 - 并且我认为此代码已从发布可执行文件中删除......因此使其更快。这是真的吗?

我正在考虑的代码位于可以调用很多次的函数中......

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

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

我的问题是,我正在添加数百万次测试if(0)不增加开销的事实......我试图弄清楚是什么可以使这段代码花费更少的时间。

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

当然,如果您在调试模式下编译,和/或将优化级别设置为零,那么编译器可能会跳过它并离开这些测试 - 但对于纯零/一/真/假值,这是极不可能的。

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

它甚至能够删除看起来复杂的情况,例如:

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 是未知的。

因此,如果可能,请使用预处理器。在微不足道的情况下,这不会做太多事情,因为编译器已经知道这一点。但是案例并不总是微不足道的,您会经常注意到这种变化。当优化器无法推断值时,即使代码已失效,它也会离开代码。另一方面,预处理器可以保证在编译和优化禁用的部分之前删除它们。此外,使用预处理器来做到这一点至少会加快编译速度:编译器/优化器不必跟踪常量/计算/检查分支等。

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

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

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

如果ifdef代码,则编译器将完全忽略它。代码可以完全是无意义的,包含语法错误,或者其他什么,编译器不会关心。

通常,#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,看看结果是否相同。如果它是一样的,那么你必须看看自上次通过以来还有什么变化。如果 slower_function 没问题,你回去看看为什么 fast_function() 在这种情况下不能正常工作。

这是

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

将调试代码放在#ifdef _DEBUG(或类似)中是将这些代码完全排除在发布版本中的标准方法。通常调试版本#define它,而发布版本则不然。

但是,通常编译器也应该删除诸如if (0)之类的代码,如果给定适当的优化标志,但这会给编译器和程序员带来额外的工作(现在您必须全部更改它们!我肯定会把这个留给预处理器。

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

只要确保在发布时#define DEBUG 0后发布代码即可。

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