代码之家  ›  专栏  ›  技术社区  ›  Steve Townsend

LINQ性能常见问题解答

  •  58
  • Steve Townsend  · 技术社区  · 15 年前

    我想和林克打交道。最让我困扰的是,即使我更好地理解了语法,我也不想为了表现力而不自觉地牺牲性能。

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

    5 回复  |  直到 15 年前
        1
  •  58
  •   Rex M    15 年前

    简单地理解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
  •  86
  •   KeithS    15 年前

    首先也是最重要的是,Linq并没有神奇地为程序节省执行操作所需的时间或内存;它只是可能会将这些操作延迟到绝对需要的时候。OrderBy()执行一个快速排序,这将占用nlogn时间,就像您在正确的时间编写自己的快速排序器或使用List.Sort()一样。因此,在编写查询时,请始终注意Linq要对序列执行的操作;如果不需要操作,请重新构造查询或方法链以避免操作。

    出于同样的原因,某些操作(排序、分组、聚合)需要了解它们所作用的整个集合。序列中的最后一个元素可能是操作必须从其迭代器返回的第一个元素。除此之外,由于Linq操作不应该改变它们的源可枚举性,但是它们使用的许多算法都会(即就地排序),因此这些操作不仅会计算,而且会将整个可枚举性复制到一个具体的有限结构中,执行该操作,并通过该操作屈服。因此,当在语句中使用OrderBy()并从最终结果中请求元素时,给定给它的IEnumerable可以生成的所有元素都将被求值、作为数组存储在内存中、排序,然后一次返回一个元素。寓意是,任何需要有限集而不是可枚举集的操作都应尽可能晚地放置在查询中,允许其他操作(如Where()和Select())来减少源集的基数和内存占用。

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

    在性能上的问题:如果你的算法的性能是神圣的,不可妥协的第一优先权,你将用C++之类的非托管语言进行编程;.NET将是非常慢的,仅仅因为它是一个托管的运行时环境,具有JIT本地编译、托管内存和额外的系统线程。我会采用一种“足够好”的理念;Linq可能会从本质上引入慢行,但如果你不能分辨出区别,而你的客户也不能分辨出区别,那么就所有实际目的而言,没有区别。”过早的优化是万恶之源,“让它发挥作用,然后寻找机会使它更有表现力,直到你和你的客户都认为它足够好。它可能总是“更好”,但除非你想成为手工包装机代码,你会发现一个点,你可以宣布胜利,并继续前进。

        3
  •  4
  •   David Burton    15 年前

    影响性能的因素有很多。

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

    就绝对效率而言,在运行预定义的存储过程时,您可能会看到一些性能问题,但通常采用的方法是使用提供合理性能的系统(如LINQ)开发解决方案,而不必担心性能损失的百分之几。如果一个查询运行得很慢,那么您可能会看到优化。

    事实上,大多数查询通过LINQ完成不会有任何问题。另一个事实是,如果查询运行缓慢,则很可能是索引、结构等方面的问题,而不是查询本身的问题,因此,即使在寻求优化时,您通常也不会触及LINQ,只是它所针对的数据库结构。

    对于处理XML,如果您已经将一个文档加载并解析到内存中(类似于基于DOM模型的任何东西,或者一个XML文档或其他什么),那么您将获得比系统更多的内存使用,这些系统做了一些类似于引发事件以指示找到开始或结束标记,但是没有构建文档的完整内存版本(如SAX或XmlReader)。缺点是基于事件的处理通常比较复杂。同样,对于大多数文档来说,这不会有问题——大多数系统都有几GB的RAM,因此占用几MB来表示单个XML文档并不是问题(而且您通常至少按一定顺序处理一大组XML文档)。只有当您有一个巨大的XML文件,它将占用100的MB时,您才会担心特定的选择。

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

    至于个人的最爱-NHibernate LINQ-它是一个对象关系映射工具,允许您定义类、定义映射细节,然后让它从类而不是相反的方式生成数据库,LINQ支持非常好(当然比亚音速类好)。

        4
  •  1
  •   Stefanvds    15 年前

    在linq to SQL中,您不需要太在意性能。你可以用你认为最易读的方式链接你的所有语句。Linq只是在最后将所有语句转换为一个SQL语句,它只在最后被调用/执行(就像在调用 .ToList()

    var 如果要在不同条件下应用不同的附加语句,则可以包含此语句而不执行它。只有当您希望将语句转换为一个结果(如对象或对象列表)时,才会最终执行。

        5
  •  1
  •   theburningmonk    15 年前

    有一个名为i4o的codeplex项目,我曾经使用过它,它可以帮助提高Linq到对象的性能,例如在进行相等比较的情况下。

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

    http://i4o.codeplex.com/ 我还没有用.Net 4测试过它,所以不能安全地说它仍然可以工作,但值得一看。

    推荐文章