2012-03-16 40 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