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

Is a big loop within a small loop always faster than a small loop within a big one?

本文关键字:小循环 大循环      更新时间:2023-10-16

我刚刚读了这篇文章,想知道我们是否可以得出这样的结论:无论代码在嵌套循环中做什么,小循环中的大循环总是比大循环中的小循环运行得更快?举个例子。

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有相关性。性能差异是什么?

您的问题不能有一个具体的答案。决定它是否快的参数是你在循环中所做的。例如,假设您正在添加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快得多。原因是缓存。查看此问题以了解更多详细信息。这些答案信息丰富,我在这里再次解释缓存的概念毫无意义。

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

另一种情况是可以展开内部循环。特别是如果较小循环的大小真的很小并且是固定的,编译器将展开内部循环(如果有益的话(。生成的代码将只有一个循环,而不是两个嵌套的循环,并减少分支。

如果在高性能关键代码中遇到这样的情况,则需要同时尝试这两种方法,并仔细进行基准测试。如果代码不是非常关键的性能,那么可能不值得担心。

一个观察。如果您在嵌套循环中读取操作,如以下

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);        
       }

则具有CCD_ 1导致比CCD_ 3更少的CCD_。对于n=1m=2将有三个读取操作,而对于n=2m=1将有四个读取操作。在n > m的情况下,将有更多的重复读取操作。或者换句话说,运行时是n + n*m。无论是CCD_ 11还是CCD_ 12,值CCD_。

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

这取决于情况。

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

如果没有,则

n * m = m * n

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

在代码段A:n < m3次切换到外部For循环但在代码段B:m=1000000次切换到外部For循环。这个原因(在两个For循环之间切换更多(使代码段A比代码段B更快。

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

在上面的代码中,如果您看到我们正在初始化j 1000次。

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

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

我认为这里正在进行大量优化。查看此jsben.ch:https://jsben.ch/FjXuL

var big = 10000;
var little = 10;
var x = 0;
// Little loop in Big loop
for (var i = 0; i < big; i++) for (var j=0; j < little; j++) { x++; }
// Big loop in Little loop
for (var i = 0; i < little; i++) for (var j=0; j < big; j++) { x++; }
// Little loop in Big loop (single initializations)
var j;
for (var i = 0; i < big; i++) for (j=0; j < little; j++) { x++; }
// Big loop in Little loop (single initializations)
var j;
for (var i = 0; i < little; i++) for (j=0; j < big; j++) { x++; }

当单独测试时,大循环中的小循环在Chrome中总是领先。在"循环"中的第一个循环之外声明j;"单一初始化";测试似乎没有帮助,而且实际上大大降低了大循环中的小循环(单个初始化(测试的速度。

也就是说,结果可能因浏览器实现/编译器而异。

我认为嵌套较大的循环是有利的,这是合乎逻辑的,因为初始化嵌套的j变量的次数更少,但显然在Chrome中,这不是性能的一个大因素,因为嵌套较小的循环更快。