5

我在 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 循环是空的,我认为它不会真正产生影响。

任何帮助都会得到帮助:)

编辑:多亏了评论,我意识到使用空的 for 循环可能不是测试事物的最佳方式......所以我更新了我的代码以将一些(非常)简单的操作变为双重。我也在发布模式下编译。然而,虽然这两种方法在时间上更加相似,但第二种方法仍然稍微快一些。

是的,这就是所有的测试代码(减去计时/输出功能,但这些并不完全针对问题)。

4

3 回答 3

11

当 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;only 贡献 1 in 90000 和testX += x;only 贡献 1 in 300。因此上面的两个部分保持可比性。

嵌套循环并不比单循环慢多少,但您观察到它们更快是正确的。

并且:你展示的次数大约是我观察到的次数的 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

结果:

  • 嵌套循环并不比单循环快。
于 2012-09-14T10:41:19.173 回答
1

如果您不在循环中使用for变量(x, y, zfor,智能编译器可以(并且应该)在一个for循环中转换您的第二种形式而无需嵌套。除非您通过让用户在运行时从 输入xyzstdin或从某个流中读取等来防止此类编译器优化消除静态可预测性。

此外,如果您不对testX变量做任何事情(例如,将其打印到stdout),智能编译器可以(并且应该)将其优化掉,即完全删除死代码。

所以我要说的是,现在的基准测试在某种程度上是不正确的。

于 2012-09-14T07:02:37.077 回答
0

你最好的选择是查看反汇编并检查生成代码的差异,我猜编译器在那里做了一些非常重要的优化。

于 2012-09-14T06:52:39.907 回答