C++中的无分支是什么样的?

MPe*_*ier 8 c++ branch if-statement

我意识到我在那个领域缺乏知识(花哨的说法,我不知道杰克).

是否有关于如何以及何时使用它们的文档?

Nec*_*lis 10

除了所有的摆弄基于网点代码(这不会面面俱到,如FP),你得到的指示专门意味着网点代码创建,这些将是SETcc,FCMOVccCMOVcc在x86下,基于从比较条件标志其执行操作.

一个非常简单的例子是(是的,这个例子非常简单,以至于人们可能永远不会写这样的东西,它只是为了清楚地表明一点):

bool CheckZero(int x)
{
    if(x == 0)
       return true;

    return false;
    //OR we can use: return (x == 0) ? true : false; 
}
Run Code Online (Sandbox Code Playgroud)

现在一个简单的x86编译器可能会将其编译为:

    MOV EAX,[ESP + 4]
    TEXT EAX,EAX
    JE _TRUE
    XOR EAX,EAX
    RETN 4

_TRUE:
    MOV EAX,1
    RETN 4
Run Code Online (Sandbox Code Playgroud)

优化的x86编译器会将其分解为以下无分支代码(或类似代码):

MOV ECX,[ESP + 4]
XOR EAX,EAX
TEST ECX,ECX
SETE AL
RETN 4
Run Code Online (Sandbox Code Playgroud)

这里可以找到一个更复杂的例子.

但是,这是编译器将执行的操作,而不是您应该担心的一些事情(至少在没有分析编译器输出的情况下).但是如果代码必须是无分支的,那么C++就无法提供足够的控制,所以你需要使用(内联)程序集.

  • @molbdnilo:只是因为表达式的返回类型是`bool`并不意味着它的评估将是无分支的(它不是一个常量表达式).你的混淆语言标准与机器级的实际实现(重要的地方),编译器可能会发出一个分支,基于`SETcc`的版本或基于twiddling的版本,取决于所应用的优化. (6认同)
  • 什么会有你写`return/*布尔条件*/?true:false;`而不是`return/*boolean condition*/;`?说真的,`return x == 0;`工作正常,甚至不需要优化编译器就是无分支的. (4认同)
  • @chris:为了清晰起见,我写了这一点,仅此而已.另外,技术上`return(x == 0);`不是自动无分支的,只是因为没有`if`或三元选择器,它取决于编译器的优化. (3认同)
  • 它是无分支的 - "x == 0"的值是一个`bool`,它刚刚从函数中返回,因此它已经是最优的. (2认同)

xak*_*p35 6

不久前我写了三元逻辑模拟器,这个问题对我来说是可行的,因为它直接影响我的解释器执行速度;我被要求尽可能快地模拟成吨和成吨的三元逻辑门。

在二进制编码-三进制系统中,一个 trit 被打包成两位。最高位表示负数,最低位表示正数。案例“11”不应该发生,但必须妥善处理并威胁为0。

考虑inline int bct_decoder( unsigned bctData )函数,它应该将格式化的 trit 返回为常规整数 -1、0 或 1;我观察到有 4 种方法:我称它们为“cond”、“mod”、“math”和“lut”;让我们调查他们

首先是基于 jz|jnz 和 jl|jb 条件跳转,因此 cond。它的性能一点也不好,因为依赖于分支预测器。更糟糕的是 - 它会有所不同,因为不知道是否会先验地有一个或两个分支。这是一个例子:

inline int bct_decoder_cond( unsigned bctData ) {
   unsigned lsB = bctData & 1;
   unsigned msB = bctData >> 1;
   return
       ( lsB == msB ) ? 0 : // most possible -> make zero fastest branch
       ( lsB > msB ) ? 1 : -1;
}
Run Code Online (Sandbox Code Playgroud)

这是最慢的版本,在最坏的情况下可能涉及 2 个分支,这是二进制逻辑失败的地方。在我的 3770k 上,它在随机数据上平均产生大约 200MIPS。(这里和之后 - 每个测试是随机填充的 2mb 数据集上 1000 次尝试的平均值)

下一个依赖于模运算符,其速度介于第一和第三之间,但绝对更快 - 600 MIPS:

inline int bct_decoder_mod( unsigned bctData ) {
    return ( int )( ( bctData + 1 ) % 3 ) - 1;
}
Run Code Online (Sandbox Code Playgroud)

下一个是无分支方法,它只涉及数学,因此是数学;它根本不假设跳转指令:

inline int bct_decoder_math( unsigned bctData ) {
    return ( int )( bctData & 1 ) - ( int )( bctData >> 1 );
}
Run Code Online (Sandbox Code Playgroud)

这做了应该做的事情,并且表现得非常好。相比之下,性能估计为 1000 MIPS,比分支版本快 5 倍。由于缺乏本机 2 位有符号整数支持,分支版本可能会变慢。但在我的应用程序中,它本身就是一个很好的版本。

如果这还不够,那么我们可以走得更远,有一些特别的东西。接下来称为查找表方法:

inline int bct_decoder_lut( unsigned bctData ) {
    static const int decoderLUT[] = { 0, 1, -1, 0 };
    return decoderLUT[ bctData & 0x3 ];
}
Run Code Online (Sandbox Code Playgroud)

在我的例子中,一个 trit 只占用了 2 位,所以 lut 表只有 2b*4 = 8 个字节,值得一试。它适合缓存并以 1400-1600 MIPS 的速度快速工作,这是我的测量精度下降的地方。这是快速数学方法的 1.5 倍加速。那是因为您只有预先计算的结果和单个AND指令。遗憾的是,缓存很小并且(如果您的索引长度大于几位)您根本无法使用它。

所以我想我回答了你的问题,关于分支/无分支代码会是什么样子。答案要好得多,并且有详细的示例、真实世界的应用程序和真实的性能测量结果。


soa*_*dos 5

http://hbfs.wordpress.com/2008/08/05/branchless-equivalents-of-simple-functions/

我认为(尽管我不知道比我在页面上读到的更多)这是一种在没有分支的情况下获取 if 功能的方法(基于无分支 if ;) 这个词是有道理的)。不知道更多。

谢谢谷歌先生。