This question already has answers here:
Why is a conditional move not vulnerable for Branch Prediction Failure?
(5个答案)
3年前关闭。
我很困惑在汇编中在哪里使用
从性能角度来看:
两者有什么区别?
哪一个更好?
如果可能,请举例说明它们的区别。
如果分支预测器猜对了,那么无论分支走到哪条路,此代码都很快。如果它猜错很多...哎呀。
因此条件移动指令。此操作基于条件代码位有条件地移动数据。我们可以重写上面的内容:
现在我们没有分支指令,因此也没有使处理器撤消所有工作的错误预测。由于不存在控制依赖性,因此无论
对于x86,每个条件组合cc都有
(5个答案)
3年前关闭。
我很困惑在汇编中在哪里使用
cmov
指令以及在哪里使用jump
指令?从性能角度来看:
两者有什么区别?
哪一个更好?
如果可能,请举例说明它们的区别。
最佳答案
movcc是所谓的谓词指令。这就是“该指令在条件(谓词)下执行”的幻想。
在进行算术运算(尤其是比较指令)之后,包括x86在内的许多处理器都将条件代码位设置为指示运算结果的状态。
条件跳转指令检查条件码位的状态,如果为真,则跳转到指定的目标。
因为跳转是有条件的,并且处理器通常具有较深的流水线,所以当CPU遇到jmp指令时,条件码位可能实际上并未准备好让jmp指令处理。芯片设计人员可以简单地等待流水线耗尽(通常需要很多时钟周期),然后执行jmp,但这会使处理器变慢。
取而代之的是,大多数人选择使用分支预测算法,该算法可以预测条件跳转的方向。处理器然后可以提取,解码和执行(或不执行)预测的分支,并继续快速执行,前提条件是如果最终到达的条件代码位对于条件而言是错误的(分支错误预测),则处理器撤消分支之后所做的所有工作,然后重新执行沿另一条路径运行的程序。
有条件的跳转比常规的数据依赖性更难于流水线执行,因为条件跳转可以改变流水线中的指令流中的下一条指令。与数据依赖关系(例如add
,其中两个输入都是其他最近指令的输出)相比,这称为control dependency。
分支预测器的结果非常好,因为大多数分支趋向于对其方向产生偏见。 (通常,大多数循环末尾的分支将分支回到顶部)。因此,大多数时候,处理器不必退出错误预测的工作。
如果分支的方向高度不可预测,则处理器将在大约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
相同(如果为CF==0
,则为cmovcc
相同):输入为CF,两个GP寄存器。输出是目标寄存器。对于x86,每个条件组合cc都有
jcc
,setcc
和setcc
指令。 (根据条件将目标设置为0或1。因此,它对标志有数据依赖性,而没有其他输入依赖性。)