【问题标题】:Why this function call in Scala is not optimized away?为什么 Scala 中的这个函数调用没有被优化掉?
【发布时间】:2013-10-19 10:10:15
【问题描述】:

我正在使用 Scala 2.10.3 运行这个程序:

object Test {
  def main(args: Array[String]) { 
    def factorial(x: BigInt): BigInt = 
      if (x == 0) 1 else x * factorial(x - 1)

    val N = 1000
    val t = new Array[Long](N)
    var r: BigInt = 0

    for (i <- 0 until N) {
      val t0 = System.nanoTime()

      r = r + factorial(300)
      t(i) = System.nanoTime()-t0
    }

    val ts = t.sortWith((x, y) => x < y)

    for (i <- 0 to 10)
      print(ts(i) + "  ")

    println("***  " + ts(N/2) + "\n" + r)
  }  
}

并在每次循环迭代期间评估带有常量参数的纯函数factorial(基于时序结果的结论)。优化器不应该在第一次调用后重用函数调用结果吗?

我正在为 Eclipse 使用 Scala IDE。编译器是否有任何优化标志,可以生成更高效的代码?

【问题讨论】:

  • 编译器怎么知道它是一个纯函数?
  • 如果BigInt 上的* 是纯的,那么factorial 是纯的。我刚刚花了一天时间阅读 Scala 中的编程,所以我对 Scala 编译器的了解太少了。我的主要观点是,由 LLVM 编译的 C++ 或 D 中的类似代码将重复调用具有优化掉常量参数的函数。
  • @PaulJurczak 顺便说一句,你能提供一些关于 LLVM 优化的文章、论文或其他内容的链接吗?我的意思是由 LLVM 编译的 C++ 或 D 中的类似代码将重复调用具有优化的常量参数的函数
  • @om-nom-nom 这是我在 D lang 论坛上关于此优化的帖子的链接:forum.dlang.org/thread/utwrmmycxfxncflofksf@forum.dlang.org 这不是相同的代码,但情况非常相似。我用 Scala 测试了e28 函数,并且常量调用没有被优化掉。

标签: scala optimization benchmarking


【解决方案1】:

Scala 不是纯粹的函数式语言,因此如果没有效果系统,它就无法知道factorial 是纯的(例如,它不“知道”任何关于大整数乘法的事情)。

您需要在此处添加自己的记忆方法。大多数情况下,只需在循环外添加 val f300 = factorial(300)


Here is a question about memoization.

【讨论】:

  • 有没有办法让 Scala 编译器知道factorial 是纯的(或足够纯的)?
  • @PaulJurczak 没有简单的方法,除非您自己完成所有工作或插入一些编译器插件。还有另一个原因——与 C++ 或 C 等编译语言不同,Scala 和 Java 编译器在编译期间不会执行积极的优化——它们推迟了大多数复杂的优化,希望 JIT 能够做到。
  • Scala 有an effects plugin,但是虽然“类型检查”您的效果是否正确组合,但它不会尝试处理诸如记忆函数值之类的优化。可能您可以向函数添加 @memoize 宏,并要求它们也是 @pure...
  • Here is some further discussion memoisation 是否应该是一种语言功能。 Eugene Burmako 实际上建议使用 @memo 宏...
  • 在这种情况下,我没有寻找记忆,因为它会破坏这个微基准。我很惊讶,优化器不够聪明。例如,LLVM 可以轻松处理此类情况。
猜你喜欢
  • 1970-01-01
  • 2016-06-22
  • 2023-03-15
  • 1970-01-01
  • 2011-11-16
  • 1970-01-01
  • 2017-08-10
  • 2015-03-22
  • 2012-03-31
相关资源
最近更新 更多