LINQ功能的顺序重要吗?


114

基本上,正如问题所述……LINQ函数的顺序是否对性能有影响?显然结果仍然必须相同...

例:

myCollection.OrderBy(item => item.CreatedDate).Where(item => item.Code > 3);
myCollection.Where(item => item.Code > 3).OrderBy(item => item.CreatedDate);

两者返回的结果相同,但LINQ的顺序不同。我意识到重新订购某些物品会导致不同的结果,我并不担心这些。我主要关心的是要知道在获得相同结果时订购是否会影响性能。而且,不仅在我进行的2个LINQ调用(OrderBy,Where)上,而且在任何LINQ调用上。


9
很棒的问题。
罗伯特·S。

更为明显的是,提供者的优化在像的更复杂的情况下很重要var query = myCollection.OrderBy(item => item.Code).Where(item => item.Code == 3);
马克·赫德

1
您值得投票:),一些有趣的问题。当我在EF中将Linq写入实体时,我会考虑这一点。
GibboK 2011年

1
@GibboK:尝试“优化”您的LINQ查询时请小心(请参见下面的答案)。有时您最终并没有真​​正优化任何东西。尝试优化时,最好使用探查器工具。
myermian 2012年

Answers:


147

这将取决于使用的LINQ提供程序。对于LINQ to Objects来说,那肯定可以带来巨大的改变。假设我们实际上有:

var query = myCollection.OrderBy(item => item.CreatedDate)
                        .Where(item => item.Code > 3);

var result = query.Last();

这就要求对整个集合进行排序,然后进行过滤。如果我们有一百万个项目,而其中只有一个代码大于3,那么我们将浪费大量时间,将结果扔掉。

将其与反向操作进行比较,首先进行过滤:

var query = myCollection.Where(item => item.Code > 3)
                        .OrderBy(item => item.CreatedDate);

var result = query.Last();

这次,我们仅订购已过滤的结果,在“仅与过滤器匹配的单个项目”的示例情况下,这将在时间和空间上效率更高。

这也可能会影响查询是否正确执行。考虑:

var query = myCollection.Where(item => item.Code != 0)
                        .OrderBy(item => 10 / item.Code);

var result = query.Last();

很好-我们知道我们永远不会被0除。但是,如果在过滤之前执行排序,查询将抛出异常。


2
@Jon Skeet,是否有关于每个LINQ提供程序和功能的Big-O的文档?或者这仅仅是“每种表达对情况都是唯一的”的情况。
迈克尔

1
@michael:没有非常清楚的记录,但是,如果您阅读了我的“ Edulinq”博客系列,我想我会谈得很详细。
乔恩·斯基特


3
@gdoron:说实话,你的意思不是很清楚。听起来您可能想写一个新问题。请记住,Queryable根本不尝试解释您的查询-它的工作仅仅是保留您的查询,以便其他人可以解释它。还要注意,LINQ to Objects甚至不使用表达式树。
乔恩·斯基特

1
@gdoron:关键是提供者的工作,而不是Queryable的工作。而且,使用实体框架也无关紧要。不过,这对于LINQ to Objects 确实很重要。但是是的,一定要问另一个问题。
乔恩·斯基特

17

是。

但究竟什么是性能差异取决于如何底层表达式树由LINQ提供评估。

例如,对于LINQ-to-XML,您的查询第二次执行速度可能更快(首先使用WHERE子句),而对于LINQ-to-SQL,则查询第一次执行速度更快。

为了准确地找出性能差异,您很可能需要分析您的应用程序。但是,与以往一样,过早的优化通常不值得付出努力-您可能会发现除LINQ性能以外的问题更为重要。


5

在您的特定示例中,它可能会影响性能。

第一个查询:您的OrderBy呼叫需要遍历整个源序列,包括Code3或更少的那些项。在Where随后条款也需要遍历整个有序序列。

第二个查询:Where调用将序列限制为仅Code大于3的那些项。OrderBy然后,该调用仅需要遍历该Where调用返回的简化序列。


3

在Linq-To-Objects中:

排序相当慢,并且O(n)占用内存。Where另一方面,它相对较快并使用恒定的内存。因此,先做Where会更快,而对于大型馆藏则要快得多。

减少的内存压力也可能很重要,因为根据我的经验,在大对象堆上的分配(以及它们的集合)相对昂贵。


1

显然结果仍然必须相同...

请注意,这实际上是不正确的-特别是以下两行将给出不同的结果(对于大多数提供程序/数据集):

myCollection.OrderBy(o => o).Distinct();
myCollection.Distinct().OrderBy(o => o);

1
不,我的意思是,即使考虑优化,结果也应该相同。“优化”某些东西并获得不同的结果是没有意义的。
迈克尔

1

值得注意的是,在考虑如何优化LINQ查询时应格外小心。例如,如果使用声明性版本的LINQ执行以下操作:

public class Record
{
    public string Name { get; set; }
    public double Score1 { get; set; }
    public double Score2 { get; set; }
}


var query = from record in Records
            order by ((record.Score1 + record.Score2) / 2) descending
            select new
                   {
                       Name = record.Name,
                       Average = ((record.Score1 + record.Score2) / 2)
                   };

如果由于某种原因,您决定先通过将平均值存储到变量中来“优化”查询,那么您将无法获得所需的结果:

// The following two queries actually takes up more space and are slower
var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            order by average descending
            select new
                   {
                       Name = record.Name,
                       Average = average
                   };

var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            select new
                   {
                       Name = record.Name,
                       Average = average
                   }
            order by average descending;

我知道没有多少人对对象使用声明式LINQ,但这是值得深思的。


0

这取决于相关性。假设如果您的Code = 3项目很少,那么下一个订单将适用于少量集合以按日期获取订单。

而如果您有许多具有相同CreatedDate的项目,则下一个订单将适用于更大的集合集以按日期获取订单。

因此,两种情况下的性能都会有所不同

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.