代码之家  ›  专栏  ›  技术社区  ›  stiank81

使用Linq“Everywhere”时的性能问题?

  •  18
  • stiank81  · 技术社区  · 15 年前

    在升级到resharper5之后,它给了我更多关于代码改进的有用提示。我现在看到的是用LINQ查询替换foreach语句的技巧。举个例子:

    private Ninja FindNinjaById(int ninjaId)
    {
        foreach (var ninja in Ninjas)
        {
            if (ninja.Id == ninjaId)
                return ninja;
        }
        return null;
    }
    

    建议使用LINQ替换为以下内容:

    private Ninja FindNinjaById(int ninjaId)
    {
        return Ninjas.FirstOrDefault(ninja => ninja.Id == ninjaId);
    }
    

    这看起来很好,我确信用foreach替换这个在性能上没有问题。但总的来说,这是我应该做的吗?或者我是否会在所有这些LINQ查询中遇到性能问题?

    9 回复  |  直到 10 年前
        1
  •  18
  •   tvanfosson    15 年前

    在知道是否应该更改代码之前,您需要了解Linq查询在“引擎盖下”要做什么,并将其与运行代码进行比较。一般来说,我并不意味着您需要知道将要生成的确切代码,但您确实需要知道执行操作的基本思路。在您的示例中,我假设Linq基本上与您的代码工作相同,并且因为Linq语句更紧凑和更具描述性,所以我更喜欢它。但是,有时,尽管可能不多,但LINQ可能不是理想的选择。一般来说,我认为任何循环构造都可以被等效的LINQ构造替换。

        2
  •  15
  •   Steven    10 年前

    首先,我要说,我喜欢Linq的表现力,并且一直使用它,没有任何问题。

    然而,在性能上存在一些差异。通常它们很小,可以忽略,但在应用程序的关键路径中,有时您可能希望将它们优化掉。

    以下是您应该注意的一组差异,这些差异可能与性能有关:

    • LINQ过度使用委托调用,委托调用(非常小的一点)比方法调用慢,当然也比内联代码慢。
    • 委托是对象内的方法指针。需要创建该对象。
    • LINQ运算符通常返回一个新对象(迭代器),该对象允许在集合中循环。因此,链接的LINQ运算符创建多个新对象。
    • 当内部循环使用来自外部的对象(称为闭包)时,它们也必须包装在对象中(需要创建)。
    • 许多LINQ运营商称 GetEnumerator 方法对集合进行迭代。打电话 方法 通常确保创建另一个对象。
    • 迭代集合是使用 IEnumerator 接口。接口调用比普通方法调用慢一点。
    • 迭代器 通常需要处理对象,或者至少, Dispose 必须打电话。

    如果性能有问题,也可以尝试使用 for 结束 foreach .

    再次,我爱林肯和 我不记得有没有决定不使用LINQ (对对象)由于性能而查询。所以, 不要过早优化 首先从最可读的解决方案开始,而不是在需要时进行优化。所以简介,简介和 轮廓 .

        3
  •  7
  •   Ilya Ryzhenkov    15 年前

    我们发现性能有问题的一件事是创建大量lambda并在小集合上迭代。转换后的样本中会发生什么?

    Ninjas.FirstOrDefault(ninja => ninja.Id == ninjaId)
    

    首先,创建(生成的)闭包类型的新实例。托管堆中的新实例,一些对GC有效。 第二,新的委托实例是从该闭包中的方法创建的。 然后调用方法FirstOrDefault。它做什么? 它迭代集合(与原始代码相同)并调用委托。

    所以基本上,这里添加了4个内容: 1。创建闭包 2。创建委托 三。通过委托呼叫 4。收集结束语并委派

    如果你经常给findninjabyid打电话,你会加上这个可能是重要的性能命中。当然,测量一下。

    如果您将其替换为(等效)

    Ninjas.Where(ninja => ninja.Id == ninjaId).FirstOrDefault()
    

    它补充说 5。为迭代器创建状态机(“where”是生成函数)

        4
  •  6
  •   Finglas    15 年前

    简况


    唯一能确定的方法就是分析。是的,某些查询速度可能较慢。但是,当你看到Resharper在这里替换了什么,它本质上是相同的事情,以不同的方式完成。忍者被循环,每个ID都被检查。如果有什么不同的话,您可能会认为这种重构归根结底就是可读性。你觉得哪一个更容易阅读?

    更大的数据集将有更大的影响,当然,正如我所说的,概况。这是唯一确定这种增强是否有负面影响的方法。

        5
  •  5
  •   Rob Fonseca-Ensor    15 年前

    我们已经构建了大量的应用程序,在整个应用程序中都有大量的linq。从来没有,从来没有让我们减速过。

    编写LINQ查询是完全有可能的,这会非常慢,但是修复简单的LINQ语句比使用大量的for/if/for/return算法要容易得多。

    接受雷斯哈珀的建议:)

        6
  •  5
  •   JulianR    15 年前

    一则轶事:当我刚开始了解C 3.0和Linq时,我还处在“当你有锤子,一切都像钉子”的阶段。作为一个学校作业,我应该写一个连接四/四行游戏,作为对抗性搜索算法的练习。我在整个程序中使用了LINQ。在一个特定的例子中,我需要找到一个游戏片段将落在某个特定列中的行。LINQ查询的完美用例!结果这真的很慢。但是,Linq不是问题,问题是我 搜索 首先。我通过保留一个查找表来优化这一点:一个整数数组,包含游戏板每一列的行号,在插入游戏块时更新该表。不用说,这要快得多。

    经验教训:首先优化您的算法,像LINQ这样的高级构造实际上可能会使这变得更容易。

    也就是说,创建所有这些代表都有一定的成本。另一方面,利用Linq的懒惰特性也可以带来性能优势。如果手动循环某个集合,则几乎必须创建中间 List<> 而对于Linq,基本上可以流式处理结果。

        7
  •  3
  •   Oskar Kjellin    15 年前

    上面的操作完全相同。

    只要正确使用LINQ查询,就不会出现性能问题。如果正确使用它,由于创建LINQ的人员的技能,它更有可能更快。

    创建自己的插件的唯一好处是,如果您想要完全控制,或者LINQ没有提供您需要的,或者您想要更好的调试能力。

        8
  •  3
  •   Dean Harding    15 年前

    Linq查询最酷的地方在于它 dead simple 转换为并行查询。取决于你在做什么,它可能会更快,也可能不会更快(像往常一样,配置文件),但它还是相当整洁的,尽管如此。

        9
  •  3
  •   Chris S    15 年前

    再加上我自己使用LINQ的经验,在性能确实很重要的地方——单触式——两者之间的差别仍然很小。

    你在3GS iPhone上“残疾”,有46MB的内存和620MHz的ARM处理器。诚然,代码是AOT编译的,但即使在它是JIT的模拟器上,经过一系列的间接寻址,对于1000秒的对象集来说,差异也只有十分之一毫秒。

    与Windows Mobile一起,这是您必须担心性能成本的地方,而不是运行在四核8GB服务器上的大型ASP.NET应用程序,或具有双重得分的台式机。这方面的一个例外是大型对象集,尽管可以说您总是懒惰地加载,并且初始查询任务将在数据库服务器上执行。

    StackOverflow有点陈词滥调,但是在100毫秒之前使用更短更可读的代码确实很重要。