关于性能:为什么Haskell与C的Fibonacci序列相比如此之慢?

Why is Haskell so slow compared to C for Fibonacci sequence?

我只是哈斯克尔的初学者。我写了一个代码来显示斐波那契序列中的n个数字。这是我在哈斯克尔的密码,

1
2
3
fib_seq 1 = 1:[]
fib_seq 2 = 1:1:[]
fib_seq n = sum(take 2 (fib_seq (n-1))):fib_seq (n-1)

当我在ghci中对更高的数字(如fib_seq 40)运行此代码时,需要很长时间来评估它,我的计算机挂起,我必须中断。但是,当我用C语言编写相同的精确逻辑时(我只是打印而不是将其保存在列表中)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>

int fib_seq (int n){
    if(n==1)        return 1;
    else if(n==2)   return 1;
    else            return fib_seq(n-1)+fib_seq(n-2);   }


void print_fib(int n){
    if(n==0)    return;
    else        printf("%i", fib_seq(n));
                print_fib(n-1);     }

int main(int argn, char* argc){
    print_fib(40);
    return 0;     }

代码很快。使用gcc编译时大约需要1秒钟的时间。哈斯克尔应该比C慢吗?我在网上找到了其他答案,他们说了一些关于记忆化的问题。我开始学哈斯克尔,我不知道这是什么意思。我要说的是,我编写的C代码和haskell代码都执行相同的步骤,haskell比C慢得多,它挂起了我的ghci。一个1-2秒的差异是我永远不会担心的,如果C也采取了同样的时间作为哈斯克尔,我也不会担心。但哈斯克尔撞车和C在1秒钟内做是不可接受的。


下面的程序是用ghc -O2 test.hs编译的,其速度是用gcc -O2 test.c编译的C代码速度的+/-2%。

1
2
3
4
5
6
fib_seq :: Int -> Int
fib_seq 1 = 1
fib_seq 2 = 1
fib_seq n = fib_seq (n-1) + fib_seq (n-2)

main = mapM_ (print . fib_seq) [40,39..1]

一些评论:

  • 与您不同,我实现了完全相同的逻辑。不过,我怀疑这是真正的区别;更多可能的原因请参阅下面的评论。
  • 我指定的类型与C用于算术的类型相同。您没有,这可能会遇到两个问题:使用Integer而不是Int来进行largenum算术,使用类多态类型而不是单态类型,在每个函数调用上添加开销。
  • 我编译了。ghci被构建成尽可能快地进行交互,而不是生成快速代码。
  • 目前我还没有安装正确版本的LLVM,但它通常能够处理大量的数字代码,这比GHC自己的codegen要好得多。如果它最终比GCC更快,我不会感到太惊讶。
  • 当然,使用许多著名的更好的斐波那契算法中的一种,将会胜过所有这些胡说八道。


    猜猜如果"fib_seq(n-1)"在每个递归上被评估两次会发生什么。然后试试这个:

    1
    2
    3
    4
    fib_seq 1 = 1:[]
    fib_seq 2 = 1:1:[]
    fib_seq n = sum(take 2 f):f
                where f = fib_seq (n-1)