【发布时间】:2020-03-14 02:04:00
【问题描述】:
我发现一个有趣的现象:
#include<stdio.h>
#include<time.h>
int main() {
int p, q;
clock_t s,e;
s=clock();
for(int i = 1; i < 1000; i++){
for(int j = 1; j < 1000; j++){
for(int k = 1; k < 1000; k++){
p = i + j * k;
q = p; //Removing this line can increase running time.
}
}
}
e = clock();
double t = (double)(e - s) / CLOCKS_PER_SEC;
printf("%lf\n", t);
return 0;
}
我在 i5-5257U Mac OS 上使用 GCC 7.3.0 来编译代码没有任何优化。这是超过 10 次的平均运行时间:
还有其他人在其他英特尔平台上测试该案例并得到相同的结果。
我发布了由 GCC here 生成的程序集。两个汇编代码之间的唯一区别是在addl $1, -12(%rbp) 之前,更快的一个还有两个操作:
movl -44(%rbp), %eax
movl %eax, -48(%rbp)
那么为什么程序在这样的分配下运行得更快?
Peter's answer 很有帮助。在 AMD Phenom II X4 810 和 ARMv7 处理器 (BCM2835) 上的测试显示了相反的结果,它支持存储转发加速特定于某些 Intel CPU。
BeeOnRope's comment and advice 促使我重写这个问题。 :)
这个问题的核心是与处理器架构和组装有关的有趣现象。所以我认为这可能值得讨论。
【问题讨论】:
-
您在构建时是否启用了优化?任何没有优化的基准测试都是毫无价值的。
-
您可以指示
gcc仅生成程序集,这通常比您提供的反汇编程序更具可读性(恕我直言,“反编译”一词是错误的)。 -
您正在对调试版本进行基准测试,which is basically useless。 但是,如果您想确切知道原因,瓶颈将是所有存储/重新加载,可能是一个循环-对
k进行依赖。如果你在 Skylake,store/reload latency can actually be lower (better) when there's more in between the dependent pair (including other stores/loads).. -
所以根本没有优化。如前所述,这不足以进行基准测试。至少使用
-O2。 -
@TobySpeight - 我不同意。未经优化的编译对于性能分析没有用处,但归根结底,无论编译器设置如何,人们可能会问为什么编译器发出的一个 sn-p 程序集比另一个慢,尽管第一个程序集具有严格的更少的陈述。正如彼得的回答所示,仅此一点就很有趣。
标签: performance assembly x86 cpu-architecture micro-architecture