我发现一个有趣的现象:#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倍: 还有其他人在其他Intel平台上测试该案例并获得相同的结果。 我将在这里发布由GCC生成的程序集。两种汇编代码之间的唯一区别是,在较快的一种汇编代码之前,还有两项操作:在此处输入图片说明addl $1, -12(%rbp)movl -44(%rbp), %eaxmovl %eax, -48(%rbp)那么,为什么用这样的分配程序运行得更快?彼得的回答很有帮助。在AMD Phenom II X4 810和ARMv7处理器(BCM2835)上进行的测试显示了相反的结果,该结果支持存储转发加速特定于某些Intel CPU。而BeeOnRope的评论和建议催着我重写的问题。:)这个问题的核心是与处理器架构和组装相关的有趣现象。因此,我认为值得讨论。
3 回答
慕娘9325324
TA贡献1783条经验 获得超4个赞
请注意,call/ret
不会创建循环承载的依赖项,因为由推送的地址call
来自推测执行+分支预测。当存储不依赖于数据时,多次存储/重载到同一地址可以使每个时钟维持一个时钟。执行ret
指令可以每个时钟执行一次,比call
指令落后5个周期。(当然,调用/ ret都是分支,因此它们彼此竞争执行资源,因此甚至没有瓶颈。)可能是问题,是a push/pop rbp
或x=foo(x)
by ref。
BIG阳
TA贡献1859条经验 获得超6个赞
我认为这个问题没有用。您开始时的一大不利之处是没有优化就进行编译,这对于“为什么Y的性能表现得像Z”已经是一个巨大的危险信号-但由于编译器仅针对较慢的情况发出了额外的指令,因此事实证明这很有趣在组装级别的问题。即,您几乎可以删除问题的C起源,以及您在不进行优化的情况下进行编译的事实,并询问程序集的行为,并且可能避免雪崩式的雪崩
添加回答
举报
0/150
提交
取消