我很困惑在哪里使用 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
的行为是否类似于 mov
或 nop
,都需要获取并解码以下指令。管道可以保持满载,而无需预测条件并推测性地执行使用 register1
的指令。 (您可以通过这种方式构建 CPU,但这会违背 movcc
的目的。)
movcc
将控制依赖项转换为数据依赖项。 CPU 将其视为 3 输入数学指令,输入为 EFLAGS 及其两个“常规”输入(目标寄存器和源寄存器或内存)。在 x86 上,就乱序执行如何跟踪依赖关系而言,adc
与 cmovae
(mov if CF==0
) 相同:输入为 CF 和 GP 寄存器。输出是目标寄存器。
对于 x86,有 cmovcc
, jcc
,和setcc
每个条件组合的说明 cc。 (setcc
根据条件将目标设置为 0 或 1。因此它具有对标志的数据依赖性,并且没有其他输入依赖性。)
关于performance - 条件指令(cmov)和跳转指令之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26154488/