C这个无分支的黑客实际上更快吗?

时间:2013-02-17 01:44:06

标签: c

我正试图在基于Cortex-M的microcontroller上将-127和127之间的值钳位。

我有两个竞争功能,一个使用条件,另一个使用无网格黑客我发现here

// Using conditional statements
int clamp(int val) { return ((val > 127) ? 127 : (val < -127) ? -127 : val); }

// Using branchless hacks
int clamp(int val) {
    val -= -127;
    val &= (~val) >> 31;
    val += -127;
    val -= 127;
    val &= val >> 31;
    val += 127;

    return val;
}

现在我知道在某些情况下,这些方法中的一种可能比另一种方法更快,反之亦然,但总的来说使用无分支技术是值得的,因为它对我来说并不重要,我使用的,在我的情况下,它们都能正常工作吗?

微控制器的一个小背景,它是一个基于ARM的微控制器,运行速度为90 MIPS,具有3级流水线,取指,解码和执行,它似乎有某种分支预测器,但我无法挖掘细节。

3 个答案:

答案 0 :(得分:4)

ARM代码(带-O3的GCC 4.6.3):

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

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

答案 1 :(得分:3)

要实现的是ARM和x86架构在分支指令方面有很大不同。跳转可以清除管道,这可以导致多个时钟周期的流失,只是为了“回到原来的位置”。

引用我前几天下载的pdf(http://simplemachines.it/doc/arm_inst.pdf的第14页),

有条件执行

  • 大多数指令集只允许有条件地执行分支。
  • 但是,通过重用条件评估硬件,ARM可以有效地增加指令数量。
  • 所有指令都包含一个条件字段,用于确定CPU是否执行它们。
  • 未执行的指令吸收1个周期。 - 仍然必须完成循环,以便允许获取和解码以下指令。
  • 这消除了对许多分支的需要,这些分支使管道停顿(3个循环以重新填充)。
  • 允许非常密集的内嵌代码,不带分支。
  • 不执行多个条件指令的时间损失通常小于原本需要的分支或子程序调用的开销。

答案 2 :(得分:0)

没有。 C语言没有速度;这是由C的实现引入的概念。完美的最佳编译器会将这两者转换为相同的机器代码。

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

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

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

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

使用第一个功能。至少它定义得很好。如果您的程序运行缓慢,优化此代码可能不会显着加快您的程序速度;使用分析器查找更重要的优化,使用更优化的操作系统或编译器或购买更快的硬件。