小循环中的大循环总是比大循环中的小循环快?

时间:2014-05-28 14:24:39

标签: c++ performance loops nested-loops

我刚刚阅读this post,并想知道我们是否可以得出结论:小循环中的大循环必须总是比大循环中的小循环运行得快,无论嵌套内部的代码是什么环?举个例子。

int m, n; 
m = 1000000;
n = 10;

摘录A

for (int i = 0; i < n; i++)         
    for (int j=0; j < m; j++)               
       {       
           DoSomething();        
       }

小组B

for (int j = 0; j < m; j++)               
    for (int i=0; i < n; i++)           
       {       
          DoSomething();          
       }

我们可以这么说,不管DoSomething()实际上做了什么,片段A总是比片段B运行得更快?

更新
正如@stackmate指出的那样,我想将这个问题扩展为两个

  1. 当嵌套循环内的代码是DoSomething()时,意味着 DoSomething()与变量i和j无关。什么是 性能差异?

  2. 当嵌套循环内的代码是DoSomething(i,j)时,意味着 DoSomething(i,j)与变量i和j有关系。性能差异是什么?

6 个答案:

答案 0 :(得分:6)

您的问题无法得到具体答案。 决定它是否快速的参数是你在循环中做的事情。 例如,假设您要添加2个数组并将它们存储在第三个数组中:

Code 1:
for(int i = 0; i < 1000; i++)
{
    for(int j = 0; j < 1000000; j++)
         C[i][j] = A[i][j] + B[i][j];
}

Code 2:
for(int i = 0; i < 1000000; i++)
{
    for(int j = 0; j < 1000; j++)
         C[j][i] = A[j][i] + B[j][i];
}

代码1将比代码2快得多。 原因是缓存。 有关详细信息,请查看this问题。答案非常丰富,我在这里再次解释缓存的概念毫无意义。

答案 1 :(得分:2)

@Cool_Coder已经涵盖了一个主要原因(内存访问模式导致更好的缓存命中率)为什么拥有较小的循环,因为内部循环可能是有益的。

另一种情况是内循环可以展开。特别是如果较小的循环的大小确实小且固定,编译器将展开内循环,如果它是有益的。结果代码将只有一个循环而不是两个嵌套循环,分支减少。

如果您在高性能关键代码中遇到类似情况,则需要同时尝试这两种情况,并仔细进行基准测试。如果代码对性能不是很重要,那么可能不值得担心。

答案 2 :(得分:1)

观察。如果您在嵌套循环中有读取操作,如下所示

for (int i = 0; i < n; i++)
    a = aList.get(i);         
    for (int j=0; j < m; j++)               
       {   
           b = bList.get(j)
           DoSomething(a, b);        
       }

然后n < m导致someList.get - 操作少于n > m。对于n=1m=2,将有三个读取操作,而n=2m=1将有四个读取操作。在n > m的情况下,将有更多的重复读取操作。或者换句话说运行时为n + n*m。无论n*m还是n > m,第一个加数都会发生变化,n < m值保持不变。

(我假设没有编译器优化并忽略了缓存行为)

答案 3 :(得分:0)

取决于。

如果你在for循环之间做任何其他事情,那么首先迭代m将需要更长时间才能执行,因为它在进入下一个循环之前由于额外增加的工作而做了更多工作。

如果没有,那么

n * m = m * n

Andy T的评论也是正确的。当外循环是最长循环时,内循环的初始化更频繁地发生。如果这是c ++,我希望编译器能够优化它,以便对代码进行基准测试可能会为两个循环产生相同的结果。

答案 4 :(得分:0)

在代码段A中:n=10次切换到外部For循环 但是在Snippet B中:m=1000000次切换到外部For循环。这个原因(两个For循环之间的更多切换)使得Snippet A比Snippet B更快。

答案 5 :(得分:0)

for(int i = 0; i < 1000; i++)
{
        for(int j = 0;
如果你看到我们正在初始化j 1000次,在上面的代码中

for(int i = 0; i < 1000000; i++)
{
    for(int j = 0;

而在第二个代码中,我们正在初始化j 1000000次,这清楚地表明第二个代码有额外的开销。