LINQ函数的顺序是否重要?

基本上,正如问题所述…… LINQ函数的顺序在性能方面很重要吗? 显然结果将不得不相同…

例:

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

两者都返回相同的结果,但在不同的LINQ顺序。 我意识到,重新排列一些项目会产生不同的结果,我不关心这些。 我主要关心的是如果得到相同的结果,sorting是否会影响性能。 而且,不只是在我做的2个LINQ调用(OrderBy,Where)上,而是在任何LINQ调用上。

这将取决于正在使用的LINQ提供程序。 对于LINQ to Objects,这肯定会造成巨大的差异。 假设我们已经有了:

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

这需要整个集合进行sorting然后过滤。 如果我们有一百万个项目,其中只有一个的代码大于3,那么我们会浪费大量的时间来sorting结果,这些结果将被丢弃。

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

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

这一次,我们只是订购过滤结果,在“只有一个项目匹配filter”的示例中,在时间和空间上都会更有效率。

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

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

这很好 – 我们知道我们永远不会被0除。但是如果我们在过滤之前执行sorting,查询将会抛出一个exception。

是。

但是,性能差异究竟取决于LINQ提供者如何评估底层expression式树。

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

要准确找出性能差异,您最有可能想要分析您的应用程序。 尽pipe如此,过早的优化通常并不值得 – 但你可能会发现LINQ性能以外的问题更为重要。

在你的特定例子中,它可以对性能产生影响。

第一个查询:您的OrderBy调用需要遍历整个源序列,包括Code为3或更小的项目。 Where子句也需要迭代整个有序的序列。

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

在Linq-To-Objects中:

sorting很慢,使用O(n)内存。 另一方面,相对较快并且使用不变的内存。 这样做首先会更快,对于大型collections显着更快。

减less的内存压力也可以是显着的,因为在我的经验中,大对象堆(连同它们的集合)的分配相对较昂贵。

显然结果将不得不相同…

请注意,这不是真的 – 特别是,以下两行将给出不同的结果(对于大多数提供者/数据集):

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

值得注意的是,在考虑如何优化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) }; 

如果出于任何原因,您决定通过首先将平均值存储为variables来“优化”查询,则不会得到期望的结果:

 // 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来处理对象,但这是一些好的思想。

这取决于相关性。 假设如果Code = 3的项目很less,则下一个订单将在小集合上工作,以按date获得订单。

而如果您有许多具有相同的CreatedDate的项目,则下一个订单将在更大的集合上工作,以按date获取订单。

所以,在这两种情况下,会有不同的performance