12

来自 Python、Javascript 和 Java 等命令式语言,我经常阅读有关函数开销以及从性能角度避免映射的原因。显然,这些不是函数式语言,外来概念通常往往不太优化且不太惯用。我知道调用函数会将值从寄存器推回堆栈,这很昂贵。

因此,随着最近关于 FP 概念和语言的热议,我真的很想知道 Haskell 是如何解决这个问题的?仅仅是编译器内联了很多吗?除此之外,JVM 上的 FP-Languages (Clojure/Scala) 如何解决这个问题?就优化 FP 代码而言,甚至没有一个像样的 Tail-Call 优化也能很好地说明 JVM 的能力。

谢谢!

4

2 回答 2

4

我无法为 Haskell 提供全面的答案,但对于 Scala,答案非常简单:它对字节码执行转换,例如,(简单)尾调用被实现为带有变量的循环。由于计算机是可变的(RAM,而不是 WORM!),这本质上是任何语言都必须做的以实现良好性能。

将一个方法变成可以传递的东西涉及到创建一个对象,但是在 JVM 上创建对象的成本很低,而且 JVM 和 Scala 都有或将有技巧来避免创建该对象,除非它真的有必要。

然后是重用内存与使用新内存的问题。这很难完全解决,但是 JVM 非常擅长快速回收内存,因此您需要支付相对适度的惩罚,例如每次重新创建列表而不是改变其中的值。(如果没有对旧列表的引用,您可以更改这些值并将其称为新列表——我不知道 GHC 是否会玩这样的伎俩。)最糟糕的情况是当您需要本地更新时,您在某些情况下,可以让 log(n) 工作而不是恒定时间工作。

当您将所有这些东西加在一起时,(单线程)性能损失通常是适度的或可以忽略不计。

于 2013-04-24T20:52:16.390 回答
0

任何基于 JVM 的语言都可以从 JIT 编译中受益,因此这已经为 Scala 和 Clojure 等语言提供了巨大的优势。

此外,scala 可以从 @tailrec 优化中受益,它将递归调用转换为循环。

于 2013-04-24T20:50:53.090 回答