在我尝试测量算术运算执行时间的过程中,我遇到了非常奇怪的行为。一个代码块包含一个 for
循环,在循环体中只有一次算术运算,总是比一个完全相同的代码块慢执行,但是在 for
循环体中有两个算术运算。这是我测试的代码:
#include <iostream>
#include <chrono>
#define NUM_ITERATIONS 100000000
int main()
{
// Block 1: one operation in loop body
{
int64_t x = 0, y = 0;
auto start = std::chrono::high_resolution_clock::now();
for (long i = 0; i < NUM_ITERATIONS; i++) {x+=31;}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << diff.count() << " seconds. x,y = " << x << "," << y << std::endl;
}
// Block 2: two operations in loop body
{
int64_t x = 0, y = 0;
auto start = std::chrono::high_resolution_clock::now();
for (long i = 0; i < NUM_ITERATIONS; i++) {x+=17; y-=37;}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << diff.count() << " seconds. x,y = " << x << "," << y << std::endl;
}
return 0;
}
我使用不同的代码优化级别(-O0
,-O1
,-O2
,-O3
)进行测试,并使用不同的在线编译器(例如onlinegdb.com),在我的工作机器、家用PC和笔记本电脑、树莓派和同事的计算机上进行测试。我重新排列了这两个代码块,重复了它们,改变了常量,改变了操作(+
,-
,<<
,=
等),改变了整数类型。但是我始终得到类似的结果:循环中有一行的块比有两行的块慢:
1.05681秒。x,y = 3100000000,0
0.90414秒。x,y = 1700000000,-3700000000
我在https://godbolt.org/上检查了汇编输出,但一切都像我预期的那样:第二个块在汇编输出中只有一个更多的操作。
三个操作总是表现如预期的那样:它们比一个操作慢,比四个操作快。那么为什么两个操作会产生这样的异常?
编辑:
让我重申一遍:我在所有未经优化的 Windows 和 Unix 机器上都有这样的行为。我查看了我执行的汇编(Visual Studio,Windows),并在那里看到要测试的指令。无论如何,如果循环被优化掉,剩下的代码中就没有我要询问的内容。我在问题中添加了优化通知,以避免出现“不要测量未经优化的代码”的答案,因为优化不是我所问的。实际上,问题是为什么我的计算机在执行两个操作时比一个操作更快,尤其是在这些操作没有被优化掉的代码中。在我的测试中,执行时间差异为5-25%(非常明显)。