for循环机制效率提示
当我在大型多维数组上使用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 Increment
i ++;
等同于: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
是否很好?