7

我试图在基于 Cortex-M 的微控制器上将值限制在 -127 和 127 之间。

我有两个相互竞争的函数,一个使用条件,另一个使用我在这里找到的无分支 hack 。

// 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 级管道、获取、解码和执行,它似乎有某种分支预测器,但我无法挖掘细节。

4

3 回答 3

4

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

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 的条件执行设计,两者都是无分支的。我敢打赌,它们在性能上基本上是可比的。

于 2013-02-17T01:59:11.663 回答
3

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

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

条件执行

  • 大多数指令集只允许有条件地执行分支。
  • 然而,通过重用条件评估硬件,ARM 有效地增加了指令数量。
  • 所有指令都包含一个条件字段,用于确定 CPU 是否会执行它们。
  • 未执行的指令占用 1 个周期。– 仍然必须完成循环,以允许获取和解码以下指令。
  • 这消除了对许多分支的需要,这些分支会使管道停止(重新填充 3 个周期)。
  • 允许非常密集的内联代码,没有分支。
  • 不执行多条条件指令的时间损失通常小于分支或子程序调用的开销,否则将需要这些开销。
于 2013-02-17T01:59:58.117 回答
0

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

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

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

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

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

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

于 2013-02-17T04:49:23.147 回答