功能语言中的函数开销,如Haskell或像Scala这样的混合类

Ale*_*Emm 10 performance haskell functional-programming scala

来自Python,Javascript和Java等命令式语言,我经常阅读函数开销以及为什么要从性能角度避免映射.显然,这些都不是函数式语言,而外国概念通常往往不那么优化,也不那么惯用.我知道调用函数正在将值从寄存器推回到堆栈,这是很昂贵的.

因此,与最近关于计划生育的概念和语言,我真的不知道如何哈斯克尔解决这个问题的嗡嗡声?这只是编译器内联的内容吗?除此之外,JVM上的FP-Languages(Clojure/Scala)如何解决这个问题?甚至没有一个像样的Tail-Call优化在优化FP Code方面也说明了JVM的功能.

谢谢!

Rex*_*err 5

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

将一个方法变成可以传递的东西涉及到创建一个对象,但是对象创建在 JVM 上是很便宜的,并且 JVM 和 Scala 都已经或将会有一些技巧来避免创建该对象,除非确实有必要。

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

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

  • “如果没有保留对旧列表的引用,你可以改变这些值并将其称为新列表 - 我不知道 GHC 是否会玩这样的把戏。” 我认为不是在这个意义上,Haskell 在几乎每个层面上都避免了可变性的概念;但 GHC 确实很好地利用了重写规则,特别是 [_stream fusion_](http://stackoverflow.com/questions/578063/what-is-haskells-stream-fusion),在某种程度上,这甚至更好:不是因为您注意到它没有在其他地方使用而改变列表,而是编译程序,因此这个中间列表_甚至从未实际构建过_! (4认同)