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

时间:2011-05-26 03:18:59

标签: c++ if-statement branch

我意识到我在那个领域缺乏知识(我不知道杰克有什么想法)。

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

3 个答案:

答案 0 :(得分:11)

除了所有基于twiddling的无分支代码(不会覆盖所有内容,例如FP)之外,您还可以获得专门用于无分支代码创建的指令,这些指令可以是SETccFCMOVcc和{ x86下的{1}},它根据比较中的条件标志执行操作。

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

CMOVcc

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

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

    return false;
    //OR we can use: return (x == 0) ? true : false; 
}

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

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

_TRUE:
    MOV EAX,1
    RETN 4

可以找到一个更复杂的例子here

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

答案 1 :(得分:4)

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

我认为(虽然我不知道比我在页面上看到的更多)但它是一种获得没有分支的功能的方法(根据无分支的话,这是有意义的;))。不知道更多。

感谢谷歌先生。

答案 2 :(得分:1)

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

在二进制编码三元系统中,一个trit以两位打包。最重要的比特意味着消极而最不重要意味着正数。案例" 11"不应该发生,但必须妥善处理并威胁为0。

考虑inline int bct_decoder( unsigned bctData )函数,它应该将格式化的trit作为常规整数-1,0或1返回;正如我观察到的那样有4种方法:我称之为" cond"," mod"," math"和" lut&#34 ;;让我们调查一下

首先是基于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;
}

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

下一个依赖于modulo运算符,它的速度介于第一和第三之间,但速度更快 - 600 MIPS:

inline int bct_decoder_mod( unsigned bctData ) {
    return ( int )( ( bctData + 1 ) % 3 ) - 1;
}

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

inline int bct_decoder_math( unsigned bctData ) {
    return ( int )( bctData & 1 ) - ( int )( bctData >> 1 );
}

这应该是应有的,并且表现得非常好。相比之下,性能估计为1000 MIPS,比分支版本快5倍。由于缺少本机2位signed int支持,可能分支版本的速度变慢。但在我的应用程序中,它本身就是非常好的版本。

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

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

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

所以我想我回答了你的问题,关于什么可以分支/无分支代码。答案要好得多,有详细的样本,实际应用和真实的性能测量结果。