Ble*_*der 5 x86 assembly gcc cpu-architecture
显然,现代处理器可以判断您是否做了一些愚蠢的事情,例如将寄存器移动到自身 ( 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;
}
Run Code Online (Sandbox Code Playgroud)
这给了我:
f1() took 175587.093750 cycles on avg
f2() took 188313.906250 cycles on avg
f3() took 194654.296875 cycles on avg
Run Code Online (Sandbox Code Playgroud)
正如人们所料,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
Run Code Online (Sandbox Code Playgroud)
链接文章中让我认为可以优化的部分是:“移动功能负责检查等效位置”
那是在谈论SBCL中的(move r x)
函数 ,而不是x86mov
指令。它谈论的是从低级中间语言生成代码期间的优化,而不是硬件运行时的优化。
两者mov %eax, %eax
都不nop
是完全免费的。它们都会消耗前端吞吐量,并且mov %eax,%eax
甚至不是 64 位模式下的 NOP(它将 EAX 零扩展为 RAX,并且因为它是相同的寄存器,所以在 Intel CPU 上 mov-elimination 会失败。)
请参阅x86 的 MOV 真的可以“免费”吗?为什么我根本无法重现这个?有关前端/后端吞吐量瓶颈与延迟的更多信息。
您可能会看到代码对齐的一些副作用,或者可能会看到时髦的 Sandybridge 系列存储转发延迟效果,例如在没有优化的情况下编译时添加冗余分配会加速代码,因为您也在禁用优化的情况下进行编译,从而使编译器制作反优化代码以实现一致的调试,将循环计数器保留在内存中。(通过存储/重载实现约 6 个循环循环携带的依赖链,而不是正常小循环每个时钟 1 次迭代。)
如果您的结果可以通过更大的迭代次数重现,则可能对您所看到的内容有一些微架构解释,但它可能与您尝试测量的任何内容无关。
当然,您还需要修复mov %ebx, %eax;
错误才能在f3
启用优化的情况下成功编译。在不告诉编译器的情况下破坏 EAX 将执行编译器生成的代码。你没有解释你想用它来测试什么,所以我不知道这是不是一个错字。
归档时间: |
|
查看次数: |
376 次 |
最近记录: |