2009-04-13 60 views
2

考慮一下:爲什麼列表<T> .ForEach比標準的foreach更快?

必要的:

//The alphabet from a-z 
List<char> letterRange = Enumerable.Range('a', 'z' - 'a' + 1) 
.Select(i => (Char)i).ToList(); //97 - 122 + 1 = 26 letters/iterations 

標準的foreach:

foreach (var range in letterRange) 
{ 
    Console.Write(range + ","); 
} 
Console.Write("\n"); 

內置的foreach:

letterRange.ForEach(range => Console.Write(range + ",")); //delegate(char range) works as well 
Console.Write("\n"); 

我試圖定時他們對彼此和內置的foreach速度提高2倍,這看起來很多。

我搜索了一下,但我似乎無法找到任何答案。

此外,關於:In .NET, which loop runs faster, 'for' or 'foreach'?

for (int i = 0; i < letterRange.Count; i++) 
{ 
    Console.Write(letterRange[i] + ","); 
} 
Console.Write("\n"); 

不採取行動執行比標準的foreach快,據我可以告訴。

回答

16

我認爲你的基準是有缺陷的。 Console.Write是一個I/O綁定任務,它是基準測試中耗時最多的部分。這是一個微觀基準,應該非常仔細地做出準確的結果。

這裏是一個基準:http://diditwith.net/PermaLink,guid,506c0888-8c5f-40e5-9d39-a09e2ebf3a55.aspx(它看起來不錯,但我沒有自己驗證它)。 該鏈接似乎被破壞,截至2015年8月14日

+0

我相信你是對的。你能提供一些適當的基準測試結果嗎? – CasperT

+0

相反,創建一個具有足夠的初始容量來保存整個結果並將每個字符串附加到該字符串的stringbuilder。然後輸出所有內容一次,然後停止計時器。 –

+0

啊,我沒有注意到那篇文章的功能列表 .ForEach基準以及。謝謝 – CasperT

2

這是因爲foreach方法沒有使用枚舉器。普查員(的foreach)往往for循環慢則一個基本的:

下面是對foreach方法的代碼:

public void ForEach(Action<T> action) 
{ 
    if (action == null) 
    { 
     ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); 
    } 
    for (int i = 0; i < this._size; i++) 
    { 
     action(this._items[i]); 
    } 
} 

雖然我認爲會有一個區別,我這是一個有點意外就像你指出的那樣大。使用枚舉器方法,您正在創建一個額外的對象,然後執行額外的步驟以確保枚舉器不失效(集合被修改)。你也要通過一個額外的函數調用Current()來獲得成員。所有這些增加了時間。

10

當您進入foreach循環時,您將枚舉每個項目。該枚舉會導致每次迭代發生兩次方法調用:一次調用IEnumerator<T>.MoveNext(),另一次調用IEnumerator<T>.Current。這是兩條call IL指令。

更快,因爲它只有一次方法調用每次迭代 - 無論您提供Action<T>代表是什麼。 這是一條callvirt IL指令。這比兩個call指令快得多。

正如其他人指出的那樣,IO-bound指令如Console.WriteLine()會污染你的基準。做一些可以完全侷限於記憶的東西,比如將序列的元素添加到一起。