这个循环可以优化吗?

3

我正在进行iPhone应用程序的调试/优化阶段。我还有一个瓶颈 - 这是程序唯一出现明显延迟的地方,就在以下循环中:(顺便说一句,我已经使用字母和类型重命名了变量。(在实际应用程序中,真实名称更易读,但在上下文之外没有多少意义,所以我希望这足够清楚。)以下是循环:

for(i=0;i<xLong; i+=yFloat*zShort){
  aFloat=0.0;
  for(int j=i;j<i+yFloat*zShort;j++){
    aFloat=hArray[j]/kFloat;
  }
  bNSNumber = [NSNumber numberWithFloat:aFloat]; 
  [cNSMutableArray addObject:bNSNumber];
}

所有的反对创建和清理都在此循环之外。

(这里的情况应该很简单,但基本上我有一个非常大的数组(数百万个元素),我按照yFloat*zShort长度的块遍历该数组,在该块中添加所有元素,并将最终总和插入另一个数组中。因此,如果hArray长达一百万个元素,而我的块长度为200,我将对前200个元素求和,将该总和插入cNSMutableArray中,并继续移动到hArray中的下一个200个元素。最终,cNSMutableArray将有5000个元素。)

当外部循环约为25k,内部循环约为200时,此代码运行需要约4秒钟。我希望尽可能地减少运行时间,因为在现实世界中,外部循环可能会更大。

有什么想法可以加快速度吗?

感谢您提供的任何想法!

5个回答

8

你试过使用C语言风格的浮点数数组来替代NSMutableArray吗?创建那么多NSNumber对象会增加开销。


谢谢。最初数据长度是未知的,但现在已知,所以你是对的,将其转换回C风格数组是有意义的。我会这样做。 - Eric Christensen

6

首先,根据您的描述,内部循环应该如下:

for(int j=i;j<i+yFloat*zShort;j++){
    aFloat+=hArray[j]/kFloat;
}

无论如何,由于kFloat不会改变,因此您可以将其移出循环并进行一次除法操作:
for(int j=i;j<i+yFloat*zShort;j++){
    aFloat+=hArray[j];
}
aFloat/=kFloat;

话虽如此,这可能会影响最终值的准确性。不知道你具体在做什么,所以我不确定这是否重要。


1
啊哈!你发现了一个 bug!我实际上没有对块进行求和。显然,这是最初的意图,但看起来在某个时候,我停止了这样做,这意味着我只是每一步重置 aFloat,当然我不需要这样做。所以我完全删除了内部循环,只设置该块的第一个值为该值,并且时间现在是以前的四分之一。谢谢! - Eric Christensen
@Eric Christensen:通过删除功能进行优化是一种有趣的方法。我很好奇为什么使用第一个元素和块的平均值一样好? - e.James
你发布的代码实际上使用了块的最后一个值,如果这很重要的话,而不是第一个值。 - R Samuel Klatchko
@e.James:很多时候,点采样就足够了。一滴苹果汁的味道与整瓶苹果汁的平均味道相同。@RSam:没错,你说得对,但第一滴和最后一滴的味道是一样的,所以无所谓。谢谢。 - Eric Christensen

2

我看到您已经获得了很好的加速,但是我有一点建议:浮点数除法非常昂贵;您可以预先计算。

float invKFloat = 1.0f / kFloat;

然后乘以这个值,而不是除以 kFloat。这意味着你只需要在外部循环中进行一次除法,而不是每次都进行。


0

这似乎是应该在后台线程中进行的计算。

您有几个选择- NSOperation 是一种可行的替代方案,但根据您的数据结构,使用 detachNewThreadSelector:toTarget:withObject: 可能更容易。


很不幸,用户在循环完成之前没有其他事情可做,因此我认为将其分离出来没有意义。后台线程没有速度优势,对吗?只是可以同时做其他事情,对吧?谢谢。 - Eric Christensen
但如果 Objective-C 跟 Windows Forms 差不多的话,你的 GUI 会锁死,所以这样做可以让你制作一个进度条或者其他东西... - RCIX
是的,这就是我的意思。锁定GUI界面是非常不好的做法 - 最好显示一个(如果可能的话可取消的)进度条或一些指示工作正在进行中。 - sbooth
是的,我明白了。这个循环位于一个加载文件的更长脚本内。整个代码块都在自己的线程中运行,同时我有一个加载屏幕。除非有性能上的好处,否则我不认为需要再开启第三个线程。谢谢。 - Eric Christensen
在这种情况下,你是正确的,再生成一个线程是毫无意义的。 - sbooth

0

您真的应该避免在紧密循环内创建对象。每次这样做,都会在堆上分配一个新对象,这涉及哈希插入。


也许我漏掉了什么。据我所知,上面的代码并没有在循环中创建任何新对象,对吧? - Eric Christensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接