2012-07-11 50 views
4

我知道有相當多的話題似乎是關於完全相同的東西,但我沒有找到真正與我想要的有關的話題。快速枚舉比嵌套枚舉中的for循環慢(帶有測試結果)?

所以我很好奇,想要比較快速枚舉到NSEnumerator和for循環的性能。 (這是要求相當頻繁的部分)

首先,我比較快速計數:

for(NSNumber *number in testArray) 
{ 
    assert(number); 
} 

NSEnumerator:

NSEnumerator *enumerator = [testArray objectEnumerator]; 
NSNumber *number; 
while (number = [enumerator nextObject]) 
{ 
    assert(number); 
} 

for循環:

for(NSUInteger i = 0; i < [testArray count]; i++) 
{ 
    NSNumber *number = [testArray objectAtIndex:i]; 
    assert(number); 
} 

testArray是一個由0到1,000,000的NSNumbers組成的數組,我在ea後100次運行測試並且計算每個測試的平均運行時間。

我也跑了他們對我的iPad 2

結果:(指所有100個運行時間)

  • 0.042687s快速計數
  • 0.582072s NSEnumerator
  • 0.627318s for-loop

正如預期的那樣,快速計數是迄今爲止速度最快,而NSEnumerator仍然是一個有點快於for循環,但是這是枚舉退出大陣

因此,這裏的不那麼頻繁的問題:

其實我感興趣的是別的東西:枚舉陣列中的相互每個對象

與一個嵌套循環第一次嘗試比較數組中:

for(int i = 0; i < [testArray count]-1; i++) 
{ 
    NSNumber *number = [testArray objectAtIndex:i]; 
    for(int j = i+1; j < [testArray count]; j++) 
    { 
     NSNumber *innerLoopNumber = [testArray objectAtIndex:j]; 
     assert(innerLoopNumber); 
     assert(number); 
    } 
} 

對於這些測試,我必須減少數組的大小和運行的次數,以便在合理的時間內完成它們,因爲迭代次數隨着O(n^2)增長而增加。 所以我跑了他們與5.000 NSNumbers數組,並重複測試5次。

結果:7.360645s 1運行

所以我想,當然,快速列舉應該會更快。但要實現三角模式,以避免兩次比較每個元素對,我不得不在外環與NSEnumerator在內環

for(NSNumber *number in testArray) 
{ 
    NSEnumerator *reverseEnumterator = [testArray reverseObjectEnumerator]; 
    NSNumber *innerLoopNumber = reverseEnumterator.nextObject; 
    while(innerLoopNumber && ![innerLoopNumber isEqualToNumber:number]) 
    { 
     innerLoopNumber = reverseEnumterator.nextObject; 
     assert(innerLoopNumber); 
     assert(number); 
    } 
} 

讓我吃驚混合快速計數,這是慢得多:18。086980s 1個運行

我然後試圖混合版本,以及,使用快速枚舉的外環和一個for循環用於內之一:

int counter = 0; 
for(NSNumber *number in testArray) 
{ 
    for(int j = counter +1; j < [testArray count]; j++) 
    { 
     NSNumber *innerLoopNumber = [testArray objectAtIndex:j]; 
     assert(innerLoopNumber); 
     assert(number); 
    } 
    counter++; 
} 

結果:7.079600s 1運行

只比普通的for-loop稍快。

在一個地方的數字:

  • 07.360645s for循環
  • 07.079600s混合
  • 18.086980s快速計數

所以我想,這是爲什麼?快速枚舉只在「未中斷」時才能正常工作,NSEnumerator的使用是否會干擾快速枚舉? 或者我只是錯過了一些東西,我的方法錯了?

+0

請注意,在'for'循環的每次迭代中,您都冗餘地調用一個方法'[testArray count]'。 – echristopherson 2012-07-11 21:03:04

+0

根據Apple的文檔,當使用for-loop:「在每個循環迭代中,它會調度一條消息來獲取數組中的項目數量,這是浪費的。如果數組中項目的數量永不改變,您可以分配該值的一個變量,並使用,而不是「 – user523234 2013-11-27 14:44:03

回答

3

您在快速枚舉循環中調用其他方法。 Objective-c具有非平凡的方法調用開銷,所以你的問題在於嵌套循環的設置。正如你所看到的,快速枚舉+ for循環比循環+ for循環更快,並且在這裏你避免了額外的方法調用。

+0

嗯,我明白你的觀點。平等檢查在其他結構中不是必需的。但是檢查是什麼使得n^2次迭代和(n^2)/ 2次迭代之間的差異。所以它將取決於循環中實際操作的成本,但在我看來,對於這種情況,嵌套的快速枚舉結構根本不適用。 – MeXx 2012-07-11 14:07:33

+0

儘量不要進行平等檢查,我敢打賭,快速枚舉的速度會比其他類型的檢查一半多兩倍,而沒有額外的調用開銷。 – Dustin 2012-07-11 14:10:52

+0

我試過了:15.004834s,比比較快了一點,但仍幾乎是其他解決方案的兩倍。如果我把真正的工作放在內部循環中,而不僅僅是斷言,它通過內部循環兩次的事實變得更加昂貴。我發現了一些更有趣的事情:在outerLoop中創建一個subarrayWithRange,然後快速枚舉這個實現相同的事情,但比2.626523s更快,比for循環快兩倍以上雙快速枚舉 – MeXx 2012-07-11 14:45:30