2008-09-28 99 views
5

由于我使用的for循环的大的多暗淡阵列,在for循环机制的任何节约本身就是有意义的。for循环机制效率的技巧

因此,我在寻找如何降低这种开销的任何提示。

例如:使用uint而不是int和!= 0作为停止而不是> 0允许CPU做更少的工作(听过一次,不确定它总是真的)

+0

看到@monoxide答案。这不应该被标记为不可知的语言,如果人们知道他们正在优化哪种语言/编译器,我想你会得到更好的答案。 – 2008-09-28 09:01:37

+0

同意,优化具体的语言和方式,你那句似乎这个问题你是到靶向特定的平台,以及(运次不同的CPU而异) – Oskar 2008-09-28 10:15:56

+0

标签的需求,澄清 – Sklivvz 2008-09-28 10:59:46

回答

4

首先,不要出汗的小东西。像倒计时和倒计时这样的细节通常在运行时间中完全不相关。人类在识别需要加速的代码领域是非常糟糕的。使用分析器。很少或根本不关注没有重复的循环的任何部分,除非分析器另有说明。请记住,内部循环中写入的内容不一定在内部循环中执行,因为现代编译器在避免不必要的重复方面非常聪明。

这就是说,要对现代CPU上的循环展开非常谨慎。它们越紧密,它们越适合缓存。在去年工作的高性能应用程序中,我通过使用循环而不是直线代码来显着提高性能,并尽可能地收紧它们。 (是的,我进行了分析;所涉及的功能占用了运行时间的80%。我还基准次以上典型的输入,所以我知道的变化帮助。)

而且,在开发有利于高效的代码习惯,没有坏处。在C++中,你应该习惯使用前增量(++ i)而不是后增(i ++)来增加循环变量。它通常没有关系,但可以产生显着差异,它不会使代码更少可读或可写,并且不会受到伤害。

12

一个重要的建议:将尽可能多的计算外环尽可能。并非所有的编译器都可以自动完成。对于eample,而不是:

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 
+0

是的,这与我的建议共鸣:请内循环快。一个例子是Quicksort。 – 2010-06-30 13:55:20

1

当你的循环将有O(N^d)的复杂性(d =尺寸),真正重要的是你把进入死循环,而不是循环本身。在循环内优化几个循环,从循环内部数百万循环的低效率算法开始,就是蛇油。

+0

我从来没有发现O符号有用,除非比较两个执行相同事情的算法。说Bubble排序是O(n^2)而Quicksort是O(n lg n)是有道理的。对我来说,说一些东西是O(n^2),没有类似的东西来比较它是没有道理的。 – 2008-09-28 10:52:05

+0

要学究:基本实现快速排序的为O的平均情况复杂度(N log n)的,但仍然为O的最坏情况复杂度(N^2)。 – 2008-09-28 13:25:41

5

循环展开可以是单向的。那就是:

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在上面的例子多。

6

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

4

这不是一个语言无关的问题,这在很大程度上取决于不仅语言,而且编译器。大多数编译器,我相信会编这两种等价的:

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

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

在大多数语言/编译器,for循环是为以后的while循环只是语法糖。 Foreach又是另一个问题,并且高度依赖于语言/编译器如何实现,但通常是for/while循环效率较低。还有多少,语言和编译器依赖。

您最好的选择可能是就一个主题运行一些基准测试与几个不同的变化,看看是什么在上面出来。

编辑:为此,该suggestions here可能会为您节省更多的时间,而不是担心循环本身。

3

我同意@Greg。你需要做的第一件事是放置一些基准。除非您证明您的处理时间花费在哪里,否则将毫无意义地优化任何内容。 “过早优化是万恶之源”!

9

试着让你的循环在内存中连续,这将优化缓存使用率。也就是说,不这样做:

for (int i = 0; i < m; i++) 
    for (j = 0; j < n; j++) 
     s += arr[j][i]; 
  • 如果处理图像,转换两个循环到一个循环上的像素用单一指标。
  • 不要让循环运行零次,因为管道已经过优化,假设循环将继续而不是结束。
4

顺便说一句,除非你需要后增量,你应该总是使用前增量操作符。这只是一个小小的区别,但它更有效率。

内部此的区别是:

  • 后增量

    i++;

    相同:

    int postincrement(int &i)
    {
    int itmp = i;
    i = i + 1;
    return itmp;
    }

  • 预公司种类调和

    ++i;

    是一样的:

    int preincrement(int &i)
    {
    i = i + 1;
    return i;
    }

0

我想大多数编译器可能会做这个,无论如何,降压零应该更有效,因为一检查处理器的零速度非常快。不过,任何值得它的权重的编译器都会在大多数循环中执行此操作。你需要知道编译器在做什么。

0

没有足够的信息来准确回答你的问题。你在循环中做什么?一次迭代中的计算是否取决于先前迭代中计算的值。如果不是的话,假设你至少有一个双核处理器,那么只需简单地使用2个线程就可以将时间缩短一半。

另一件事看是你如何访问您的数据,如果你正在做大型阵列处理,以确保您访问数据依次的,因为它是存储在内存中,避免冲洗的L1/L2缓存在每次迭代中(在较小的L1缓存中可以看到这种差异,这种差异可能很大)。

再一次,我会先看看循环内部是什么,大部分增益(> 99%)将会在哪里,而不是外部循环管道。但是,如果你的循环代码是I/O绑定的,那么在优化上花费的任何时间都是浪费的。

1

顺便说一句,是不是很好用short,而不是int在循环,如果Int16的能力是保证足够?