【问题标题】:What is the LINQ to objects 'where' clause doing behind the scenes?LINQ to objects 'where' 子句在幕后做什么?
【发布时间】:2009-03-19 22:40:20
【问题描述】:

我刚刚替换了这段代码:

foreach( var source in m_sources )
{
    if( !source.IsExhausted )
    {
        ....
    }
}

用这个:

foreach( var source in m_sources.Where( src => !src.IsExhausted ) )
{
   ...
}

现在代码看起来更好(对我来说),但我想知道这里到底发生了什么。我担心这种情况下的性能,如果应用此过滤器意味着会发生某种编译器魔法,那将是个坏消息。

这两段代码在做“相同”的事情吗?是否创建了临时容器来执行过滤然后将它们传递给我的 foreach?

非常感谢您对此主题的任何帮助。谢谢。

【问题讨论】:

标签: c# .net linq performance where-clause


【解决方案1】:

yield return 关键字和 lambda 确实涉及在编译时创建隐藏类和在运行时分配额外对象,如果您的背景是 C 或 C++,那么关心性能是很自然的。

自然,但错误!

I tried measuring the overhead 用于对局部变量进行闭包的 lambda,并发现它非常小(仅几纳秒),几乎在所有应用程序中都没有意义。

【讨论】:

  • 不错的文章,在 XBox 上看到 Compact 框架和 XNA 的结果会很有趣。
【解决方案2】:

这取决于 m_sources 的类型。

如果它是来自 LINQ to SQL 或实体框架的数据上下文,则您传递的参数将被编译为表达式的实例并被解析以创建 SQL(在数据模型的帮助下)。在此过程中存在一些实际成本,但可能(在大多数情况下)主要由数据库的往返行程所支配。

如果它是 IEnumerable,那么 Where 的实现几乎是:

public static IEnumnerable<T> Where(this IEnumerable<T> input, Func<T, bool> predicate) {
  foreach (var v in input) {
    if (predicate(v)) {
      yield return v;
    }
  }
}

这非常有效并且执行得很慢(所以如果你提前退出循环,谓词将不会应用于整个集合)。

【讨论】:

  • 这是我最初发现它可能在做的事情,但我认为与简单地测试一个属性相比,这会表现得非常糟糕。
  • 访问属性需要方法调用,虽然委托是一种更昂贵的方法调用,但编译器优化的机会很多。最终确定的唯一方法是测量,除非集合非常大,否则不太可能产生任何影响。
【解决方案3】:

基本上,是的,都是一样的,O(n)。

where 子句将在您循环遍历列表时执行(即,如果您在第一项之后中断,则不会测试以下项)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多