【问题标题】:Linq Slowness on Single Call单次调用的 Linq 缓慢
【发布时间】:2012-12-03 12:53:15
【问题描述】:

背景:我的游戏使用的是组件系​​统。我有一个Entity 类,它在List<IComponent> 中有一个IComponent 实例列表。我目前对Entity.GetComponent<T>() 的实现是:

return (T)this.components.Single(c => c is T);

添加碰撞检测后,我注意到我的游戏降到了 1FPS。分析显示罪魁祸首就是这个调用(每帧调用 3000 多次)。

除了 3000x 之外,我注意到调用这 300k 次大约需要 2 秒。我将其优化为一个简单的迭代循环:

foreach (IComponent c in this.components) { 
  if (c is T) {
    return (T)c; 
  }
}

return default(T);

此代码现在运行时间约为 0.4 秒,快了一个数量级。

我认为Single 会比单个 foreach 循环更有效。这是怎么回事?

【问题讨论】:

  • 你可以用 FirstOrDefault 替换 Single ,它只会返回第一次出现,应该和你的迭代循环一样快
  • 如果你更喜欢抽象出铸件,你可以像这样使用OfType<T>()return this.components.OfType<T>().FirstOrDefault();
  • 感谢 Martheen 和 @RiskyMartin,我会采纳您的建议。

标签: c# linq optimization


【解决方案1】:

Single 的文档说:

返回序列的唯一元素,如果出现异常则抛出异常 序列中没有一个元素。

另一方面First:

序列中通过测试的第一个元素 指定谓词函数。

因此,使用Single,您可以在没有short circuiting 的情况下遍历整个序列,这就是上面的foreach 循环所做的。所以,请使用FirstFirstOrDefault 而不是Single

【讨论】:

  • 语义上,如果存在多个满足该条件的对象,Single 将引发异常。如果我使用First,我该如何保持这种行为
  • 如果不遍历整个列表,您将无法做到这一点。如果你需要了解欺骗,你必须使用 Single,或者管理你自己的数据结构来索引内存中存在的每种类型的数量。
  • 谢谢,感谢您帮助解决这个问题。
【解决方案2】:

Single 遍历整个集合并确保只找到一项。所以你最好的表现总是 O(N)

您的迭代搜索也会受到 O(N) 性能的影响,但这是最坏的情况。

来源: List<T>.Single Method

【讨论】:

  • 请注意,我不确定 .Single 是否是迭代搜索,它也可能会多次遍历集合。
猜你喜欢
  • 2011-03-22
  • 2012-02-17
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
  • 2015-04-11
  • 2010-09-27
  • 2019-04-12
  • 2012-04-15
相关资源
最近更新 更多