【问题标题】:Why is Haskell so slow compared to C for Fibonacci sequence?为什么与斐波那契数列的 C 相比 Haskell 这么慢?
【发布时间】:2017-08-03 22:11:23
【问题描述】:

我只是 Haskell 的初学者。我编写了一个代码来显示斐波那契数列中的 N 个数字。这是我在 Haskell 中的代码,

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 中编写相同的确切逻辑时,(我只是打印而不是将其保存在列表中),

#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 秒。 Haskell 应该比 C 慢吗?我在互联网上查找了其他答案,他们说了一些关于记忆的事情。我正在开始 Haskell,我不知道那是什么意思。我要说的是,我编写的 C 代码和 Haskell 代码都执行相同的步骤,而且 Haskell 比 C 慢得多,它挂起我的 GHCI。 1-2 秒的差异是我永远不会担心的,如果 C 也花费了与 Haskell 相同的确切时间,我也不会担心。但是 Haskell 崩溃而 C 在 1 秒内完成它是不可接受的。

【问题讨论】:

  • 不要使用ghci 进行基准测试:使用优化的ghc 编译并运行该程序...
  • 我相信fib_seq(42) 会溢出 c 整数。你会得到一个快速、错误的值(更不用说未定义的行为),但值更大。
  • 在 Haskell 中你还可以构建一个列表。这是计算第 i 个数字之外的另一项任务。
  • “我编写的[The] C 代码和 Haskell 代码都执行相同的步骤”——它们没有;一方面,正如 Willem Van Onsem 指出的那样,C 代码中的任何地方都没有列表。您可能会发现 this question asked yesteday 相关。
  • @Ayatana 我没有对 C 做出任何声明。我只是想指出它们字面上不是相同的步骤,它们之间的对应关系可能不像你的问题所暗示的那么简单。 (参见丹尼尔瓦格纳的回答。)

标签: c performance haskell fibonacci


【解决方案1】:

以下程序,使用ghc -O2 test.hs 编译,是您发布的使用gcc -O2 test.c 编译的 C 代码的 +/-2%。

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]

一些cmets:

  1. 与您不同,我实现了完全相同的逻辑。不过,我怀疑这是真正的区别;查看剩余的 cmets 以了解更可能的原因。
  2. 我指定了与 C 用于算术的相同类型。您没有这样做,这可能会遇到两个问题:使用 Integer 而不是 Int 进行大数运算,以及使用类多态类型而不是单态类型,这会在每个函数调用上增加开销。
  3. 我编译了。 ghci 旨在尽可能快地进行交互,而不是生成快速代码。
  4. 我目前没有安装正确的 llvm 版本,但它通常会比 ghc 自己的 codegen 更好地处理像这样的大量数字代码。如果它最终比 gcc 更快,我不会太惊讶。

当然,使用许多著名的更好的斐波那契算法之一将胜过所有这些废话。

【讨论】:

    【解决方案2】:

    猜猜如果“fib_seq (n-1)”在每次递归中被计算两次会发生什么。 然后试试这个:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-29
      • 2021-04-18
      • 2011-02-17
      • 2012-10-24
      • 2017-12-06
      • 1970-01-01
      • 2021-02-14
      相关资源
      最近更新 更多