LINQ性能常见问题

我正在努力与LINQ握手。 最令我困扰的是,即使我更好地理解语法,我也不想在不知不觉中牺牲performance力。

他们是否是“有效的LINQ”的信息或书籍的集中式存储库? 否则,你个人最喜欢的高性能LINQ技术是什么?

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

只要理解LINQ在内部进行的工作,就可以获得足够的信息来知道你是否正在进行性能testing。

这里是一个简单的例子,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固有的链接和懒惰评估,你可能获得很多好处。 再一次,与其他任何事情一样,这只是一个理解实际上在做什么的问题。

Linq作为一种内置技术,具有性能优势和劣势。 扩展方法背后的代码已经在.NET团队中引起了相当大的性能关注,并且其提供懒惰评估的能力意味着对一组对象执行大部分操作的代价遍布于需要操作集的较大algorithm。 但是,有一些事情需要知道,可以使你的代码的性能。

首先,Linq不会奇迹般地保存您的程序执行操作所需的时间或内存; 它可能会延迟这些操作,直到绝对需要。 OrderBy()执行一个QuickSort,它将花费nlogn的时间,就像您在正确的时间编写您自己的QuickSorter或使用List.Sort()一样。 所以,在编写查询时,请始终注意Linq对系列的要求; 如果操作不是必须的,请查看重构查询或方法链以避免它。

同样,某些操作(sorting,分组,集合)需要知道他们正在执行的整个集合。 系列中最后一个元素可能是操作必须从其迭代器返回的第一个元素。 最重要的是,因为Linq操作不应该改变它们的源可枚举,但是它们使用的许多algorithm将会(即就地sorting),这些操作最终不仅仅是评估,而是将整个枚举复制到一个具体的有限结构,执行操作,并通过它屈服。 所以,当你在一个语句中使用OrderBy(),并且从最终结果中请求一个元素时,所有赋给它的IEnumerable都会产生一个评估值,作为一个数组存储在内存中,然后返回一个元素时间。 道德是,任何需要一个有限集而不是一个枚举的操作应尽可能放在查询中,允许像Where()和Select()这样的其他操作来减less源集的基数和内存占用。

最后,Linq方法大大增加了系统的调用堆栈大小和内存占用。 每个必须知道整个集合的操作都将整个源集保留在内存中,直到最后一个元素被迭代为止,并且每个元素的评估将涉及一个调用堆栈,其深度至less是链或子句中方法的数量的两倍在你的内联语句中(对每个迭代器的MoveNext()的调用或者产生GetEnumerator,以及沿途至less加一个对每个lambda的调用)。 这只会导致比执行相同操作的智能devise的内联algorithm更大,更慢的algorithm。 Linq的主要优点是代码简单。 创build,然后sorting组列表的字典值不是很容易理解的代码(相信我)。 微优化可以进一步混淆它。 如果性能是您最关心的问题,那么请不要使用Linq; 它将会增加大约10%的时间开销和几倍于自己操作列表的内存开销。 然而,可维护性通常是开发人员最关心的问题,Linq DEFINITELY在这方面有所帮助。

在性能踢:如果你的algorithm的性能是神圣的,不可压倒的优先级,你会在一个非托pipe的语言如C ++编程; 由于它是一个托pipe的运行时环境,使用JIT本地编译,托pipe内存和额外的系统线程,.NET会慢得多。 我会采取一种“足够好”的理念。 Linq本质上可能会引入减速,但是如果你不能区分这个差别,而你的客户无法区分这个差别,那么对于所有的实际目的来说,没有什么区别。 “过早优化是万恶之源”; 让它工作,然后寻找机会,使其更高性能,直到你和你的客户都认为足够好。 它可以永远是“更好的”,但是除非你想要手工打包机器代码,否则你会发现一个缺点,那就是你可以声明胜利并继续前进。

有各种因素会影响性能。

通常,使用LINQ开发一个解决scheme将提供相当合理的性能,因为系统可以构build一个expression式树来表示查询,而无需实际运行查询。 只有在迭代结果时,才会使用此expression式树来生成并运行查询。

在绝对效率方面,针对预定义的存储过程运行,您可能会看到一些性能问题,但通常采取的方法是使用提供合理性能的系统(如LINQ)来开发解决scheme,而不必担心会有百分之几的损失的performance。 如果查询然后运行缓慢,那么也许你看优化。

现实情况是大多数查询不会有通过LINQ完成的任何问题。 另一个事实是,如果你的查询运行的很慢,可能更多的是索引,结构等问题,而不是查询本身,所以即使在优化的时候,你也经常不会碰到LINQ,只是数据库结构它正在对付。

对于处理XML,如果你有一个文档被加载和parsing到内存中(就像任何基于DOM模型或XmlDocument或者其他的东西),那么你会得到比系统更多的内存使用指示find一个开始或结束标记,但不build立一个完整的内存版本的文档(如SAX或XmlReader)。 缺点是基于事件的处理通常比较复杂。 同样,对于大多数文档来说,不存在问题 – 大多数系统具有几GB的RAM,因此占用几MB代表单个XML文档不是问题(并且您经常处理大量的XML文档至less有点顺序地)。 只有当你有一个巨大的XML文件,占用了100 MB的MB,你担心特定的select。

请记住,LINQ允许你遍历内存中的列表等等,所以在某些情况下(比如你要在一个函数中一次又一次地使用一组结果),你可以使用.ToList或.ToArray返回结果。 有时这可能是有用的,虽然通常你想尝试使用数据库的查询,而不是在内存中。

至于个人的最爱 – NHibernate的LINQ – 它是一个对象关系映射工具,它允许你定义类,定义映射的细节,然后让它从你的类生成数据库,而不是其他的方式,LINQ支持是漂亮好(当然比SubSonic更好)。

在LINQ to SQL中,你不需要关心性能。 你可以用你认为最可读的方式链接你的所有陈述。 Linq只是将所有的语句翻译成1个SQL语句,最后只是调用/执行(就像调用.ToList()

如果你想在不同的条件下应用各种额外的语句, var可以包含这个语句而不执行它。 最后的执行只发生在你想把语句翻译成像对象或对象列表的结果时。

有一个叫做i4o的codeplex项目,我用了一段时间,可以帮助提高Linq在对象比较中的性能,比如

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

http://i4o.codeplex.com/我还没有用;.Net 4testing过,所以不能安全的说它仍然可以工作,但值得一试。 为了让它发挥它的魔力,你大多只需要用一些属性来修饰你的类来指定哪些属性应该被索引。 当我使用它之前,它只能用于平等比较。