Apparently,现代处理器可以判断您是否做了一些愚蠢的事情,例如将寄存器移到其自身(mov %eax, %eax
)并对其进行优化。为了验证该声明,我运行了以下程序:
#include <stdio.h>
#include <time.h>
static inline void f1() {
for (int i = 0; i < 100000000; i++)
__asm__(
"mov %eax, %eax;"
"nop;"
);
}
static inline void f2() {
for (int i = 0; i < 100000000; i++)
__asm__(
"nop;"
);
}
static inline void f3() {
for (int i = 0; i < 100000000; i++)
__asm__(
"mov %ebx, %eax;"
"nop;"
);
}
int main() {
int NRUNS = 10;
clock_t t, t1, t2, t3;
t1 = t2 = t3 = 0;
for (int run = 0; run < NRUNS; run++) {
t = clock(); f1(); t1 += clock()-t;
t = clock(); f2(); t2 += clock()-t;
t = clock(); f3(); t3 += clock()-t;
}
printf("f1() took %f cycles on avg\n", (float) t1/ (float) NRUNS);
printf("f2() took %f cycles on avg\n", (float) t2/ (float) NRUNS);
printf("f3() took %f cycles on avg\n", (float) t3/ (float) NRUNS);
return 0;
}
这给了我
f1() took 175587.093750 cycles on avg
f2() took 188313.906250 cycles on avg
f3() took 194654.296875 cycles on avg
正如人们所期望的,f3()
的发布速度最慢。但是令人惊讶的是(至少对我来说),f1()
比f2()
快。为什么会这样?
更新:使用-falign-loops
进行编译可得出定性的结果:
f1() took 164271.000000 cycles on avg
f2() took 173783.296875 cycles on avg
f3() took 177765.203125 cycles on avg
答案 0 :(得分:2)
链接文章中让我认为可以对其进行优化的部分是:“移动功能负责检查等效位置”
那是在谈论SBCL中的(move r x)
功能,而不是x86 mov
指令。讨论的是从低级中间语言生成代码期间的优化,而不是硬件在运行时的优化。
mov %eax, %eax
和nop
都不是完全免费的。它们都消耗前端吞吐量,并且mov %eax,%eax
甚至在64位模式下也不是NOP(它将EAX零扩展为RAX,因为它是相同的寄存器,因此在Intel CPU上无法进行移动消除。)< / p>
有关前端/后端吞吐量瓶颈与延迟的关系,请参见Can x86's MOV really be "free"? Why can't I reproduce this at all?。
您可能会看到代码对齐的一些副作用,或者像Adding a redundant assignment speeds up code when compiled without optimization中那样出现了时髦的Sandybridge系列存储转发延迟效应,因为您还禁用了优化功能进行编译,从而使编译器反-用于一致调试的优化代码,可将循环计数器保留在内存中。 (通过一个存储/重载,大约有6个循环的循环依赖链,而不是正常的微小循环每个时钟1次迭代。)
如果您的结果可以通过较大的迭代次数进行重现,则可能对您所看到的内容有一些微体系结构的解释,但可能与您尝试测量的内容无关。
当然,您还需要修复mov %ebx, %eax;
中的f3
错误,以在启用优化的情况下成功编译。在不通知编译器的情况下破坏EAX将会依靠编译器生成的代码。您没有解释要使用的测试内容,因此请IDK(如果是错字的话)。