|
|
|
|
|
asp.net(c#)中foreach和for循環(huán)的性能差異表現(xiàn)在它們的速度快慢上,在這篇文章中,我們探討一下這兩者的細(xì)微差異。
讓我們看一下下面的代碼:
foreach (var item in Enumerable.Range(0, 128))
{
Console.WriteLine(item);
}
foreach
在編譯器中會被轉(zhuǎn)換為以下代碼:
IEnumerator<int> enumerator = Enumerable.Range(0, 128).GetEnumerator();
try
{
while (enumerator.MoveNext())
{
int item = enumerator.Current;
Console.WriteLine(item);
}
}
finally
{
if (enumerator != null)
{
enumerator.Dispose();
}
}
可以看到,foreach
執(zhí)行的整個過程:
不過,它并不像聽起來那么容易。
C#/CLR 可能會在運(yùn)行時執(zhí)行優(yōu)化,使代碼運(yùn)行得更快。
數(shù)組是一種深度集成到 CLR 中的類型,CLR 為這種類型提供了許多優(yōu)化。FOREACH 循環(huán)是一個可迭代的實(shí)體,這是性能的一個關(guān)鍵方面。在本文后面,我們將討論如何在 Array.ForEach
靜態(tài)方法和 List.ForEach
方法的幫助下遍歷數(shù)組和列表。
static double ArrayForWithoutOptimization(int[] array)
{
int sum = 0;
var watch = Stopwatch.StartNew();
for (int i = 0; i < array.Length; i++)
sum += array[i];
watch.Stop();
return watch.Elapsed.TotalMilliseconds;
}
static double ArrayForWithOptimization(int[] array)
{
int length = array.Length;
int sum = 0;
var watch = Stopwatch.StartNew();
for (int i = 0; i < length; i++)
sum += array[i];
watch.Stop();
return watch.Elapsed.TotalMilliseconds;
}
static double ArrayForeach(int[] array)
{
int sum = 0;
var watch = Stopwatch.StartNew();
foreach (var item in array)
sum += item;
watch.Stop();
return watch.Elapsed.TotalMilliseconds;
}
static double ArrayForEach(int[] array)
{
int sum = 0;
var watch = Stopwatch.StartNew();
Array.ForEach(array, i => { sum += i; });
watch.Stop();
return watch.Elapsed.TotalMilliseconds;
}
測試條件:
該圖顯示 FOR
和 FOREACH
在遍歷數(shù)組時花費(fèi)相同的時間。也是因?yàn)镃LR優(yōu)化將FOREACH
轉(zhuǎn)換為FOR
,并以數(shù)組的長度作為最大迭代邊界。不管數(shù)組長度是否緩存(使用FOR
時),結(jié)果都差不多。
聽起來可能很奇怪,但是緩存數(shù)組長度可能會影響性能。在使用Array.Length
作為迭代邊界時,JIT 測試索引是否命中循環(huán)之外的右邊界。此檢查僅執(zhí)行一次。
破壞這種優(yōu)化非常容易。緩存變量的情況幾乎沒有優(yōu)化。
Array.foreach
展示了最差的結(jié)果。它的實(shí)現(xiàn)非常簡單:
public static void ForEach<T>(T[] array, Action<T> action)
{
for (int index = 0; index < array.Length; ++index)
action(array[index]);
}
那為什么它運(yùn)行這么慢?它在引擎蓋下使用 FOR
。原因在于調(diào)用 ACTION 委托。事實(shí)上,每次迭代都會調(diào)用一個方法,這會降低性能。此外,委托的調(diào)用速度沒有我們希望的那么快。
結(jié)果完全不同。迭代列表時,FOR
和 FOREACH
顯示不同的結(jié)果。沒有優(yōu)化。FOR
(緩存列表長度)顯示最佳結(jié)果,而 FOREACH
慢 2 倍以上。這是因?yàn)樗诤笈_處理 MoveNext
和 Current
。List.ForEach
和 Array.ForEach
顯示最差的結(jié)果。代表總是被虛擬調(diào)用。此方法的實(shí)現(xiàn)如下所示:
public void ForEach(Action<T> action)
{
int num = this._version;
for (int index = 0; index < this._size && num == this._version; ++index)
action(this._items[index]);
if (num == this._version)
return;
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
每次迭代都會調(diào)用 Action
委托。它還檢查列表是否更改,如果更改,則拋出異常。
List
內(nèi)部使用基于數(shù)組的模型,而 ForEach
方法使用數(shù)組索引進(jìn)行迭代,這比使用索引器快得多。
FOR
循環(huán)和 FOREACH
在數(shù)組上的工作比使用長度緩存的 FOR
稍快。Foreach
性能 比 FOR
/ FOREACH
性能慢大約 6 倍。FOR
循環(huán)在列表上的運(yùn)行速度要慢 3 倍。FOR
循環(huán)在列表上的運(yùn)行速度要慢 2 倍。FOREACH
循環(huán)在列表上的運(yùn)行速度要慢 6 倍。這是列表的排行榜:
對于數(shù)組:
事實(shí)證明,FOREACH
在數(shù)組上比 FOR
的長度追蹤更快。在列表結(jié)構(gòu)上,FOREACH
比 FOR
慢。
使用 FOREACH
時代碼看起來更好,并且現(xiàn)代處理器允許使用它。但是,如果你需要高度優(yōu)化你的代碼庫,最好使用 FOR
。
文章推薦