C 这种无分支的黑客真的更快吗?

C 这种无分支的黑客真的更快吗?,c,C,我试图在基于Cortex-m的计算机上钳制一个介于-127和127之间的值 我有两个相互竞争的函数,一个使用条件句,另一个使用我发现的无分支hack //使用条件语句 int-clamp(int-val){return((val>127)?127:(val>31; val+=-127; val-=127; val&=val>>31; val+=127; 返回val; } 现在我知道,在某些情况下,这些方法中的一种可能比另一种更快,反之亦然,但一般来说,使用无分支技术是否值得,因为我使用哪种方法

我试图在基于Cortex-m的计算机上钳制一个介于-127和127之间的值

我有两个相互竞争的函数,一个使用条件句,另一个使用我发现的无分支hack

//使用条件语句
int-clamp(int-val){return((val>127)?127:(val<-127)?-127:val);}
//使用无分支黑客
内夹(内夹){
val-=-127;
val&=(~val)>>31;
val+=-127;
val-=127;
val&=val>>31;
val+=127;
返回val;
}
现在我知道,在某些情况下,这些方法中的一种可能比另一种更快,反之亦然,但一般来说,使用无分支技术是否值得,因为我使用哪种方法对我来说并不重要,它们在我的情况下都会很好地工作

关于微控制器的一点背景知识,它是一个基于ARM的微控制器,运行速度为90 MIPS,具有3级管道,获取、解码和执行,似乎有一些分支预测器,但我无法挖掘细节

ARM代码(GCC 4.6.3中带有
-O3
):

拇指代码:

clamp1:
    mvn r3, #126
    cmp r0, r3
    it  lt
    movlt   r0, r3
    cmp r0, #127
    it  ge
    movge   r0, #127
    bx  lr

clamp2:
    adds    r0, r0, #127
    mvns    r3, r0
    and r0, r0, r3, asr #31
    subs    r0, r0, #254
    and r0, r0, r0, asr #31
    adds    r0, r0, #127
    bx  lr

由于ARM的条件执行设计,两者都是无分支的。我敢打赌,它们在性能上基本相当。

需要了解的是,在分支指令方面,ARM和x86体系结构是非常不同的。跳转清除了管道,这可能导致大量时钟周期的加速,只是为了在吞吐量方面“回到原来的位置”

引用我前几天下载的pdf(第14页,共页)

有条件执行

  • 大多数指令集只允许有条件地执行分支
  • 然而,通过重用条件评估硬件,ARM有效地增加了指令数量
  • 所有指令都包含一个条件字段,该字段确定CPU是否将执行这些指令
  • 未执行指令占用1个周期。 –仍需完成循环,以便获取和解码以下指令
  • 这消除了对许多分支的需要,这些分支会使管道停止运行(需要3个循环才能重新加注)
  • 允许非常密集的内联代码,没有分支
  • 不执行多条条件指令的时间代价通常小于否则需要的分支或子例程调用的开销

  • 不,C语言没有速度;这是C语言实现中引入的一个概念。一个完美的最佳编译器会将这两个概念翻译成相同的机器代码

    C编译器更有可能优化符合常见样式且定义良好的代码。第二个函数没有很好地定义

    这些加法和减法可能导致整数溢出。整数溢出是未定义的行为,因此它们可能导致程序故障。乐观地说,您的硬件可能实现包装或饱和。稍不乐观的是,您的操作系统或编译器可能会为整数溢出实现信号或陷阱表示。检测整数溢出可能会影响修改变量的感知性能。最坏的情况是程序失去了完整性

    &和>>运算符具有签名类型的实现定义方面。它们可能导致负零,这是陷阱表示的一个示例。使用陷阱表示是未定义的行为,因此程序可能会失去完整性

    也许您的操作系统或编译器实现了int对象的奇偶校验位检查。在这种情况下,试着想象每次变量更改时重新计算奇偶校验位,每次读取变量时验证奇偶校验位。如果奇偶校验失败,程序可能会失去完整性


    使用第一个函数。至少定义得很清楚。如果您的程序运行缓慢,优化此代码可能不会显著提高程序的速度;使用探查器查找更重要的优化,使用更优化的操作系统或编译器,或购买更快的硬件。

    您是否对其进行了基准测试?编译代码时,优化似乎已停止。您关心ARM的性能,但您正在查看生成的x86 ASM。没错,不幸的是,据我所知,我必须使用的ARM IDE无法提供实际的汇编。它叫RobotC,霍布斯说得很好。尝试使用ARM编译器编译(除非您使用VS for ARM,否则我听说它可能受支持?)。还可以尝试几个不同的优化级别(例如最快和最小),然后开始比较生成的程序集。您还可以尝试比较简单的if(value>127)返回127;否则如果(值<-127)返回-127;否则返回值;看看这有什么用。您可能会发现这是最小的(我意识到这应该与您编写的三元组相同或非常相似)。但是,有条件的执行可以消除很多短跳转。非常正确,谢谢你做了比我更好的总结工作。你说得比我简洁得多竖起大拇指:我目前没有臂板,但我很想看看这四个版本之间的性能差异。(我的钱在手臂上,但是差别会非常小)。
    clamp1:
        mvn r3, #126
        cmp r0, r3
        movlt   r0, r3
        cmp r0, #127
        movge   r0, #127
        bx  lr
    
    clamp2:
        add r0, r0, #127
        mvn r3, r0
        and r0, r0, r3, asr #31
        sub r0, r0, #254
        and r0, r0, r0, asr #31
        add r0, r0, #127
        bx  lr
    
    clamp1:
        mvn r3, #126
        cmp r0, r3
        it  lt
        movlt   r0, r3
        cmp r0, #127
        it  ge
        movge   r0, #127
        bx  lr
    
    clamp2:
        adds    r0, r0, #127
        mvns    r3, r0
        and r0, r0, r3, asr #31
        subs    r0, r0, #254
        and r0, r0, r0, asr #31
        adds    r0, r0, #127
        bx  lr