LINQ。是否链接Select / Where子句更慢?

时间:2012-12-05 13:56:59

标签: performance linq select iterator where

如果我链接诸如

之类的条款
var results = elements
    .Where(n => n > 3)
    .Where(n => n % 2 == 0);

这比

var results = elements.Where(n => n > 3 && n % 2 == 0);

解释原因或原因?

编辑:似乎共识是即使POCO对象迭代两次。如果是这种情况,有人可以解释为什么微软不会合并这些谓词。我偶然发现了Enumerable.CombinePredicates,我认为这样做了。有人可以解释一下这是做什么的。

2 个答案:

答案 0 :(得分:2)

如果你正在谈论LINQ到对象,每个Where都涉及设置一个新的迭代器状态机,这很昂贵,所以是的,它比将两个条件放在一起要慢。

如果你正在谈论LINQ到别的东西,那么,这取决于;额外的Where可能只涉及某处额外的字符串连接。它仍然可能更昂贵,但exect差异取决于LINQ提供商。

答案 1 :(得分:2)

修改: 我仔细看了一下。 WhereEnumerableIterator扩展方法返回的Where实际上会覆盖Where方法,并将谓词合并为一个回调。

public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) {
    return new Enumerable.WhereEnumerableIterator<TSource>(
        this.source, 
        Enumerable.CombinePredicates<TSource>(this.predicate, predicate));
}

private static Func<TSource, bool> CombinePredicates<TSource>(
    Func<TSource, bool> predicate1, Func<TSource, bool> predicate2
    ) {
    return (TSource x) => predicate1(x) && predicate2(x);
}

因此,我在机器上看到的速度差异应该归因于其他东西。

<击> 第一个示例将循环遍历elements集合,以查找满足条件item > 3的项目,并再次查找满足条件item % 2 == 0的项目。

<击>

第二个示例将循环遍历elements集合,以查找满足条件item > 3 && item % 2 == 0的项目。

在提供的示例中,第二个很可能总是比第一个快,因为它只会循环elements一次。

以下是我在我的机器(.NET 3.5)上获得的一些非常一致的结果的示例:

    var stopwatch = new System.Diagnostics.Stopwatch();
    var elements = Enumerable.Range(1, 100000000);
    var results = default(List<int>);
    stopwatch.Start();
    results = elements.Where(n => n > 3).Where(n => n % 2 == 0).ToList();
    stopwatch.Stop();
    Console.WriteLine(stopwatch.Elapsed);
    stopwatch.Reset();
    stopwatch.Start();
    results = elements.Where(n => n > 3 && n % 2 == 0).ToList();
    stopwatch.Stop();
    Console.WriteLine(stopwatch.Elapsed);
    Console.WriteLine("Done");
    Console.ReadLine();

结果:

00:00:03.0932811
00:00:02.3854886
Done

修改
@Rawling是对的,因为我的解释仅适用于POCO对象集合中使用的LINQ。当用作LINQ-to-SQL,NHibernate,EF等的接口时,您的结果将更依赖于实现。