我看到了这个问题,它比较了各种编译器在计算斐波那契数列的朴素方法上的性能。
我尝试用Haskell实现这个问题,并与C进行比较。
C代码:
#include <stdio.h>
#include <stdlib.h>
int fib (int n) {
if (n < 2) return 1;
return fib (n-1) + fib (n-2);
}
int main (int argc, char* argv[]) {
printf ("%i\n", fib (atoi(argv[1])));
return 0;
}
结果:
> gcc -O3 main.c -o fib
> time ./fib 40
165580141
real 0m0.421s
user 0m0.420s
sys 0m0.000s
Haskell:
module Main where
import System.Environment (getArgs)
fib :: Int -> Int
fib n | n < 2 = 1
| otherwise = fib (n-1) + fib (n-2)
main = getArgs >>= print . fib . read . head
结果:
> ghc -O3 -fllvm -optlo-O3 Main.hs -o fib
> time ./fib 40
165580141
real 0m1.476s
user 0m1.476s
sys 0m0.000s
使用性能分析工具
> ghc -O3 -fllvm -optlo-O3 -prof -auto-all -caf-all -rtsopts Main.hs -fforce-recomp -o fib
> ./fib 40 +RTS -prof
显示fib
占用了100%的时间和分配内存,这并不奇怪。我对堆进行了一些剖析,但是不知道它们意味着什么:
> ./fib 40 +RTS -hc
> ./fib 40 +RTS -hd
我的问题是:有没有什么我可以做的,使得这个 Haskell 程序的性能更接近 C 语言?还是 GHC 就是以这种方式运行导致在这个微基准测试中它变慢了呢?(我不是要求使用渐进快速算法来计算斐波那契数列。)
非常感谢。
[编辑]
事实证明,在这种情况下 ghc -O3
要比 ghc -O3 -fllvm -optlo-O3
快。但对于 LLVM 后端,optlo-block-placement
可以产生明显的差异:
> ghc -O3 Main.hs -o fib -fforce-recomp
> time ./fib 40
165580141
real 0m1.283s
user 0m1.284s
sys 0m0.000s
> ghc -O3 -fllvm -optlo-O3 -o fib -fforce-recomp
> time ./fib 40
165580141
real 0m1.449s
user 0m1.448s
sys 0m0.000s
> ghc -O3 -fllvm -optlo-O3 -optlo-block-placement -o fib -fforce-recomp
> time ./fib 40
165580141
real 0m1.112s
user 0m1.096s
sys 0m0.016s
我想调查这个问题的原因是,对于这个程序来说,C和OCaml比Haskell快得多。我有点不能接受这一点,想学习更多知识,确保我已经做了我能做的一切:D
> ocamlopt main.ml -o fib
> time ./fib 40
165580141
real 0m0.668s
user 0m0.660s
sys 0m0.008s
gcc -O3
可能会将其转换为非递归循环。我不知道 GHC 是否也可以做到这一点。 - user395760