有一天,我在考虑 NSArray 列举办法 (也称迭代办法): Mac OS X 10.6 和 iOS 4 带来了以块(block)构成的漂亮新天下,enumerateObjectsUsingBlock: 办法随之而来。我觉得这个办法要慢于疾速列举 (for (object in array) { ... }),由于有整体开支,但我其实不能断定。因而我决议做一次功能测评。
整体来讲,我们有4种可使用的列举办法 (参考 Mike Ash 的 周五常用问题 2010-04-09: Objective-C 的列举办法比照)。
objectAtIndex: enumeration
运用一个 for 轮回,递增轮回变量,然后用 [myArray objectAtIndex:index] 来拜访元素。这是最根本的列举方式。
NSUInteger count = [myArray count]; for (NSUInteger index = 0; index < count ; index++) { [self doSomethingWith:[myArray objectAtIndex:index]]; }
NSEnumerator
内部迭代(external iteration)的方式: [myArray objectEnumerator] 前往一个工具,这个工具有 nextObject 办法。我们可以轮回挪用这个办法,直到前往 nil 为止。
NSEnumerator *enumerator = [myArray objectEnumerator]; id object; while (object = [enumerator nextObject]) { [self doSomethingWith:object]; }
NSFastEnumerator
The idea behind 疾速列举 的思惟是应用 C 数组疾速拜访 来优化迭代。不只它实际上比传统的 NSEnumerator 更快,并且 Objective-C 2.0 供给了这类简明的语法:
id object; for (object in myArray) { [self doSomethingWith:object]; }
Block enumeration(块列举)
引入 blocks 后呈现的办法,它可以基于块来迭代拜访一个数组。它的语法没有疾速列举那末简练,但它有一个风趣的特征: 并发列举。假如列举的次序其实不主要,并且施行的处置可以并发实行,不必锁,这类办法可以在多核零碎上带来相当分明的效力晋升。概况参考 并发列举一节。
[myArray enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) { [self doSomethingWith:object]; }]; [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [self doSomethingWith:object]; }];
起首,我们会商一下线性列举:一个项目接着前一个。
fast enumeration和NSEnumeration之间的差别在非常多地方曾经十分分明:关于iPhone 4S,前者破费约0.037秒然后者需求0.140秒。这曾经相差了3.7陪。
其它翻译版本 (1) 加载中初次在顺序平分配 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份子之间。用更少的份子元素,去掉正常的块迭代。
其它翻译版本 (1) 加载中我也想晓得列举的功能会不会受数组创立方法的影响。我测试了两个分歧的办法:
起首创立一个 C 数组,外面援用了数组元素的工具实例,然后再用 initWithObjects:count: 创立NSArray。
间接创立 NSMutableArray 并顺次用 addObject: 添加工具。
后果是迭代进程的没有差别,但分派进程有所分歧:initWithObjects:count: 快一些。数组元素非常多时,差距愈加明显。这个例子创立了一个元素为 NSNumber 的数组:
NSArray *generateArrayMalloc(NSUInteger numEntries) { id *entries; NSArray *result; entries = malloc(sizeof(id) * numEntries); for (NSUInteger i = 0; i < numEntries; i++) { entries[i] = [NSNumber numberWithUnsignedInt:i]; } result = [NSArray arrayWithObjects:entries count:numEntries]; free(entries); 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上的图表看起来与此类似,只是更慢一些。
本文中的一切译文仅用于进修和交换目标,转载请务必注明文章译者、出处、和本文链接。 2KB翻译任务按照 CC 协定,假如我们的任务有进犯到您的权益,请实时联络我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务