gcc - 为什么`mov %eax, %eax; nop` 比`nop` 快?
问题描述
显然,现代处理器可以判断您是否做了一些愚蠢的事情,例如将寄存器移到自身 ( 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
解决方案
链接文章中让我认为可以优化掉的部分是:“移动功能负责检查等效位置”
那是在谈论 SBCL 中的(move r x)
功能,而不是 x86mov
指令。它谈论的是从低级中间语言生成代码期间的优化,而不是在运行时由硬件进行的优化。
两者都不mov %eax, %eax
是nop
完全免费的。它们都消耗前端吞吐量,mov %eax,%eax
甚至不是 64 位模式下的 NOP(它将 EAX 零扩展为 RAX,并且因为它是相同的寄存器移动消除在 Intel CPU 上失败。)
看看x86 的 MOV 真的可以“免费”吗?为什么我根本无法重现这个?有关前端/后端吞吐量瓶颈与延迟的更多信息。
您可能会看到代码对齐的一些副作用,或者可能是一种时髦的 Sandybridge 家族存储转发延迟效应,例如添加冗余分配在没有优化的情况下编译时会加速代码,因为您也在禁用优化的情况下进行编译,让您的编译器制作反优化代码以进行一致的调试,将循环计数器保存在内存中。(通过存储/重新加载约 6 周期循环携带的依赖链,而不是普通微小循环的每时钟 1 次迭代。)
如果您的结果可以通过更大的迭代次数重现,那么您所看到的可能有一些微架构解释,但它可能与您尝试测量的任何内容无关。
当然,您还需要修复mov %ebx, %eax;
错误f3
才能在启用优化的情况下成功编译。在不告诉编译器的情况下破坏 EAX 将踩到编译器生成的代码。你没有解释你试图用它来测试什么,所以如果它是一个错字,请 IDK。
推荐阅读
- git - 使用 git push 时出现错误“错误的行长度 8192iB”
- javascript - 如何将 Auth0 JWT 与 angularjs 一起用于 payLoad 数据
- email - 不完整的附件仍然附在邮件中
- c# - 交换盒子的位置?
- thingsboard - 如何聚合来自一项资产中包含的不同设备的遥测数据?
- angular - 从“远程”json获取数据到当前json ionic 3
- python-3.x - Python 3 - 在测试标头后从 URL 读取 CSV
- sql - SQL-SERVER:外部 Select 每一行的子查询
- shiny - 我怎样才能让 Shiny 记住来自多个 selectInputs 的各种选择
- configuration - Payara 5 - 在执行此操作之前,应将 Statement Wrapping 设置为 true