我在c ++(MSVS)中进行了一些速度测试并得到了一个奇怪的结果。我正在测试使用一个for循环和多个嵌套for循环的速度。这是代码:
double testX = 0;
// Single loop executes in roughly 0.04 seconds
for( int i = 0; i < 27000000; i++ ){
testX += 1;
}
// Nested loop executes in roughly 0.03 seconds
for( int x = 0; x < 300; x++ ){
for( int y = 0; y < 300; y++ ){
for( int z = 0; z < 300; z++ ){
testX += 1;
}
}
}
如您所见,速度差异相当明显。我已经运行了很多次,这些是我看到的平均时间(这些是使用glfwGetTime()计时的)。
所以我的问题是:为什么?我的测试方法不合适吗?我使用的循环太少了吗?我试过搜索谷歌,我能找到的唯一类似的问题与缓存一致性的问题有关,但由于这些循环是空的,我认为它不会真正产生影响。
任何帮助都是适当的:)
编辑:感谢评论,我意识到使用空for循环可能不是最好的测试方法...所以我已经更新了我的代码来做一些(非常)简单操作到双。我也在发布模式下编译。然而,尽管这两种方法在时间上更相似,但第二种方法仍然稍微快一些。
是的,这是所有的测试代码(减去时序/输出函数,但这些并不是特定的问题)。
答案 0 :(得分:9)
当在代码中的某个地方使用testX变量时,编译器不会“优化”循环。当我只在代码中添加一行输出testX时,结果如下:
single for loop: 1.218 ms
nested for loop: 1.218 ms
这几乎表明编译器尽可能将嵌套循环转换为单个循环。循环索引可用于防止优化:
以这种方式修改代码
for( int i = 0; i < 27000000; i++ ){
testX += i;
}
和
for( int x = 0; x < 300; x++ ){
testX += x;
for( int y = 0; y < 300; y++ ){
testX += y;
for( int z = 0; z < 300; z++ ){
testX += z;
}
}
}
会给嵌套循环增加一点开销,但执行时间会显示
single for loop: 1.224 ms
nested for loop: 1.226 ms
此处给出的时间平均超过30.000次循环运行。
注意:testX += x;
仅在90000中贡献1,testX += x;
仅在300中贡献1。因此,上述两个部分仍具有可比性。
嵌套循环并不比单循环慢得多,但您观察到它们更快是不为真。
并且:您展示的时间大约是我观察次数的40倍。因为我在中速硬件上运行测试,所以我建议仔细检查编译器设置。也许glfwGetTime()
的结果值得怀疑,这是您提问的主要原因。你有没有尝试过另一种计时方案?
编辑:为了防止编译器优化,可以选择循环限制为非常数:
int lmt = rand() % 1 + 300; // random value 300 or 301
int big_lmt = lmt * lmt * lmt; // random value 27000000 or 27270901
for( int i = 0; i < big_lmt; i++ ){
testX += i;
}
for( int x = 0; x < lmt; x++ ){
testX += x;
for( int y = 0; y < lmt; y++ ){
testX += y;
for( int z = 0; z < lmt; z++ ){
testX += z;
}
}
}
这避免了编译器的可预测性。
结果(lmt = 300
案例具有可比性):
single for loop: 1.213 ms
nested for loop: 1.216 ms
结果:
答案 1 :(得分:1)
如果您未在for
循环中使用x
变量(y
,z
,for
),则可以使用智能编译器(和应该)在没有嵌套的情况下在单个for
循环中转换您的第二个表单。除非您通过让用户在运行时从x
输入y
,z
,stdin
值或从某些流中读取等来阻止此类编译器优化来消除静态可预测性。
此外,如果你没有对你的testX
变量做任何事情(比如将其打印到stdout
),智能编译器可以(并且应该)优化它,即删除死代码共
所以我所说的是,现在的基准测试在某种程度上是不合理的。
答案 2 :(得分:0)
您最好的选择是查看反汇编并检查生成代码的差异,我猜编译器会在那里进行一些非常重的优化。