cmov条件传送指令
-
指令介绍
cmovx S, D
- 条件传送指令集每条指令都有两个操作数:源寄存器或者内存地址S,和目的寄存器R,这些指令的结果取决于条件码的值。源值可以从内存或者源寄存器中读取,但是只有在指定的条件满足时,才会被复制到目的寄存器中。
- 源和目的的值可以是16位、32位或64位长,不支持单字节的条件传送,汇编器可以从目标寄存器地名字推断出条件传送指令的操作数长度。(这点与无条件的指令有差别,因为无条件指令的操作数显式地编码在指令名中,如movw、movl)
-
Example
cmp %rsi, %rdi cmova %rdx, %rax
这个代码片段的含义为:
如果RDI寄存器中的值大于RSI寄存器中的值,则把RAX寄存器中的值替换为RDX寄存器中的值。
-
为什么使用条件传送指令会提高性能?
当机器遇到条件跳转指令的时候,一般都会使用分支预测方法来预测哪个分支会被执行,从而使流水线充满指令,提高效率;但是分支预测不会总是正确的(特别是对于一些诸如x<y不可预测的条件),如果错误预测的话,就需要处理器丢掉之前的工作,从正确位置处的指令开始去填充流水线,这就会浪费很多时钟周期,导致程序性能下降。
而使用条件传送指令,就可以取消“处理器分支预测”这一步,减少控制冒险。
可以配合下面这个例子进行理解:
-
条件传送指令不会提高性能的情况
不过因为条件传送指令需要一开始就将两种情况的结果计算出来,当不需要被执行的分支计算量很大时,这样做显然是不划算的。
在《深入理解计算机系统》这本书中,提到:
我们对GCC的实验表明,只有当两个表达式都很容易计算时,例如表达式分别都只是一条加法指令,它才会使用条件传送。根据我们的经验,即使许多分支预测错误的开销会超过更复杂的计算,GCC还是会使用条件控制转移。
我认为这就可能是老师所说的“GCC优化保守”的原因。甚至对于那些有着高分支预测错误率的benchmark(如541leela_r、505mcf_r、557xz_r)如果能更多地使用条件传送指令,可能会进一步提高性能(这只是我的一个猜想,具体还是要根据这些benchmark的源码来下定论)。
-
不适用条件传送指令的情况
由于条件传送指令需要将两种情况的结果计算出来,这就有可能会导致问题。如:
int cread(int* xp) { return (xp? *xp: 0); }
使用条件传送指令时,会分别将“*xp”和“0”放入寄存器,如果xp为NULL时,就会出现空指针引用错误。因此在做这方面优化的时候,一定要注意,两种情况的任意一个都不能出现错误或者副作用。
-
补充:如何确定分支预测错误的处罚