C++优化如果性能

C++ optimization if performance

本文关键字:性能 如果 优化 C++      更新时间:2023-10-16

考虑if语句的这两种情况:

if( error ==0 )
{
    // DO success stuff
}
else
{
    // DO error handling stuff
}

这个:

if( error != 0 )
{
    // DO error handling stuff
}
else
{
    // DO success stuff
}

哪一个执行得比另一个好,因为我知道大多数时候我都会走成功代码的道路。

与其担心这可能只是在极少数情况下的性能问题,不如问问自己哪一个更可读。对于错误检查,您可以使用guard子句,它可以避免过多的缩进/括号:

if( error != 0 )
{
    // DO error handling stuff
    return;
}
// DO success stuff

如果你知道一条路径比另一条路径更有可能,并且你确信这确实是性能关键,你可以让编译器知道(例如GCC):

if( _builtin_expect (error == 0, 1) )
{
    // DO success stuff
}
else
{
    // DO error handling stuff
}

当然,这会使代码更难阅读——只有在真正必要的时候才使用它。

这取决于情况

当代码只运行一次时,从统计数据来看,一开始使用更可能的代码会更快——如果并且仅当分支预测的cpu实现不是在多行之间"共享计数器"(例如,每个第16条语句共享相同的计数器)。然而,大多数代码并不是这样运行的。它将运行数万亿次(例如在while循环中)。

多次跑步

没有人会比其他人表现得更好。原因是分支预测。每次程序运行if语句时,cpu都会向上或向下计数该语句为true的次数。这样,如果代码再次运行,它现在可以高精度地预测下一次。如果你测试你的代码十亿次,你会发现如果你的If或else部分被执行了,那也没关系。CPU将针对其认为最有可能发生的情况进行优化。

这是一个简化的解释,因为CPU分支预测足够聪明,也可以看到某些代码何时总是触发器:真、假、真、假,真、假甚至真、真、假的、真、真。。。

你可以在维基百科上了解到很多关于分支预测的文章

Gcc的默认行为是针对if语句的真实情况进行优化。在此基础上,它将选择应该使用jejne

如果您知道并希望精细控制哪个调用路径更有可能,请使用以下宏查找控件。

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

它们的性能相同。您只是在用一条指令jz交换一条从Assembly级别观察的jnz。它们中没有一个比另一个执行更多或更复杂的指令。

您不太可能注意到这两段代码之间的差异。与0进行比较是相同的操作,无论代码后来是"true"还是"false"都会跳转。因此,使用最能在代码中表达"含义"的形式,而不是试图"智胜编译器"(除非你真的很擅长,否则你可能只会混淆事情。

由于在这两种情况下都有if ... else ...(大多数编译器都会生成一个返回点,因此即使在函数中间有return,它仍然会生成从return到函数底部的分支,如果条件为false,则生成一个分支以跳过它

解决这一问题的唯一真正有益的方法是使用分支被/未被采用的提示,这至少在一些处理器上是有益的(编译器可以扭转分支,这样不太可能的情况会产生最多的分支)。但它也相当不可移植,因为C和C++语言没有任何功能允许向编译器提供这样的反馈。但有些编译器确实实现了这样的东西。

当然,这样做的效果/结果在很大程度上取决于实际的处理器是什么(如果这个特定的分支没有"历史记录",现代x86会提示处理器输入分支预测单元——一些嵌入式系统等中使用的旧x86不会有。其他处理器可能有也可能没有相同的功能——我相信ARM有几个方面可以说"这很可能被接受/没有被接受")。理想情况下,为此,您需要"配置文件驱动的优化",这样编译器就可以根据最可能的变体来检测和组织代码。

始终使用评测和基准测试来衡量任何优化的结果。只看代码通常很难猜测什么更好(如果你看不到编译器生成的机器代码,情况更糟)。

任何编译器都应该优化差异。证据如下。如果在运行时设置了错误,那么。

g++4.8与-O3 配合使用

这个

int main(int argc, char **argv) {
    bool error=argv[1];
    if( error ){
        return 0;
    }else{
        return 1;
    }
}

使。

main:
    xorl    %eax, %eax
    cmpq    $0, 8(%rsi)
    setne   %al
    ret

而这个。。。

int main(int argc, char **argv) {
    bool error=argv[1];
    if( !error ){
        return 1;
    }else{
        return 0;
    }
}

使。。。

main:
    xorl    %eax, %eax
    cmpq    $0, 8(%rsi)
    setne   %al
    ret

与CPU相同的东西。如有疑问,请使用机器代码。http://gcc.godbolt.org/

相关文章: