performance - 条件指令(cmov)和跳转指令之间的区别

标签 performance assembly instructions mov

我很困惑在哪里使用 cmov说明和使用地点jump assembly 说明?

性能的角度来看:

  • 两者有什么区别?
  • 哪一个更好?

如果可能,请举例说明它们的区别。

最佳答案

movcc 是所谓的谓词指令。这是“该指令在条件(谓词)下执行”的奇特说法。

许多处理器,包括 x86,在执行算术运算(尤其是比较指令)后,会设置条件代码位来指示运算结果的状态。

条件跳转指令检查条件代码位的状态,如果为真,则跳转到指定目标。

由于跳转是有条件的,并且处理器通常具有很深的管道,因此当 CPU 遇到 jmp 指令时,条件代码位实际上可能尚未准备好供 jmp 指令处理。芯片设计人员可以简单地等待管道耗尽(通常是多个时钟周期),然后执行 jmp,但这会使处理器变慢。

相反,他们中的大多数人选择使用分支预测算法,该算法可以预测条件跳转的走向。然后,处理器可以获取、解码并执行(或不执行)预测的分支,并继续快速执行,但条件是如果最终到达的条件代码位被证明是错误的对于条件(分支错误预测),处理器会撤消分支后所做的所有工作,并沿着另一条路径重新执行程序。

条件跳转对于流水线执行来说比正常的数据依赖更困难,因为它们可以改变流经流水线的指令流中的下一个指令。这称为 control dependency ,而不是数据依赖(例如 add,其中两个输入都是其他最近指令的输出)。

分支预测器结果非常好,因为大多数分支往往对其方向有偏差。 (大多数循环末尾的分支通常会分支回顶部)。因此,大多数时候处理器不必退出错误预测的工作。

如果分支的方向高度不可预测,那么处理器将在大约 50% 的时间内猜测错误,从而不得不取消工作。太贵了。

好的,现在,人们经常会发现这样的代码:

  cmp   ...
  jcc   $
  mov   register1, register2
$: ; continue here
  ...
  ; use register1

如果分支预测器猜对了,那么无论分支走哪条路,这段代码都会很快。如果它猜错很多...哎呀。

因此是条件移动指令。这是根据条件代码位有条件地移动数据的移动。我们可以重写上面的内容:

  cmp   ...
  movcc  register1, register2
$: ; continue here
  ...
  ; use register1

现在我们没有分支指令,因此不会出现使处理器撤消所有工作的错误预测。由于不存在控制依赖性,因此无论 movcc 的行为是否类似于 movnop,都需要获取并解码以下指令。管道可以保持满载,而无需预测条件并推测性地执行使用 register1 的指令。 (您可以通过这种方式构建 CPU,但这会违背 movcc 的目的。)

movcc 将控制依赖项转换为数据依赖项。 CPU 将其视为 3 输入数学指令,输入为 EFLAGS 及其两个“常规”输入(目标寄存器和源寄存器或内存)。在 x86 上,就乱序执行如何跟踪依赖关系而言,adccmovae (mov if CF==0) 相同:输入为 CF 和 GP 寄存器。输出是目标寄存器。

对于 x86,有 cmovcc , jcc ,和setcc每个条件组合的说明 cc。 (setcc 根据条件将目标设置为 0 或 1。因此它具有对标志的数据依赖性,并且没有其他输入依赖性。)

关于performance - 条件指令(cmov)和跳转指令之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26154488/

相关文章:

assembly - `teSTL` eax 与 eax?

c# - 为什么局部变量引用会导致性能大幅下降?

php - 在 CodeIgniter 中,加载不必要的库、帮助程序等是否会带来性能劣势?

memory - 为什么加载不能绕过写入缓冲区中同一内核上的另一个线程写入的值?

optimization - Gnu 汇编程序 (GAS) 优化

c++ - 使用rdtsc计算系统时间

assembly - x64 MOV 32位立即数到64位寄存器

asp.net-mvc - LINQ to SQL 在压力测试时抛出异常

Javascript - 在函数的外部范围内访问变量有多昂贵?

c - 了解程序集中的条件代码标志设置