我很困惑在哪里使用cmov
指令,在哪里使用jump
汇编指令?
从性能的角度来看:
- 它们两者有什么区别?
- 哪一个更好?
如果可能,请举例说明它们的区别。
movcc 是一个所谓的谓词指令。 这是"此指令在条件(谓词(下执行"的花哨说法。
许多处理器(包括 x86(在执行算术运算(尤其是比较指令(后,设置条件代码位以指示运算结果的状态。
条件跳转指令检查条件代码位的状态,如果为 true,则跳转到指定目标。
由于跳转是有条件的,并且处理器通常具有深层管道,因此当 CPU 遇到 jmp 指令时,条件代码位可能实际上尚未准备好供 jmp 指令处理。 芯片设计人员可以简单地等待流水线耗尽(通常是许多时钟周期(,然后执行jmp,但这会使处理器变慢。
相反,他们中的大多数人选择使用分支预测算法,该算法预测条件跳转将走向何方。 然后,处理器可以获取、解码和执行预测的分支(或不执行(,并继续快速执行,条件是,如果最终到达的条件代码位被证明是错误的条件(分支错误预测(,处理器将撤消它在分支之后所做的所有工作,并沿着另一条路径重新执行程序。
条件跳转对于流水线执行比普通数据依赖关系更难,因为它们可以更改流经流的指令流中的下一个指令。 这称为控件依赖关系,而不是数据依赖关系(如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及其两个"常规"输入(dest寄存器和源寄存器或存储器(。 在 x86 上,就无序执行跟踪依赖关系的方式而言,adc
与 cmovae
(如果CF==0
为 mov (相同:输入是 CF,并且都是 GP 寄存器。 输出是目标寄存器。
对于 x86,每个条件组合 cc 都有cmovcc
、jcc
和setcc
指令。 ( setcc
根据条件将目标设置为 0 或 1。 因此,它对标志具有数据依赖性,并且没有其他输入依赖关系。