【问题标题】:IEnumerable<T> and "yield return" performance questionIEnumerable<T> 和“收益回报”性能问题
【发布时间】:2010-11-23 14:37:28
【问题描述】:

下午好,

我正在编写一个简单的词法分析器,它基本上是this one 的修改版本。获得每个令牌后,我需要进行轻微修改并重新分析它以重新检查它的类型。另外,当然,在词法分析之后,我需要重新使用整个令牌列表来对其进行一种“解析”。我的问题是,如果在词法分析器中使用IEnumerable&lt;Token&gt;yield return 语句会使整个程序的性能变慢......是否最好使用List&lt;Token&gt;,迭代地构建列表并使用普通的return 语句?遍历IEnumerable/List 怎么样?哪个更快?

非常感谢。

【问题讨论】:

  • 尝试对代码进行基准测试...不要为了感知性能改进而牺牲可读性

标签: c# list ienumerable


【解决方案1】:

您问错了问题,您应该更担心 Regex 的成本。枚举令牌将只是其中的一小部分,优化可以加倍速度但只能将程序性能提高 1% 的代码是没有意义的。

编写代码,对其进行分析,您就会知道如何处理版本 2。鉴于这类工具是在“人工时间”运行的(当程序需要 20 毫秒的时间是其两倍时,没有明显的差异) ,最可能的结果是“什么都不需要做”。

【讨论】:

  • 谢谢汉斯。但是你有什么建议来提高 Regex 的性能呢?
  • 您没有收到消息。它需要反过来:“我发现我的正则表达式存在性能问题。这是我要求它做的,这是我测量的”。只有当你记录你真正的问题时,你才能期待一个真正的答案。你还没有真正的问题。
【解决方案2】:

它可能会对性能产生一些影响 - 但它也允许延迟构建迭代器。

就我个人而言,我会以最易读的方式编写代码并衡量其性能 - 然后开始担心微优化这类事情。以一种方式进行测试,以另一种方式进行测试,看看使用性能最高的解决方案会损失多少可读性(如果有的话),以及实际获得多少速度。

请注意,迭代已知为 List&lt;T&gt; 类型的表达式与迭代 IEnumerable&lt;T&gt;(恰好由 List&lt;T&gt; 实现)相比,在性能上的优势很小,因为 List&lt;T&gt; 实现了迭代器本身使用可变结构...基本上,如果您使用更高的抽象层,您最终会得到一个装箱的值,但在这种特殊情况下,我几乎肯定更喜欢使用正确的抽象而不是微小的抽象性能提升。

【讨论】:

    【解决方案3】:

    IEnumerable 和 yield return 语句被转换为 GetEnumator() 和 IL 代码中枚举器的实现。

    虽然收益回报在为每个返回的令牌做一些额外的工作方面有其优点 在枚举期间,我会坚持创建列表并返回列表,因为它会产生更少的方法调用,因此应该更快。

    【讨论】:

    • 是什么让您认为方法调用会减少?大概每个项目都会调用 Add ......这在迭代器块版本中不需要。在这两种情况下,都会有相同数量的 MoveNext()/Current 调用。
    【解决方案4】:

    到现在为止,我相信你会发现你正在尝试过早地进行优化,据许多人说,the root of all evil.

    但是,如果您真的想加快速度,正则表达式似乎是一种昂贵的方法。每次执行 Regex.Match() 时,都会再次扫描字符串,这会导致至少与令牌一样多的扫描。

    如果您知道定义标记的边界(例如,'{' 和 '}'),则可以扫描字符串一次以构建可枚举的标记(使用 yield 或列表,我不认为会有很大的不同)。然后调用者可以重建字符串,查找要替换标记的值。

    当然,这只适用于简单的“搜索和替换”类型标记。更复杂的需要更复杂的东西,例如正则表达式。也许您可以扩展 TokenDefinition 以指定匹配是简单匹配还是正则表达式匹配。这将减少执行的正则表达式的数量,但仍保持所需的灵活性。

    【讨论】:

      猜你喜欢
      • 2018-01-23
      • 1970-01-01
      • 2010-09-22
      • 1970-01-01
      • 2011-05-27
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 2016-02-21
      相关资源
      最近更新 更多