Closed wanghenshui closed 2 months ago
cmov在哪些场景下不如jmp
cmov
(条件移动)指令和jmp
(跳转)指令在处理器中服务于不同的目的,各有其优势和适用场景。cmov
是一种条件执行指令,用于在满足特定条件时将一个值移动到寄存器中,而不需要执行跳转,从而避免分支预测失败带来的性能损失。jmp
是一种控制流指令,用于无条件或条件性地改变程序的执行流程。在某些场景下,cmov
不如jmp
有效,主要包括以下几点:
cmov
指令的效率可能会因为强数据依赖性而降低。如果cmov
操作的目标值依赖于前面指令的结果,这可能会导致执行管线的延迟。在这种情况下,使用jmp
来实现分支可能更高效,因为它允许处理器更早地进行分支预测和执行流程的优化。jmp
进行分支可能比cmov
更高效。因为在这种情况下,分支预测失败带来的开销相对较小,而cmov
可能不会带来显著的性能提升。jmp
实现的分支可能会非常高效。现代处理器具有高度优化的分支预测器,能够在大多数情况下准确预测分支方向,减少因分支预测失败而导致的性能损失。jmp
(或高级语言中的相应控制流结构)可以使得代码的逻辑更加清晰和易于理解。在复杂的逻辑条件下,保持代码的可读性和维护性可能比微优化执行性能更为重要。cmov
指令适用于执行路径中有条件性数据移动且不希望引入分支预测失败开销的场景。然而,当存在强数据依赖、代码块非常小、分支预测准确性高或需要优化代码可读性和维护性时,使用jmp
指令实现的分支可能更为合适。在实际开发中,最佳选择取决于具体的应用场景、性能需求以及维护考虑。
https://llvm.org/docs/SpeculativeLoadHardening.html
Speculative问题会影响cmov速度吗
是的,speculative execution(推测执行)问题可能会影响cmov
(条件移动指令)的执行速度,以及其他类型指令的执行性能。推测执行是现代处理器用来提高执行速度的一种技术,它允许CPU在等待分支指令最终解析的同时,预先执行接下来可能的指令。这种方式可以显著提高处理器的效率,因为它减少了等待分支决策的时间。
然而,推测执行也引入了潜在的安全漏洞(如Spectre和Meltdown),因为它可能允许恶意程序推断出在推测执行路径中访问的敏感数据。为了缓解这些安全问题,处理器制造商和操作系统开发者实施了各种补丁和更新,这些更新可能会影响CPU指令的性能,包括cmov
。
cmov
与推测执行的关系cmov
指令的性能可能受到推测执行机制的影响,原因如下:
cmov
指令的效率依赖于处理器的分支预测准确性。如果预测的条件结果不准确,那么cmov
执行的结果可能需要被丢弃,从而导致性能下降。在对抗推测执行漏洞的修复措施中,增强的安全检查可能增加了分支预测的复杂性,进而影响到cmov
的效率。cmov
等条件指令。这些调整可能会影响到cmov
的执行速度,尤其是在密集使用条件移动指令的代码路径中。虽然cmov
指令本身是为了提高在条件分支情况下的执行效率而设计的,推测执行引入的安全修复可能会对其性能造成一定影响。不过,这种影响的实际程度会根据具体的处理器模型、操作系统补丁和应用场景而有所不同。在大多数情况下,现代处理器和编译器的优化足以减轻这些影响,但在性能敏感的应用中,开发者可能需要密切关注这些因素,以及可能的性能变化。
https://yarchive.net/comp/linux/cmov.html
#include <stdio.h>
/* How many iterations? */
#define ITERATIONS (100000000)
/* Which bit of the counter to test? */
#define BIT 1
#ifdef CMOV
#define choose(i, a, b) ({ \
unsigned long result; \
asm("testl %1,%2 ; cmovne %3,%0" \
:"=r" (result) \
:"i" (BIT), \
"g" (i), \
"rm" (a), \
"0" (b)); \
result; })
#else
#define choose(i, a, b) ({ \
unsigned long result; \
asm("testl %1,%2 ; je 1f ; mov %3,%0\n1:" \
:"=r" (result) \
:"i" (BIT), \
"g" (i), \
"g" (a), \
"0" (b)); \
result; })
#endif
int main(int argc, char **argv)
{
int i;
unsigned long sum = 0;
for (i = 0; i < ITERATIONS; i++) {
unsigned long a = 5, b = 7;
sum += choose(i, a, b);
}
printf("%lu\n", sum);
return 0;
}
gcc -Wall -O2 cmov.c
time ./a.out 600000000
real 0m0.540s user 0m0.065s sys 0m0.001s
gcc -Wall -O2 -DCMOV cmov.c time ./a.out
600000000
real 0m0.522s user 0m0.055s sys 0m0.000s
linus这个结论过时了,毕竟07年。
新机器cmov是快的。这个测试比较简单没有引入cacheline的影响
改成godbolt
https://github.com/marcin-osowski/cmov/
https://kristerw.github.io/2022/05/24/branchless/
https://www.agner.org/optimize/optimizing_assembly.pdf
我发现agner这哥们的手册基本高手都知道,但是大家都不怎么传播