2010-10-24 81 views
1

我有一个应用程序遍历数组的每一步,当数组为空时,我似乎得到令人惊讶的慢结果。所以,我调查了一些后续测试,如下所示:Objective-C空阵列性能

NSMutableArray* ar = [NSMutableArray array]; 
double time = CFAbsoluteTimeGetCurrent(); 
for (int i = 0; i < 10000; i++) 
{ 
    for (NSObject* obj in ar) 
    { 
     [obj retain]; 
     [obj release]; 
    } 
} 
time = CFAbsoluteTimeGetCurrent() - time; 
printf("Empty Time: %1.12f", time/10000.0f); 

time = CFAbsoluteTimeGetCurrent(); 
for (int i = 0; i < 10000; i++) 
{ 
    if ([ar count] > 0) 
    { 
     for (NSObject* obj in ar) 
     { 
      [obj retain]; 
      [obj release]; 
     } 
    } 
} 
time = CFAbsoluteTimeGetCurrent() - time; 
printf("Checked Time: %1.12f", time/10000.0f); 

我试过100次| 1,000 |万个迭代周期,结果如下:

Empty Time: 0.000000039935   //100 
Checked Time: 0.000000020266  //100 
Empty Time: 0.000000018001   //1000 
Checked Time: 0.000000011027  //1000 
Empty Time: 0.000000015503   //10000 
Checked Time: 0.000000008899  //10000 

奇怪的是,这表明具有简单地计算检查显著提高了低迭代运行性能(可能是因为缓存方案)。这对我来说是绝对令人惊讶的,因为我期望Objective-C编译/运行时已经在执行foreach循环时执行此检查!有没有人有任何想法,为什么这可能是这种情况,如果有什么办法来挤出更多的性能出这个循环设置?谢谢!

回答

7

空数组在一个典型的Cocoa程序中不是很常见,也不会迭代数千次的空数组。

这将是非常令人惊讶的是有史以来看到空列阵列作为CPU周期的重要消费者出现在乐器中。

鉴于基金会和核心基金会针对现实世界的绩效模式进行了优化,因此没有进行0计数检查就不足为奇了。

但是,如果你真的必须遍历空数组一个bazillion倍,最快的方法是使用一个块:

time = CFAbsoluteTimeGetCurrent(); 
[ar enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 
    [obj retain]; 
    [obj release]; 
}]; 

我贴你的代码为基础工具的main(),并得到了该上相对近期的MacBook Pro:

 Empty Time: 0.000000019896 
    Checked Time: 0.000000007498 
    Block Time: 0.000000000298 

当然,不是空数组,只需使用nil。即我在ar = nil;之后第二次完成了所有测试。

ar = nil; 
time = CFAbsoluteTimeGetCurrent(); 
for (int i = 0; i < 10000; i++) 
{ 
    for (NSObject* obj in ar) 
    { 
     [obj retain]; 
     [obj release]; 
    } 
} 
... etc ... 


     Empty Time: 0.000000019902 
    Checked Time: 0.000000007999 
     Block Time: 0.000000000298 
    nil Empty Time: 0.000000015599 
nil Checked Time: 0.000000004703 
    nil Block Time: 0.000000000000 

,总的来说,如果你的数据结构是复杂的,你是敲着他们多在每一帧渲染,我建议不同的数据结构可能是为了。

当然,只有当您真的使用Instruments来对代码进行采样并且正在优化某些占用整个CPU周期很大比例的东西时。

+0

HOLY POO!该块的实现将执行时间缩短了100倍!它必须使用GCD来表现这种表现,是的?这非常不可思议,我将不得不考虑这一点。谢谢!哦,是的,我正在构建一个游戏,所以遍历空数组会发生在每一帧,因此空数组的性能非常重要。幸运的是,计数检查没有我想象的那么糟糕(例如20行锅炉代码)。再次感谢! – Grimless 2010-10-24 20:48:11

+0

奇怪。我试过你的块实现,它实际上使执行时间增加了三倍!这是我得到的:检查时间:\t 0。000000009954空时间:\t \t 0.000000016987块时间:\t \t 0.000000037014。注意:这已经完成了1000次,所以有可能块创建实际上是造成这种情况的原因。编辑:是的,所以我摆脱了fori循环,并试图直接块运行,它减少了2倍的运行时间。好的解决方案 – Grimless 2010-10-24 20:56:35

+0

OOPS!忘了调整其他循环。是的,所以该块的实施将运行时间增加了3倍......哎哟。 – Grimless 2010-10-24 21:02:32

-2

for-in构造不是免费的,它必须解决某种枚举方法调用,所以报告的时间实际上是有意义的。在这种情况下,我会使用普通的C数组。如果你使用objc_msgsend()在这样的大循环中调用objc方法,你也会获得更好的性能。

+1

这个答案有两个问题,我认为没有帮助和/或危险。举例来说:由于数组的动态特性,我不能使用C数组;对象被一直添加到这些数组中并从中移除,使C数组的管理更加痛苦。其次:Objective-C引导明确说永远不要明确调用objc_msgsend(),我打算坚持。 – Grimless 2010-11-30 00:18:25