【问题标题】:LINQ vs. Anonymous Lambda Performance: When to Use Each [closed]LINQ 与匿名 Lambda 性能:何时使用每个 [关闭]
【发布时间】:2011-10-24 18:15:27
【问题描述】:

我喜欢匿名 lambda;它们让您可以从列表中简洁地表达复杂的选择。 LINQ 做了类似的事情,所以我想我会尝试一下(最后)。

作为一个经常使用 lambdas 来选择集合子集的人,我应该什么时候使用 LINQ,什么时候应该使用 lambdas?this one 这样的问题显示了 10-100 倍的性能差异一种或另一种方式。

【问题讨论】:

  • 除了.ToArray()之外,您的第一个代码将与第二个代码相同地编译。
  • 另外,DateTime.Now 对于这种基准测试来说还不够准确。
  • 我认为有一个显着的语义差异,即 ToArray 调用。如果你把它附加到 LINQ 表达式上,我希望两者更接近。
  • 更新了我的问题以澄清我到底想知道什么。

标签: performance linq lambda


【解决方案1】:

LINQ 评估被推迟。 primesnumbers.Where(n => isOddAndNotDivisibleBy(n)) 的表达式与编译器相同。在这两种情况下,isOddAndNotDivisibleBy 永远不会被调用,直到某些东西对表达式的结果求值。在这种情况下,cb 之间的 ToArray 会强制进行评估。如果您在 ab 之间添加 ToArray,您会得到相似的时间。

为了比较,你可以试试:

var primes = (
    from n in numbers
    where isOddAndNotDivisibleBy(n)
    select n).ToArray();

【讨论】:

  • 这将性能差异缩小到约 10%。但是,它并没有告诉我在哪种情况下使用哪个,假设“大多数时候”的性能有点相似。
  • 根本没有区别——两个版本是等价的(它们编译成相同的IL)。你用你觉得更好的那个。
  • 加速 100 倍的链接问题与您第一次犯的错误相同——最后他们忘记调用 ToArray()、ToList()、Count() 等,这意味着他们查询永远不必执行。
  • 如果您看到性能差异,那是因为缓存了一些数据。如果您更改两者的顺序,您将看到相同但交换的性能差异
【解决方案2】:

所谓的“LINQ”,其实就是所谓的 LINQ 查询语法。而你所谓的“匿名 lambda”实际上就是所谓的 LINQ 方法语法。重要的是表达式:

from n in numbers
where isOddAndNotDivisibleBy(n)
select n

实际上是在编译之前转换成这个的:

numbers.Where(n => isOddAndNotDivisibleBy(n))

这意味着如果你直接使用前者或后者,你将得到相同的IL代码。

所以差异必须在其他地方。这是ToArray() 电话。 Where() 很懒惰。这意味着它几乎什么都不做,除非您实际上以一种或另一种方式迭代结果序列。这意味着只需调用Where() 几乎是即时的。但是如果你调用ToArray(),集合实际上是迭代的,结果会立即计算出来。这就是为什么你会看到如此巨大的差异。

编辑:

对于修改后的问题:为什么使用一种语法而不是另一种?主要选择更具可读性的那个。

有些方法和重载无法使用查询语法表达(First()Aggregate()Concat()Zip()、引入索引的重载……)。但是您可以对查询的一部分使用一种语法,而对其余部分使用另一种语法。

查询语法还有一个强大的特性是不能用方法语法轻易表达的:透明标识符,例如使用let 子句时。

但是至少在正确使用的情况下,这两种语法之间没有性能差异。

【讨论】:

【解决方案3】:

您的测试不一样:您在bc 之间调用ToArray(),这将在实际执行查询后分配(然后重新分配,然后再分配)内存。

ab 之间,您只需创建一个查询,该查询在执行时将执行计算。调用ToList()ToArray(),或者通过foreach循环迭代查询实际上会强制执行;它应该花费几乎完全相同的时间,因为它将从查询理解语法转换为一系列与您的第二个测试相匹配的扩展方法调用。

最后,永远不要使用DateTime.Now 进行计时;使用StopWatch

【讨论】:

    【解决方案4】:

    因为你没有执行 linq one 只是做primes .ToArray() 然后比较它们。实际上这是 linq 的 deffered execution 行为,它只是创建查询并没有运行它,直到您使用 foreachToList、...等函数执行它。

    您所做的两件事也是 linq,因为两者都在 Enumerable 类的 IEnumerable 上使用了一些扩展方法,只有第一个是查询语法,第二个是点符号语法。在msdn linq 中看到它们。

    【讨论】:

      【解决方案5】:

      这两种情况确实是相同的 LINQ 查询,但语法糖不同。

      我所说的查询是什么意思? LINQ 查询,无论您是使用mySequence.Where(i => i > 5) 还是(from item in mySequence where item > 5) 创建它,唯一存储的数据是允许您枚举序列的数据。

      当您 ToArrayToListToDictionaryforeach 时,将评估该序列,这就是您有如此巨大的性能差距的原因 - 即您的第一个查询没有被评估。曾经。

      【讨论】:

        猜你喜欢
        • 2010-11-05
        • 1970-01-01
        • 2013-06-24
        • 2012-10-20
        • 1970-01-01
        • 1970-01-01
        • 2012-01-30
        • 1970-01-01
        • 2014-05-03
        相关资源
        最近更新 更多