Linq表现:(ElementAt,Count)vs(foreach)

时间:2011-11-10 17:24:03

标签: c# performance linq

我使用(ElementAt,Count)和(foreach)作为Linq查询的结果迭代IEnumerable。令我惊讶的是,性能差异是25-30倍!那是为什么?

IEnumerable<double> result =
     ... simple Linq query that joins two tables
     ... returns about 600 items

double total = 0;

// Method 1: iterate with Count and ElementAt
for( int i = 0; i < result.Count(); i++ )
{
    total += result.ElementAt(i);
}

// Method 2: iterate with foreach
foreach( double value in result )
{
    total += value;
}

7 个答案:

答案 0 :(得分:9)

ElementAt()方法是O(n),除非IEnumerable表示的实际具体类优化它。这意味着每次调用它时,都必须循环遍历整个Enumerable才能找到n处的元素。更不用说因为i < result.Count()循环的条件部分中有for,所以每次都要循环遍历整个可枚举数。

第二种方式,你只需要遍历result一次。

答案 1 :(得分:6)

因为ElementAt每次调用IEnumerable时都在迭代IEnumerableElementAt未编入索引,因此GetEnumerator()必须使用total = result.Sum(); 实施。

为什么不

{{1}}

答案 2 :(得分:1)

因为每次调用都会遍历列表。简单的解决方案是在迭代之前调用.ToList(),更好的解决方案是停止迭代。

var theList = result.ToList();

for( int i = 0; i < result.Count; i++ )
{
    total += result[i];
}

更好的解决方案:

total = result.Sum();

答案 3 :(得分:1)

性能差异是由于IEnumerable不允许您通过索引获取的事实,所以每次在第一个循环中调用ElementAt时,它必须遍历每个项目,直到它到达你要求的元素。

答案 4 :(得分:0)

如果我冒险猜测,result.Count()调用是非延迟的,实际上是命中数据库,而foreach则没有。如果你翻转订单,可能会得到相反的结果。你也可以total = result.Sum();

答案 5 :(得分:0)

第一个可能相当于:

double total = 0;

int i = 0;
while(true)
{
  int max = /*Database call to obtain COUNT(*) of join*/
  if(max > i)
    break;
  int j = 0;
  foreach(double value in result)
  {
    if(j++ == i)
    {
      total += value;
      break;
    }
  }
  ++i
}

或者甚至可以相当于:

double total = 0;

int i = 0;
while(true)
{
  int max = 0;
  foreach(double value in result)
    ++max;
  if(max > i)
    break;
  int j = 0;
  foreach(double value in result)
  {
    if(j++ == i)
    {
      total += value;
      break;
    }
  }
  ++i
}

或者每次在上面的代码中出现result时,它甚至可以重新查询。

另一方面,Count() 可以通过一次属性访问获得ElementAt()可以是O(1),如果这些是在允许的结构上支持的话这样的优化,确实可以实现这样的优化(例如,它适用于List<T>)。

答案 6 :(得分:0)

这是关于理解延期执行的全部内容!当多次执行查询时,运行时间会急剧增加。 LINQ可以更快,但你真的需要根据你将如何使用你的查询结果做出选择。

看一下这篇文章http://allthingscs.blogspot.com/2011/03/linq-performance.html。它分析了这个问题。