【问题标题】:Will scala compiler hoist regular expressionsscala编译器会提升正则表达式吗
【发布时间】:2023-03-15 07:25:02
【问题描述】:

我想知道这是不是:

object Foo {
  val regex = "some complex regex".r
  def foo() {
    // use regex
  }
}

还有这个:

object Foo {
  def foo() {
    val regex = "some complex regex".r
    // use regex
  }
}

会有任何性能差异。即,scala编译器会识别"some complex regex".r是一个常量并缓存它,这样它就不会每次都重新编译?

【问题讨论】:

  • 你可能想举一个例子,而不是 main 方法 - "some complex regex".r 在这两个例子中只会被执行一次,不管任何编译器优化,因为 main 方法只会被调用一次(启动程序)。当然,除非您从程序中调用 main 方法,但这不是阅读示例的人们所期望的。
  • @Cyäegha 感谢您指出这一点,已更正。
  • 为了能够做到这一点,编译器必须证明StringLike.r 是纯的,这(在一般情况下)等价于解决停机问题。对于简单的方法,它可能仍然是可能的,但我不知道编译器是否甚至尝试过,考虑到它很可能无论如何都无法证明任何事情。

标签: scala optimization hoisting


【解决方案1】:

它会在运行时有所不同。第一个示例中的表达式将只计算一次。从秒开始的表达式 - 每次您拨打 Foo.foo() 时。这里的计算意味着将隐式添加的函数“r”(来自scala-library)应用于字符串:

scala> ".*".r
res40: scala.util.matching.Regex = .*

这个函数实际上每次调用它时都会编译正则表达式(无缓存)。

顺便说一句,运行时任何简单的正则表达式缓存都容易受到OutOfMemory 的攻击-但是,我相信可以使用WeakHashMap 安全地实现它,但是当前Java 的Pattern 实现(这是scala 的基础) Regex) doesn't 实际实现它,可能是因为这样的实现可能对性能没有可预测的影响(GC 可能必须在每次运行时删除大部分缓存值)。带有驱逐的缓存更可预测,但仍然不是那么简单的方法(谁会为它选择超时/大小?)。谈到 scala-way,一些智能宏可以在编译时进行优化(仅对基于“字符串常量”的正则表达式进行缓存),但默认情况下:

Scala 编译器也没有对正则表达式进行任何优化,因为正则表达式不是 scala 语言的一部分。

所以最好把静态的 "".r 结构移出函数。

【讨论】:

  • 缓存是否发生并不取决于正则表达式是否是语言的一部分。例如,r 的实现可以缓存以未编译模式字符串为键的已编译模式。 Scala 正则表达式没有被缓存的真正原因是底层的 java.util.regex 包没有像回答的那样缓存here
  • @Sim 从标题中可以清楚的看到这个问题是关于compiler的
  • 我看到了这一点,并且从问题的措辞中也看到了目标是理解性能差异而不是编译器功能。我知道没有一种编程语言存在语言级别的正则表达式语法,例如,Ruby 或 JavaScript 中的 /[a-c]/,编译器可以轻松检测到,但没有办法通过将字符串传递给一些来创建正则表达式构造函数,编译器不容易检测到。这就是为什么缓存通常是在库级别而不是在编译器级别实现的。 Ruby 就是这样工作的,我相信 JavaScript 也是如此。
  • @Sim 我在这里看到了关于编译器和 scala 的明确问题,我无法读心,宁愿不这样做:)。我明确表示r 每次调用它时都会编译正则表达式(这显然意味着尽可能简单地告诉“不缓存”)——我没有说它与编译时/运行时问题有某种关系。关于编译时不可能优化的信息 - 是我回答中的最后一个(也是附加的)句子,旨在明确 也 scala-compile-time 中没有关于正则表达式的优化。
  • @Sim 我从来没有说过“缓存取决于正则表达式是否是语言的一部分”——你只是想读懂我的想法,但它不起作用:)。我说过 scala 编译器(在问题中明确提到了两次)并没有给出关于正则表达式的 s***
猜你喜欢
  • 2015-12-14
  • 2011-05-30
  • 1970-01-01
  • 2014-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多