2011-01-11 144 views
2

這個問題來自我問我關於foreach循環的最後一個問題的原因。我有一個很大的字符串數組(比如說數千),我想遍歷數組,並且能夠根據某種條件分解出來,而且我需要最佳性能。更快的循環方法('for'和'foreach')?

一些示例代碼:

for(int i = 0; i < array.length && flag == true; i++){ 
    //Processing and set flag 
} 

//..or 

foreach(string item in array){ 
    //processing...set flag 
    if(!flag) 
     break; 
} 

哪種方式更便宜?

+3

老實說,這不會有問題,你的性能問題將在別處 – BrokenGlass 2011-01-11 18:23:43

+1

你有沒有嘗試過自己測量它? – 2011-01-11 18:24:21

+0

看看這個:http://stackoverflow.com/questions/1124753/for-vs-foreach-loop-in-c – 2011-01-11 18:24:36

回答

5

您可以隨時對它們進行基準測試。使用Stopwatch並迭代,例如,重複1000萬次以查看哪個更快。

我想你會發現,雖然是兩個都幾乎相同因爲JIT編譯器的陣列基本上一個for上優化foreach

flevine100實際上是正確的,一般一個for是略微foreach該類型的方法GetEnumerator創建實現IEnumeratorIEnumerator<T>一個新的對象更有效(由於存儲器分配和方法調用開銷);然而,System.Collections.Generic中大多數收集的情況不是這樣,但是由於其明確的IEnumerable實現使用值類型枚舉器(更不用說the foreach construct does not actually require an IEnumerable implementation in the first place)。

它甚至是更少數組的具體情況,因爲它們是固定大小的,因此無法通過JIT編譯器進行優化。

2

我發現(...)比foreach()更快。我認爲這是因爲foreach()使用IEnumerable管道。

由於您關心速度......在.NET 4.0中如果您的循環體不依賴於共享狀態,則應該使用Parallel.For或Parallel.Foreach將處理擴展到多個處理器上。

+1

有趣的是 - 我的測試(僅僅爲了好玩)顯示了與數組*相反的 - foreach *通常在我的測試中勝過。這就是說,從實際的角度來看,IMO沒有「真正的」區別。 (另外,這些類型的基準測試通常存在可怕的缺陷,因爲太多依賴於平臺和構建設置......) – 2011-01-11 18:27:40

1

沒有基準測試,如果兩者之間存在顯着差異(當然,答案高度依賴於循環內的工作和集合類型),我會非常驚訝。

根據我的經驗,這些類型的東西永遠不會在生產代碼中造成性能瓶頸。任何具有重大意義的應用程序無疑都會涉及某種類型的網絡交互,它們佔據了大部分性能損失。

如果您擔心,但我強烈建議分析有問題的代碼並找到更快的代碼。

2

我不會專注於這個級別的微優化。

機會是你有更好的優化機會,尤其是如果你正在處理字符串。 for/foreach差異將是整個運行時間的一小部分,它將執行基本相同的操作。

使算法儘可能「乾淨」,並尋找其他性能機會,如果需要,如線程化整個例程。

1

在第二個示例中,您沒有提前退出子句,但添加break代替您的標誌將實現該功能。

我不清楚內部,除了foreach使用枚舉器和for循環將取決於您的元素訪問器的可伸縮性。在列表中,一旦添加了中斷,它們實際上是平等的。

0

對於一個簡單的裸數組,for循環往往會產生稍小的IL。比較

static int[] array = new int[100]; 

    static void UseForLoop() { 
     for (int i = 0; i < array.Length; ++i) { 
      Console.WriteLine(array[i]); 
     } 
    } 

    static void UseForeachLoop() { 
     foreach (int i in array) { 
      Console.WriteLine(i); 
     } 
    } 

從2010 VS產生IL的下面套,默認釋放配置:

.method private hidebysig static void UseForLoop() cil managed 
{ 
     .maxstack 2 
     .locals init (
       [0] int32 i) 
     L_0000: ldc.i4.0 
     L_0001: stloc.0 
     L_0002: br.s L_0014 
     L_0004: ldsfld int32[] ConsoleApplication5.Program::array 
     L_0009: ldloc.0 
     L_000a: ldelem.i4 
     L_000b: call void [mscorlib]System.Console::WriteLine(int32) 
     L_0010: ldloc.0 
     L_0011: ldc.i4.1 
     L_0012: add 
     L_0013: stloc.0 
     L_0014: ldloc.0 
     L_0015: ldsfld int32[] ConsoleApplication5.Program::array 
     L_001a: ldlen 
     L_001b: conv.i4 
     L_001c: blt.s L_0004 
     L_001e: ret 
} 

.method private hidebysig static void UseForeachLoop() cil managed 
{ 
     .maxstack 2 
     .locals init (
       [0] int32 i, 
       [1] int32[] CS$6$0000, 
       [2] int32 CS$7$0001) 
     L_0000: ldsfld int32[] ConsoleApplication5.Program::array 
     L_0005: stloc.1 
     L_0006: ldc.i4.0 
     L_0007: stloc.2 
     L_0008: br.s L_0018 
     L_000a: ldloc.1 
     L_000b: ldloc.2 
     L_000c: ldelem.i4 
     L_000d: stloc.0 
     L_000e: ldloc.0 
     L_000f: call void [mscorlib]System.Console::WriteLine(int32) 
     L_0014: ldloc.2 
     L_0015: ldc.i4.1 
     L_0016: add 
     L_0017: stloc.2 
     L_0018: ldloc.2 
     L_0019: ldloc.1 
     L_001a: ldlen 
     L_001b: conv.i4 
     L_001c: blt.s L_000a 
     L_001e: ret 
} 

..但有關鍵部位,環,基本上是相同的。正如其他人所說,這也是一種微觀優化。來自這兩種方法的JIT'd x86可能會相同,除非您使用複雜的枚舉器遍歷複雜集合,但即使在實際示例中,差異也不大。

我會使用一個更易讀 - 如果速度是真的那麼多的問題,贊成for循環,但你可能會從算法優化得到更好的結果。