【问题标题】:Linq to Objects: filtering performance questionLinq to Objects:过滤性能问题
【发布时间】:2010-10-13 09:14:16
【问题描述】:

我在思考 linq 的计算方式,这让我想知道:

如果我写

var count = collection.Count(o => o.Category == 3);

这与以下有何不同:

var count = collection.Where(o => o.Category == 3).Count();

毕竟,IEnumerable<T>.Where() 将返回 IEnumerable<T>,它没有实现 Count 属性,因此后续的 Count() 实际上必须遍历项目以确定计数,这会导致花费额外的时间这。

我编写了一些快速测试代码来获取一些指标,但它们似乎随机地相互击败。测试代码我一开始就不放这里了,如果有人要求,我会加进去的。

那么,我错过了什么吗?

【问题讨论】:

  • 它们应该实际上是相同的,因为列表只会被枚举一次。

标签: linq linq-to-objects performance


【解决方案1】:

实际上不会有很多内容 - 两种形式都会遍历集合,检查每个项目的谓词,并计算匹配项。这两种方法都会流式传输数据 - 例如,Where 实际上并不是在构建所有匹配项的内存列表。

第一种形式的间接层更少(薄),仅此而已。使用它 (IMO) 的主要原因是为了可读性/简单性,而不是性能。

【讨论】:

    【解决方案2】:

    正如 Jon Skeet 所说,这两种技术本质上都必须做同样的事情 - 枚举序列,同时在匹配谓词时有条件地递增计数器。两者之间的任何性能差异都应该是微小的:对于几乎所有用例来说都是微不足道的。如果有一个令牌赢家,我会认为它应该是第一个,因为从反射器看来Count的重载使用谓词使用它自己的foreach来枚举而不是比在第二个示例中将工作卸载到流式 aWhere无参数Count 的更明显方式。这意味着技术#1可能具有两个次要性能优势:

    1. 更少的参数验证(空测试等)检查。技术 #2 的 Count 还将检查其(管道)输入是否是 ICollectionICollection<T> ,这是不可能的。
    2. 单个构造的枚举器与通过管道连接的两个枚举器(额外的状态机需要成本)。

    虽然有一个小众支持技术#2 点:Where 在构造源序列的枚举器方面稍微复杂一些;它对列表和数组使用不同的。这可能在某些情况下会提高性能。

    当然,我应该重申我的分析可能完全错误 - 通过静态代码分析来推断性能,尤其是当差异可能很小时,这不是一个好主意。只有一种方法可以找出答案 - 测量特定设置的执行时间。

    仅供参考,我反映的来源来自 .NET 3.5 SP1。

    【讨论】:

      【解决方案3】:

      我知道你在想什么。至少,我认为我愿意; Count() 将查看 Count 是否可用作属性,如果是,将简单地返回它。否则,它必须枚举项目以获取其返回值。

      不过,接受谓词的Count() 版本总是会导致集合被迭代,因为它必须这样做才能查看哪些匹配。

      【讨论】:

        【解决方案4】:

        以上答案很有意义,还请考虑,如果您拆分为任何延迟执行的 Linq-To-X 实现(Linq to Sql 是主要的),这些方法中使用的表达式参数可能会导致不同的结果。

        【讨论】:

          猜你喜欢
          • 2013-09-03
          • 2011-11-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-03
          • 2016-04-17
          • 1970-01-01
          相关资源
          最近更新 更多