LINQ性能常见问题解答


70

我试图与LINQ保持联系。最让我困扰的是,即使我对语法有了更好的了解,我也不想在表现力方面无意中牺牲性能。

它们是否是用于“有效LINQ”的良好的集中式信息存储库或书籍?如果失败,您个人最喜欢的高性能LINQ技术是什么?

我主要关心LINQ to Objects,但是当然也欢迎有关LINQ to SQL和LINQ to XML的所有建议。谢谢。


2
以我的个人经验,LINQ与您自己编写的代码的速度完全相同。在许多情况下,由于懒惰的评估(以及.NET团队已经为您完成的疯狂优化),它甚至更快。
特拉维斯·高克尔

1
我不知道您在什么环境中工作,但是我在对性能至关重要的企业应用程序上工作。定期检查每种方法的性能,并严格禁止LINQ。LINQ的使用是一种惩罚,因为必须在一周内每天按需为办公室中的每个人沏茶/咖啡。

3
@Rex M-这次交流正是我问这个问题的原因。我发现很难追踪LINQ优于手动编码循环的情况,但是当LINQ彻底失败时,有很多反例。如果我们一无所知,请教育我们。
史蒂夫·汤森

1
@Steve和其他东西有什么不同?您应该知道嵌套的for循环是O(Nx),并且您应该知道LINQ流通过yield(因此是O(N))产生结果。我认为您预测的困难要比实际困难多。
Rex M

2
@Rex M-也许是这样。我只是想知道我在这里看到多少个性感的LINQ答案是最佳的。表现力很诱人。感谢您的想法。
史蒂夫·汤森

Answers:


63

只需了解LINQ在内部进行的操作,就可以获得足够的信息,以了解您是否对性能产生了影响。

这是一个简单的示例,其中LINQ有助于提高性能。考虑以下典型的老式方法:

List<Foo> foos = GetSomeFoos();
List<Foo> filteredFoos = new List<Foo>();
foreach(Foo foo in foos)
{
    if(foo.SomeProperty == "somevalue")
    {
        filteredFoos.Add(foo);
    }
}
myRepeater.DataSource = filteredFoos;
myRepeater.DataBind();

因此,上面的代码将迭代两次,并分配另一个容器来保存过滤后的值。真是浪费!与之比较:

var foos = GetSomeFoos();
var filteredFoos = foos.Where(foo => foo.SomeProperty == "somevalue");
myRepeater.DataSource = filteredFoos;
myRepeater.DataBind();

这仅迭代一次(绑定转发器时);它只会使用原始容器;filteredFoos只是一个中间的枚举器。并且,由于某种原因,如果您以后决定不绑定转发器,则不会浪费任何信息。您甚至不需要重复或评估一次。

当你进入非常复杂的顺序操作,可以潜在地通过利用LINQ的固有使用链接和懒惰评估的收获很多。再说一遍,与其他任何事情一样,这只是了解它实际在做什么。


2
谢谢,这正是我希望看到的示例。我只是希望通过实例学习比反复试验或先验分析容易。
史蒂夫·汤森

两次重复的样本为+1。值得一提的是,过滤后的列表还会消耗内存,这会给GC造成一定压力。
devgeezer 2011年

8
我不明白第一个示例如何重复两次。当您在其上调用“ DataBind()”时,这是否与任何“ myRepeater”有关?
2012年

@AR考虑中继器在内部将如何工作-它具有项目模板和数据源-它需要遍历源并将每个元素绑定到项目模板的副本。
Rex M

8
我不明白为什么LINQ实现应该更快。至少在正常情况下,如果真正枚举了中继器,则不是这样。检查LINQ源,您将看到...使用LINQ,您可以保存一个分配,但是要做更多的代码(更多的方法,更多的检查),并且此代码的​​执行效率较低(与调用者代码混合)。
2013年

96

Linq作为一种内置技术,在性能方面有优缺点。.NET团队对扩展方法背后的代码给予了相当大的性能关注,其提供惰性评估的能力意味着对一组对象执行大多数操作的成本分布在需要该操作集的较大算法上。但是,您需要了解一些可能影响或破坏代码性能的事情。

首先,Linq不会神奇地为您的程序节省执行操作所需的时间或内存。否则可能会将这些操作延迟到绝对需要。OrderBy()执行QuickSort,这将花费nlogn时间,就像您在正确的时间编写自己的QuickSorter或使用List.Sort()一样。因此,在编写查询时,请始终记住您要Linq对系列进行的操作。如果不需要进行操作,请尝试重组查询或方法链,以免发生这种情况。

同样,某些操作(排序,分组,聚合)需要了解它们所作用的整个集合。系列中的最后一个元素可能是操作必须从其迭代器返回的第一个元素。最重要的是,由于Linq运算不应更改其可枚举的源,但是它们使用的许多算法(即就地排序)都将因此而产生,因此,这些运算不仅要评估,还要将整个可枚举复制到具体的有限结构中,执行操作并通过操作产生收益。因此,当您在语句中使用OrderBy()并从最终结果中索要一个元素时,将对赋给它的IEnumerable可以产生的所有内容进行评估,将其作为数组存储在内存中,进行排序,然后在一个位置返回一个元素。时间。道德是

最后,Linq方法极大地增加了系统的调用堆栈大小和内存占用。每个必须了解整个集合的操作都会将整个源集合保留在内存中,直到对最后一个元素进行迭代为止,并且对每个元素的求值将涉及调用堆栈,其深度至少是链或子句中方法数量的两倍在您的内联语句中(对每个迭代器的MoveNext()或产生GetEnumerator的调用,以及在此过程中至少对每个lambda的调用)。与执行相同操作的智能工程内联算法相比,这只会导致更大,更慢的算法。Linq的主要优点是代码简单。创建然后排序组值列表的字典不是很容易理解的代码(相信我)。微观优化可以进一步混淆它。如果您最关心性能,那么不要使用Linq。它将增加大约10%的时间开销,并且是自己处理列表的内存开销的几倍。但是,可维护性通常是开发人员最关心的问题,Linq绝对会在此方面提供帮助。

提高性能:如果算法的性能是神圣,不可妥协的第一要务,那么您将使用非托管语言(如C ++)进行编程;.NET将会变得更加缓慢,因为它是一个托管的运行时环境,具有JIT本机编译,托管的内存和额外的系统线程。我会采用“足够好”的哲学。Linq可能会因其性质而导致变慢,但是如果您无法分辨出差异,而您的客户也无法分辨出差异,那么就所有实际目的而言,都没有差异。“过早的优化是万恶之源”;使其发挥作用,然后寻找机会使其性能更高,直到您和您的客户都认为它足够好为止。它可能总是“更好”,但是除非您想手动打包机器代码,否则,


1
“如果您最关注性能,那么就不要使用Linq”-Web开发呢?那里的性能可能是一个主要问题,因为您可能期望成千上万的用户同时执行操作,所以您会避免在Web开发中避免Linq吗?
BornToCode 2014年

6
Wellll ...是的,性能是一个问题,但是ASP.NET运行时和生命周期的体系结构为您处理了许多多用户方面。此外,运行时性能问题通常与构成回发延迟大部分的网络带宽/延迟问题相形见war。您仍然应该尽可能快地完成服务器端的工作,但是如果我的网站在实际流量负载下响应速度不够快,那么相对结构良好的Linq查询就不会是我的第一个怀疑对象。
KeithS 2014年

4

有多种因素会影响性能。

通常,使用LINQ开发解决方案将提供相当合理的性能,因为系统可以构建表达式树来表示查询,而无需在构建查询时实际运行查询。仅当您遍历结果时,它才使用此表达式树来生成并运行查询。

就绝对效率而言,在预定义的存储过程上运行可能会遇到一些性能损失,但是通常采取的方法是使用具有合理性能的系统(例如LINQ)来开发解决方案,而不用担心会损失几分钱性能。如果查询运行缓慢,那么您可能会考虑优化。

现实情况是,通过LINQ完成大多数查询不会有丝毫问题。另一个事实是,如果查询运行缓慢,则与查询本身相比,索引,结构等问题更可能是索引,结构等问题,因此,即使在进行优化时,您通常也不会碰LINQ,正在处理的数据库结构。

对于处理XML,如果您已将文档加载并解析到内存中(例如基于DOM模型的任何东西,或XmlDocument或其他东西),那么与进行某些事件(例如引发事件)的系统相比,您将获得更多的内存使用率。表示找到开始或结束标签,但未构建文档的完整内存版本(例如SAX或XmlReader)。缺点是基于事件的处理通常相当复杂。同样,对于大多数文档来说,这不会有问题-大多数系统具有数GB的RAM,因此占用几MB表示一个XML文档并不是问题(而且您通常至少会处理一些XML文档)按顺序)。只有当您拥有一个占用100 MB的巨大XML文件时,您才会担心这种选择。

请记住,LINQ还允许您遍历内存中的列表等,因此在某些情况下(例如,您将在函数中一次又一次使用一组结果),可以使用.ToList或.ToArray返回结果。有时这可能很有用,尽管通常您希望尝试使用数据库的查询而不是内存查询。

至于个人收藏夹-NHibernate LINQ-它是一个对象关系映射工具,它使您可以定义类,定义映射详细信息,然后从类中获取它来生成数据库,而无需反过来,LINQ支持好(肯定比SubSonic之类的好)。


1

在linq to SQL中,您不需要太在意性能。您可以按照您认为最可读的方式链接所有语句。Linq最终将所有语句转换为1条SQL语句,最后仅被调用/执行(例如,当您调用a.ToList()

一个var可以包含该声明没有,如果你想在不同的条件下应用各种额外的语句执行它。最后的执行仅在您要将语句转换为结果(如对象或对象列表)时发生。


2
这仅适用于LINQ to SQL。
nlawalker

他做到了。请参阅第一个修订的最后一句。
Rex M 2010年

1
-1。有时LinqToSql会将您的查询转换为n + 1条语句。有时产生的查询不是最佳查询。LinqToSql查询需要数据库性能分析,与其他任何来源的查询没有什么不同。
艾米B 2010年

1

有一个名为i4o的codeplex项目,我使用了一段时间,可以在进行相等比较的情况下帮助提高Linq to Objects的性能,例如

from p in People 
where p.Age == 21 
select p;

http://i4o.codeplex.com/ 我尚未在.Net 4上进行过测试,因此不能安全地说它仍然可以工作,但值得一试。为了使其发挥作用,您通常只需要用一些属性装饰您的类即可指定应为哪个属性建立索引。当我之前使用它时,它只能与相等比较一起使用。


我发现一般来说,如果我正在使用需要索引的LINQ来做一些事情,通常最好自己做索引,因为a)我可能希望将它们用于其他目的,并且b)我控制索引的管理方式。这也使我可以创建更多的
comple
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.