繁体   English   中英

Haskell计算Ackermann 4 1的速度很慢?

[英]Haskell slow to compute Ackermann 4 1?

这是7个月前的一个老问题,当时堆栈溢出器同意Haskell计算Ackermann函数的低效率是由于编译器错误造成的。

Ackermann与Haskell / GHC的效率非常低

7个月后,这似乎是固定的。 似乎ack使用线性内存运行,但它运行速度非常慢。

main = print (ack 4 1)
-- Ackermann function
ack 0 n = n + 1
ack m 0 = ack (m-1) 1
ack m n = ack (m-1) (ack m (n - 1))

$ time ./ack
65533

>real   8m53.274s
>user   8m47.313s
>sys    0m4.868s


Processor  2.8 GHz Intel Core i7
Memory  8 GB 1333 MHz DDR3
Software  Mac OS X Lion 10.7.5 (11G63)

我只是要求对此有任何见解。 更详细的将得到投票。 请记住,我是函数式编程的新手,甚至关于尾递归与常规递归的简单评论也会受到赞赏和赞成。

我不知道你是怎么运行的,但我怀疑完整列表是:

  1. 您的程序没有任何更改,编译时没有优化。 初始时间: 7m29.755s
  2. 看来你没有使用优化。 编译时一定要使用-O2和try -fllvm 新时间: 1m2.412s

  3. 使用显式类型签名并Int使用Int (默认值为Integer )。 新时间: 0m15.486s

因此,我们通过使用优化获得了近8倍的加速(为什么每个其他基准测试问题都没有使用优化标志?!?!?)和使用Int而不是Integer的额外约4倍。

ack添加类型签名:

ack :: Int -> Int -> Int

这应解决您的代码的两个问题:

过于笼统的类型

如果没有签名,编译器将派生以下类型:

ack :: (Eq a, Eq b, Num a, Num b) => a -> b -> b

ack最终推广到所有数字类型,而不仅仅是整数。 这个额外的间接层使代码变慢。

ack一个具体类型(比如Int )会删除这个间接。

输入默认值

另外,我猜你的主要动作是这样写的:

main = print (ack 4 1)

您的ack适用于任何数字类型,但您没有准确指定哪一个。 这意味着GHC在一个称为类型默认的过程中自动选择一个。

在这种情况下,它选择Integer ,一种可变长度类型。 因为Integer可以处理任意大小的数字,所以它比机器大小的Int慢得多。

结论

总结一下:

  • 始终为顶级定义编写类型签名。
  • 总是用-Wall编译。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM