溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Mac OS X NSArray 枚舉性能研究的示例分析

發(fā)布時(shí)間:2021-12-31 14:21:21 來(lái)源:億速云 閱讀:138 作者:柒染 欄目:編程語(yǔ)言

Mac OS X NSArray 枚舉性能研究的示例分析,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。

一天,我在思考 NSArray 枚舉方法 (也稱迭代方法): Mac OS X 10.6 和 iOS 4 帶來(lái)了以塊(block)組成的美麗新世界,enumerateObjectsUsingBlock: 方法隨之而來(lái)。我感覺(jué)這個(gè)方法要慢于快速枚舉 (for (object in array) { ... }),因?yàn)橛锌傮w開(kāi)銷,但我并不能確定。因此我決定做一次性能測(cè)評(píng)。

都有哪些枚舉方法?

總體來(lái)說(shuō),我們有4種可以使用的枚舉方法 (參考 Mike Ash 的 周五常見(jiàn)問(wèn)題 2010-04-09: Objective-C 的枚舉方法對(duì)比)。

1、objectAtIndex: enumeration 使用一個(gè) for 循環(huán),遞增循環(huán)變量,然后用 [myArray objectAtIndex:index] 來(lái)訪問(wèn)元素。這是最基本的枚舉形式。

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

2、NSEnumerator 外部迭代(external iteration)的形式: [myArray objectEnumerator] 返回一個(gè)對(duì)象,這個(gè)對(duì)象有  nextObject 方法。我們可以循環(huán)調(diào)用這個(gè)方法,直到返回 nil 為止。

NSEnumerator *enumerator = [myArray objectEnumerator];  id object;  while (object = [enumerator nextObject]) {      [self doSomethingWith:object];  }

3、NSFastEnumerator The idea behind 快速枚舉 的思想是利用 C 數(shù)組快速訪問(wèn) 來(lái)優(yōu)化迭代。不僅它理論上比傳統(tǒng)的  NSEnumerator 更快,而且 Objective-C 2.0 提供了這種簡(jiǎn)明的語(yǔ)法:

id object;  for (object in myArray) {      [self doSomethingWith:object];  }

4、Block enumeration(塊枚舉)引入 blocks 后出現(xiàn)的方法,它可以基于塊來(lái)迭代訪問(wèn)一個(gè)數(shù)組。它的語(yǔ)法沒(méi)有快速枚舉那么簡(jiǎn)潔,但它有一個(gè)有趣的特性: 并發(fā)枚舉。如果枚舉的順序并不重要,而且實(shí)施的處理可以并發(fā)進(jìn)行,不用鎖,這種方法可以在多核系統(tǒng)上帶來(lái)相當(dāng)明顯的效率提升。詳情參考 并發(fā)枚舉一節(jié)。

[myArray enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {      [self doSomethingWith:object];  }];  [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {      [self doSomethingWith:object];  }];

線性枚舉

首先,我們討論一下線性枚舉:一個(gè)項(xiàng)目接著前一個(gè)。

圖表

Mac OS X NSArray 枚舉性能研究的示例分析

結(jié)論

有一點(diǎn)令人驚訝的是,NSEnumerator甚至比使用objectAtIndex:還慢。這對(duì)于Mac OS X 和IOS是一個(gè)事實(shí)。我猜想這是由于枚舉器在每次迭代時(shí)都去檢查數(shù)組是否被修改。自然地,快速枚舉保存了每個(gè)原始的名字,因此是最快的解決方案。

對(duì)于小的數(shù)組,block enumeration 比objectAtIndex:稍慢一點(diǎn),但在有大量元素的數(shù)組里,它的性能變得與fast enumeration差不多快。

fast enumeration和NSEnumeration之間的區(qū)別在很多地方已經(jīng)非常明顯:對(duì)于iPhone 4S,前者花費(fèi)約0.037秒而后者需要0.140秒。這已經(jīng)相差了3.7陪。

奇怪的一點(diǎn)

***在程序中分配 NSArray 和***用objectEnumerator 獲取 enumerator 都需要異常長(zhǎng)的時(shí)間才能完成。例如,在我 2007 年的 17 寸 MacBook Pro 上分配含一個(gè)元素的數(shù)組,所需時(shí)間的中位數(shù)是 415 納秒。但***分配的時(shí)候會(huì)需要 500,000 納秒,有時(shí)甚至要到 1,000,000 納秒!獲取 enumerator 也是如此:盡管中位數(shù)只有 673 納秒,***獲取卻要花 500,000 納秒以上。

我只能猜測(cè)其中的原因,但我懷疑延遲加載是罪魁禍?zhǔn)?。在?shí)際應(yīng)用中,你可能不會(huì)注意到這一點(diǎn),因?yàn)榈鹊綀?zhí)行你的代碼時(shí),Cocoa 或 Cocoa Touch 很可能已經(jīng)創(chuàng)建過(guò)數(shù)組了。

并發(fā)枚舉

如果情況允許,你可以選擇用塊枚舉來(lái)并發(fā)枚舉對(duì)象。這意味著計(jì)算的工作量可以分散到幾個(gè) CPU 內(nèi)核上。并不是每種枚舉過(guò)程中的處理都是可并發(fā)的,因此只有沒(méi)用到鎖的時(shí)候,才能使用并發(fā)枚舉:要么每一步操作確實(shí)是絕對(duì)相互獨(dú)立的,要么有原子性的操作可用 (如 OSAtomicAdd32 之類)。

那么,它相比其他枚舉類型有多大優(yōu)勢(shì)呢?

圖表

Mac OS X NSArray 枚舉性能研究的示例分析

結(jié)論

元素不多時(shí),并發(fā)枚舉是目前最慢的方法。主要原因可能是為了讓數(shù)組能并發(fā)訪問(wèn)而做的準(zhǔn)備工作和開(kāi)啟線程(我不知道用的是 GCD 還是“傳統(tǒng)的”線程,這不重要;這是我們不需關(guān)心的實(shí)現(xiàn)細(xì)節(jié))。

盡管如此,如果數(shù)組足夠大,并發(fā)枚舉突然就成了最快的方法了,正如我們所料。在 iPhone 4S 上枚舉 100 萬(wàn)個(gè)元素,用并發(fā)枚舉需要 0.024 秒,但快速枚舉需要 0.036 秒。相形之下,還是同一個(gè)數(shù)組,NSEnumeration 要用 0.139 秒! 這已經(jīng)是非常大的差距了,足有 5.7 倍之多。

在我的辦公室,2011 iMac 24"采用了酷睿i7四核CPU,同時(shí)在0.0016秒之內(nèi)列舉了百萬(wàn)項(xiàng)。同一數(shù)組快速枚舉了0.0044秒和NSEnumeration o.oo93秒。那個(gè)因數(shù)是5.8,它非常接近于ipone 4S的結(jié)果。在這里,我期待一個(gè)更大的差異,雖然,在我的2007 MacBook采用了Core2 Duo雙核CPU,在這里因數(shù)剛好是3.7.當(dāng)同時(shí)枚舉的閾值成為有用,在某處以我的測(cè)試是10,000和50,000分子之間。用更少的分子元素,去掉正常的塊迭代。

分配方式

我也想知道枚舉的性能會(huì)不會(huì)受數(shù)組創(chuàng)建方式的影響。我測(cè)試了兩個(gè)不同的方法:

  1. 首先創(chuàng)建一個(gè) C 數(shù)組,里面引用了數(shù)組元素的對(duì)象實(shí)例,然后再用 initWithObjects:count: 創(chuàng)建NSArray。

  2. 直接創(chuàng)建 NSMutableArray 并依次用 addObject: 添加對(duì)象。

結(jié)果是迭代過(guò)程的沒(méi)有區(qū)別,但分配過(guò)程有所不同:initWithObjects:count: 快一些。數(shù)組元素很多時(shí),差距更加顯著。這個(gè)例子創(chuàng)建了一個(gè)元素為 NSNumber 的數(shù)組:

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;  }

Mac OS X NSArray 枚舉性能研究的示例分析

我是如何來(lái)測(cè)量的?

你可以從 http://darkdust.net/files/arraytest.m 來(lái)下載這個(gè)測(cè)試應(yīng)用 看看我是如何來(lái)測(cè)量的?;旧衔揖褪菧y(cè)量重復(fù)迭代一個(gè)數(shù)組(什么處理也不做)1000次需要多長(zhǎng)時(shí)間。在圖表中,取每個(gè)數(shù)組尺寸的平均值。這個(gè)應(yīng)用的編譯選項(xiàng)是關(guān)閉優(yōu)化(-O0)。對(duì)于 iOS,我是在一個(gè) iPhone 4S 上進(jìn)行的測(cè)試。對(duì) MAC OS X,我用我家里2007年產(chǎn)的 MacBook Pro 17”和我辦公室2011年產(chǎn)的 iMac 24”來(lái)測(cè)試。MAC OS X的圖表顯示的是iMac上的結(jié)果,在MacBook Pro上的圖表看起來(lái)與此相似,只是更慢一些。

關(guān)于Mac OS X NSArray 枚舉性能研究的示例分析問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI