Linq to Objects:过滤性能问题

时间:2010-10-13 09:14:16

标签: linq linq-to-objects performance

我正在考虑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()实际上必须遍历项目以确定计数这会导致额外的时间花在这上面。

我写了一些快速测试代码来获得一些指标,但它们似乎随机相互击败。我最初不会在这里输入测试代码,但如果有人要求,我会把它拿进来。

那么,我错过了什么吗?

4 个答案:

答案 0 :(得分:3)

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

第一种形式有一个(薄)间接层,这就是全部。使用它(IMO)的主要原因是为了可读性/简单性,而不是性能。

答案 1 :(得分:2)

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

  1. 更少的参数验证(空值测试等)检查。技术#2的Count还会检查其(管道)输入是Count还是ICollection,这是不可能的。
  2. 单个构造的枚举器与两个管道连接在一起的枚举器(另一个状态机有成本)。
  3. 虽然有一个未成年人赞成技术#2点:ICollection<T>在为源序列构造枚举器时稍微复杂一些;它对列表和数组使用不同的一个。 可能在某些情况下使其更具性能。

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

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

答案 2 :(得分:0)

我知道你在想什么。至少,我想我做; Count()会查看Count是否可用作属性,如果是,则会返回。否则,它必须枚举项以获得其返回值。

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

答案 3 :(得分:0)

上面的答案提出了好的观点,还要考虑如果你闯入延迟执行的任何Linq-To-X实现(Linq到Sql是主要的),这些方法中使用的Expression参数可能会导致不同的结果。