Mac OS X NSArray 枚举性能研究

开发 项目管理 后端
一天,我在思考 NSArray 枚举方法 (也称迭代方法): Mac OS X 10.6 和 iOS 4 带来了以块(block)组成的美丽新世界,enumerateObjectsUsingBlock: 方法随之而来。

一天,我在思考 NSArray 枚举方法 (也称迭代方法): Mac OS X 10.6 和 iOS 4 带来了以块(block)组成的美丽新世界,enumerateObjectsUsingBlock: 方法随之而来。我感觉这个方法要慢于快速枚举 (for (object in array) { ... }),因为有总体开销,但我并不能确定。因此我决定做一次性能测评。

都有哪些枚举方法?

总体来说,我们有4种可以使用的枚举方法 (参考 Mike Ash 的 周五常见问题 2010-04-09: Objective-C 的枚举方法对比)。

1、objectAtIndex: enumeration 使用一个 for 循环,递增循环变量,然后用 [myArray objectAtIndex:index] 来访问元素。这是最基本的枚举形式。

  1. NSUInteger count = [myArray count];  
  2. for (NSUInteger index = 0; index < count ; index++) {  
  3.     [self doSomethingWith:[myArray objectAtIndex:index]];  
  4. }  

2、NSEnumerator 外部迭代(external iteration)的形式: [myArray objectEnumerator] 返回一个对象,这个对象有  nextObject 方法。我们可以循环调用这个方法,直到返回 nil 为止。

  1. NSEnumerator *enumerator = [myArray objectEnumerator];  
  2. id object;  
  3. while (object = [enumerator nextObject]) {  
  4.     [self doSomethingWith:object];  
  5. }  

3、NSFastEnumerator The idea behind 快速枚举 的思想是利用 C 数组快速访问 来优化迭代。不仅它理论上比传统的  NSEnumerator 更快,而且 Objective-C 2.0 提供了这种简明的语法:

  1. id object;  
  2. for (object in myArray) {  
  3.     [self doSomethingWith:object];  

4、Block enumeration(块枚举)引入 blocks 后出现的方法,它可以基于块来迭代访问一个数组。它的语法没有快速枚举那么简洁,但它有一个有趣的特性: 并发枚举。如果枚举的顺序并不重要,而且实施的处理可以并发进行,不用锁,这种方法可以在多核系统上带来相当明显的效率提升。详情参考 并发枚举一节

  1. [myArray enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {  
  2.     [self doSomethingWith:object];  
  3. }];  
  4. [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {  
  5.     [self doSomethingWith:object];  
  6. }]; 

线性枚举

首先,我们讨论一下线性枚举:一个项目接着前一个。

图表

 

结论

有一点令人惊讶的是,NSEnumerator甚至比使用objectAtIndex:还慢。这对于Mac OS X 和IOS是一个事实。我猜想这是由于枚举器在每次迭代时都去检查数组是否被修改。自然地,快速枚举保存了每个原始的名字,因此是最快的解决方案。

对于小的数组,block enumeration 比objectAtIndex:稍慢一点,但在有大量元素的数组里,它的性能变得与fast enumeration差不多快。

fast enumeration和NSEnumeration之间的区别在很多地方已经非常明显:对于iPhone 4S,前者花费约0.037秒而后者需要0.140秒。这已经相差了3.7陪。

奇怪的一点

***在程序中分配 NSArray 和***用objectEnumerator 获取 enumerator 都需要异常长的时间才能完成。例如,在我 2007 年的 17 寸 MacBook Pro 上分配含一个元素的数组,所需时间的中位数是 415 纳秒。但***分配的时候会需要 500,000 纳秒,有时甚至要到 1,000,000 纳秒!获取 enumerator 也是如此:尽管中位数只有 673 纳秒,***获取却要花 500,000 纳秒以上。

我只能猜测其中的原因,但我怀疑延迟加载是罪魁祸首。在实际应用中,你可能不会注意到这一点,因为等到执行你的代码时,Cocoa 或 Cocoa Touch 很可能已经创建过数组了。

并发枚举

如果情况允许,你可以选择用块枚举来并发枚举对象。这意味着计算的工作量可以分散到几个 CPU 内核上。并不是每种枚举过程中的处理都是可并发的,因此只有没用到锁的时候,才能使用并发枚举:要么每一步操作确实是绝对相互独立的,要么有原子性的操作可用 (如 OSAtomicAdd32 之类)。

那么,它相比其他枚举类型有多大优势呢?

图表

 

结论

元素不多时,并发枚举是目前最慢的方法。主要原因可能是为了让数组能并发访问而做的准备工作和开启线程(我不知道用的是 GCD 还是“传统的”线程,这不重要;这是我们不需关心的实现细节)。

尽管如此,如果数组足够大,并发枚举突然就成了最快的方法了,正如我们所料。在 iPhone 4S 上枚举 100 万个元素,用并发枚举需要 0.024 秒,但快速枚举需要 0.036 秒。相形之下,还是同一个数组,NSEnumeration 要用 0.139 秒! 这已经是非常大的差距了,足有 5.7 倍之多。

在我的办公室,2011 iMac 24"采用了酷睿i7四核CPU,同时在0.0016秒之内列举了百万项。同一数组快速枚举了0.0044秒和NSEnumeration o.oo93秒。那个因数是5.8,它非常接近于ipone 4S的结果。在这里,我期待一个更大的差异,虽然,在我的2007 MacBook采用了Core2 Duo双核CPU,在这里因数刚好是3.7.当同时枚举的阈值成为有用,在某处以我的测试是10,000和50,000分子之间。用更少的分子元素,去掉正常的块迭代。

#p#

分配方式

我也想知道枚举的性能会不会受数组创建方式的影响。我测试了两个不同的方法:

  1. 首先创建一个 C 数组,里面引用了数组元素的对象实例,然后再用 initWithObjects:count: 创建NSArray。

  2. 直接创建 NSMutableArray 并依次用 addObject: 添加对象。

结果是迭代过程的没有区别,但分配过程有所不同:initWithObjects:count: 快一些。数组元素很多时,差距更加显著。这个例子创建了一个元素为 NSNumber 的数组:

  1. NSArray *generateArrayMalloc(NSUInteger numEntries) {  
  2.     id *entries;  
  3.     NSArray *result;  
  4.           
  5.     entries = malloc(sizeof(id) * numEntries);  
  6.     for (NSUInteger i = 0; i < numEntries; i++) {  
  7.         entries[i] = [NSNumber numberWithUnsignedInt:i];  
  8.     }  
  9.       
  10.     result = [NSArray arrayWithObjects:entries count:numEntries];  
  11.       
  12.     free(entries);  
  13.     return result;  

 

我是如何来测量的?

你可以从 http://darkdust.net/files/arraytest.m 来下载这个测试应用 看看我是如何来测量的。基本上我就是测量重复迭代一个数组(什么处理也不做)1000次需要多长时间。在图表中,取每个数组尺寸的平均值。这个应用的编译选项是关闭优化(-O0)。对于 iOS,我是在一个 iPhone 4S 上进行的测试。对 MAC OS X,我用我家里2007年产的 MacBook Pro 17”和我办公室2011年产的 iMac 24”来测试。MAC OS X的图表显示的是iMac上的结果,在MacBook Pro上的图表看起来与此相似,只是更慢一些。

英文原文:NSArray enumeration performance examined

原文链接:http://www.oschina.net/translate/nsarray-enumeration-performance

责任编辑:林师授 来源: 开源中国社区 编译
相关推荐

2012-02-17 09:21:22

Mac OS X

2009-09-01 12:59:06

雪豹苹果操作系统

2012-02-17 14:41:12

Mac OS XOS XiOS

2011-10-20 20:56:42

UbuntuMac OS X

2013-06-07 14:35:19

Mac OS X

2011-07-22 09:06:01

Mac OS X Li

2012-02-02 10:30:30

Mac OS X 10正式发布

2010-01-11 09:58:33

谷歌苹果Chrome OS

2011-06-21 11:04:55

Linus TorvaMac OS X

2011-11-16 09:47:54

2014-12-24 09:11:53

Mac OS X Se

2013-02-26 09:14:52

2016-12-13 10:22:54

Mac OS X显示服务

2009-05-14 17:31:20

Ubuntu 9.04Mac OS X 10

2011-09-28 10:12:06

Mac OS X

2009-10-27 14:50:47

Mac OS X 10

2010-03-31 13:37:38

Ubuntu 10.0

2009-03-23 09:57:19

2011-07-08 09:23:45

Mac OS X Li

2011-08-11 13:59:53

Mac OS X
点赞
收藏

51CTO技术栈公众号