2012-03-16 69 views
2

這兩個工作都工作的方式,但我不知道是否有性能差異:性能和LINQ以迭代

Dim collection As ItemCollection = CType(CellCollection.Where(Function(i) i.IsPending = True), ItemCollection) 
For Each item As Item In collection 
    'Do something here 
Next 

For Each item As Item In CellCollection.Where(Function(i) i.IsPending = True) 
    'Do something here 
Next 

我認爲第二個是更好因爲你的變量更少,看起來更乾淨,但是第二個想法,我不太清楚當你在迭代中放入linq查詢時會發生什麼。

是否每次循環都要重新評估?哪一個是最乾淨/最高性能的?

在此先感謝。

+5

如果您對性能感興趣 - 測試它! – 2012-03-16 14:22:23

+0

爲什麼在VB中添加'= True'或者這是必需的? – 2012-03-16 14:25:02

+0

習慣問題我認爲 – Terry 2012-03-16 14:25:42

回答

2

我創建了一個簡單的測試控制檯應用程序。

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Text; 

namespace LinqPerformance 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var data = Enumerable.Range(1, 100000000); 

      for (int x = 0; x < 10; x++) 
      { 
       ExecuteMethods(data); 
      } 
     } 

     private static void ExecuteMethods(IEnumerable<int> data) 
     { 
      Method1("linq collection",() => 
      { 
       var collection = data.Where(d => d % 2 == 0); 
       double count = 0; 

       foreach (var c in collection) 
       { 
        count += c; 
       } 
      }); 

      Method1("list collection",() => 
      { 
       var collection = data.Where(d => d % 2 == 0).ToList(); 
       double count = 0; 
       foreach (var c in collection) 
       { 
        count += c; 
       } 
      }); 

      Method1("iterable collection",() => 
      { 
       double count = 0; 
       foreach (var c in data.Where(d => d % 2 == 0)) 
       { 
        count += c; 
       } 
      }); 
     } 

     private static void Method1(string name, Action body) 
     { 
      Stopwatch s = new Stopwatch(); 

      s.Start(); 
      body(); 
      s.Stop(); 

      Console.WriteLine(name + ": " + s.Elapsed); 
     } 
    } 
} 

運行後,我可以看到ToList()是最慢的。其他兩種方法似乎是相同的。

我想這是因爲在foreach擴展到

var enumerator = collection.GetEnumerator(); 

while(enumerator.MoveNext()) 
{ 
    var c = enumerator.Current; 
    count += c; 
} 
+0

這是最慢的,因爲你在數據上迭代1次,然後在「過濾」列表中再次迭代。除IEnumerable 的保持變量外,其他兩種方法相同。 – Malmi 2012-03-16 14:54:57

1

性能是一樣的,不管你指定LINQ查詢到一個變量,或者直接在每次調用它。在這兩種情況下,迭代器都將被創建一次,For Each循環將遍歷列表中的每個項目一次。

在第一個代碼示例中,雖然CType不是必需的(實際上我不認爲它會起作用)。你可以簡單地做:

Dim collection = CellCollection.Where(Function(i) i.IsPending = True) 
For Each item As Item In collection 
    'Do something here 
Next 

但正如我所說的,分配給一個變量是沒有必要的。在For Each行上具有Where子句將產生相同的性能,並且代碼將更短且更具可讀性。

0

兩者的表現相同。 foreach將創建IEnumerable(Of T)然後通過它枚舉。

不過,如果你擔心性能,請嘗試:

Dim collection As IEnumerable(Of Item) _ 
    = CellCollection.Where(Function(i) i.IsPending) 

For Each item As Item In collection 
    'Do something here 
Next 

這有可能是鑄造IEnumerable(Of Item)ItemCollection會導致它枚舉(如ToArrayToList)。這將導致集合枚舉兩次。將其保留爲IEnumerable可確保在枚舉For Each期間發生i.IsPending檢查,而不是CType()

最快的解決方案是完全放棄LINQ(LINQ語句,儘管可讀,但會增加一些開銷)。

For Each item As Item In CellCollection 
    If Not item.IsPending Then 
     Continue For 
    End If 
    'Do something here 
Next