“按名称呼叫”会减慢Haskell的速度吗?

时间:2012-12-22 09:06:39

标签: performance haskell implementation callbyname

我认为没有。

我的理由是Haskell是纯函数式编程(没有I / O Monad),如果“名称”相同,他们可以使每个“名字调用”使用相同的评估值。

我对实施细节一无所知,但我真的很感兴趣 详细解释将非常感谢:)

顺便说一下,我试过谷歌,很难得到任何有用的东西。

2 个答案:

答案 0 :(得分:9)

首先,Haskell是一个规范,而不是一个实现;该报告实际上并不需要使用逐个名称的评估或懒惰的评估。 Haskell实现只需要是非严格的,这会排除按值调用和类似的策略。

因此,严格来说(ha,ha)说,评估策略不能减慢Haskell的速度。我不确定可以减慢Haskell的速度,虽然显然有什么东西,否则在Haskell 98之后不需要花费12年才能获得下一版本的报告。我的猜测是它以某种方式涉及委员会。


无论如何,“懒惰评估”指的是“按需调用”策略,这是Haskell最常见的实现选择。这与按名称调用的不同之处在于,如果在多个位置使用子表达式,则最多只评估一次子表达式。

有资格作为将要共享的子表达式的细节有点微妙,可能在某种程度上依赖于实现,但是要使用GHC Haskell中的示例:考虑函数cycle,它无限地重复输入列表。一个天真的实现可能是:

cycle xs = xs ++ cycle xs

这最终效率低下,因为没有单个cycle xs表达式可以共享,因此结果列表必须在遍历时不断构造,每次分配更多内存并进行更多计算。

相比之下,actual implementation看起来像这样:

cycle xs = xs' where xs' = xs ++ xs'

这里名称xs'是递归定义的,因为它本身附加在输入列表的末尾。此时xs'已共享,仅评估一次;生成的无限列表实际上是内存中的有限循环链表,一旦评估了整个循环,就不需要进一步的工作。


一般情况下,GHC不会为您记忆功能:给定fx,除非您给予f x,否则将重新评估{{1}}的每次使用结果一个名称并使用它。在任何一种情况下,结果值都是相同的,但性能可能会有很大差异。这主要是为了避免悲观 - GHC很容易为你记忆,但在很多情况下,这会花费大量内存来获得微小或不存在的速度。

另一方面是保留了共享值;如果你有一个计算成本非常高的数据结构,命名构造它的结果并将其传递给使用它的函数确保没有重复的工作 - 即使它被不同的线程同时使用。

你也可以通过这种方式让自己感到悲观 - 如果一个数据结构计算成本低并且使用大量内存,你应该避免共享对完整结构的引用,因为这将保持只要以后任何事情都可能使用它,整个事物就会在记忆中存活下来。

答案 1 :(得分:2)

是的,确实如此。问题是Haskell通常不能太早地计算值(例如,如果它会导致异常),所以它有时需要保留thunk(用于计算值的代码)而不是值本身,使用更多内存并减慢速度。编译器尝试检测可以避免这种情况的情况,但是无法检测到所有