for循环机制效率提示

时间:2020-03-06 14:50:29  来源:igfitidea点击:

当我在大型多维数组上使用for循环时,for循环机制本身的任何节省都是有意义的。

因此,我正在寻找有关如何减少这种开销的任何技巧。

例如:使用uint而不是int和!= 0作为stop而不是> 0来递减计数,这会使CPU做更少的工作(听过一次,不确定它是否始终为真)

解决方案

一个重要建议:将尽可能多的计算移到外部循环。并非所有的编译器都可以自动执行此操作。对于示例,而不是:

for row = 0 to 999
    for col = 0 to 999
        cell[row*1000+col] = row * 7 + col

使用:

for row = 0 to 999
    x = row * 1000
    y = row * 7
    for col = 0 to 999
        cell[x+col] = y + col

由于循环将具有O(n ^ d)复杂度(d =维度),因此真正重要的是我们将INTO放入循环中,而不是循环本身。在循环框架中,从循环内效率低下的算法的数百万个循环中优化出几个循环,简直就是蛇油。

循环展开可以是一种方法。那是:

for (i=0; i<N; i++) {
  a[i]=...;
}

转换为:

for (i=0; i<N; i+=4) {
  a[i]=...;
  a[i+1]=...;
  a[i+2]=...;
  a[i+3]=...;
}

在上面的示例中,当N不是4的倍数时,我们将需要特殊处理。

你测量过开销了吗?我们知道处理for循环花费了多少时间,而执行应用程序代码花费了多少时间吗?你的目标是什么?

这不是一个与语言无关的问题,它不仅取决于语言,而且还取决于编译器。我相信大多数编译器会等效地编译这两个:

for (int i = 0; i < 10; i++) { /* ... */ }

int i = 0;
while (i < 10) {
    // ...
    i++;
}

在大多数语言/编译器中,for循环只是后面while循环的语法糖。 Foreach还是另一个问题,在实现方式上高度依赖于语言/编译器,但通常效率不如常规for / while循环。取决于语言和编译器的大小又是多少。

最好的选择可能是对某个主题运行一些具有不同变体的基准测试,然后看看结果如何。

编辑:为此,这里的建议可能会节省我们更多的时间,而不用担心循环本身。

我同意@Greg。我们需要做的第一件事是建立一些基准测试。除非我们证明所有处理时间都花在了哪里,否则优化几乎没有意义。 "过早的优化是万恶之源"!

尝试使循环在内存中连续,这将优化高速缓存的使用。也就是说,不要这样做:

for (int i = 0; i < m; i++)  
    for (j = 0; j < n; j++)  
        s += arr[j][i];
  • 如果要处理图像,请使用单个索引将两个循环转换为一个像素循环。
  • 不要进行将运行零次的循环,因为对管道进行了优化,以假定循环将继续而不是结束。

顺便说一句,除非需要后增量,否则应始终使用前增量运算符。这只是微小的差异,但效率更高。

在内部,这是不同的:

  • Post Incrementi ++;等同于:int postincrement(int&i){int itmp = i;我=我+ 1;返回itmp; }
  • Pre Increment++ i;等同于:int preincrement(int&i){i = i + 1;返回我}

我认为大多数编译器都可能会这样做,降到零应该更有效,因为对于处理器而言,零检查非常快。再次强调一下,任何值得考虑的编译器无论如何都会对大多数循环执行此操作。我们需要查看编译器在做什么。

没有足够的信息来准确回答问题。我们在循环中做什么?一次迭代中的计算是否取决于上一次迭代中计算的值。如果不是这样,假设我们至少有一个双核处理器,则只需使用2个线程就可以将时间几乎减少一半。

要查看的另一件事是,如果要进行大型阵列处理,则如何访问数据,以确保在存储在内存中时按顺序访问数据,避免在每次迭代时刷新L1 / L2缓存(请参见之前在较小的L1缓存上,差异可能很大)。

再次,我将首先查看循环内部的内容,其中大部分收益(> 99%)将出现在循环内部,而不是外部循环中。

但是话又说回来,如果循环代码受I / O约束,那么浪费在优化上的任何时间都将被浪费掉。

首先,不要流汗小东西。诸如递增计数与递减计数之类的细节通常与运行时间完全无关。众所周知,人类在发现需要加速的代码方面很糟糕。使用探查器。很少或者根本不注意循环中没有重复的任何部分,除非事件探查器另有说明。请记住,在内部循环中编写的内容不一定在内部循环中执行,因为现代编译器非常聪明,可以避免不必要的重复。

话虽这么说,要非常警惕现代CPU上的展开循环。它们越紧密,就越适合缓存。在去年工作的高性能应用程序中,我使用循环代替了直线代码,并尽可能地加紧了它们,从而显着提高了性能。 (是的,我进行了分析;有问题的函数占用了运行时间的80%。我还对典型输入的时间进行了基准测试,因此我知道所做的更改会有所帮助。)

而且,养成习惯于高效代码的习惯也没有害处。在C ++中,我们应该养成使用前递增(++ i)而不是后递增(i ++)来递增循环变量的习惯。通常,这无关紧要,但是可以带来很大的不同,它不会使代码的可读性或者可写性降低,并且不会造成伤害。

另一个stackoverflow问题的答案中有一些相关信息,即缓存是如何工作的。我发现此答案中提到的Ulrich Drepper的论文特别有用。

顺便说一句,如果保证Int16容量足够,在循环中使用short而不是int是否很好?