使Haskell代码更快

时间:2014-12-18 23:37:21

标签: haskell

有谁知道如何让这个Haskell代码更快乐?我在做Project Euler #14。 此代码在4.029秒内运行:

collatz :: Int -> Int64 -> Int                                                                                                                                                 
collatz c 1 = c                                                                 
collatz c k                                                                     
    | even k    = collatz (c+1) (k `div` 2)                                     
    | otherwise = collatz (c+1) (3*k + 1)                                       

main = do                    
    print $ maximum (map (\i -> (collatz 1 i, i)) [1..1000000])

记住collat​​z函数实际上会增加运行时间,所以我没有做任何memoization。 可比的C代码在0.239秒内运行:

int main(int argc, char *argv[])
{
    int maxlength = 0;
    int maxstart = 1;
    for (int i = 1; i <= 1000000; i++) {
        unsigned long k = i;
        int length = 1;
        while (k > 1) {
            length += 1;
            if (k % 2 == 0)
                k = k / 2;
            else
                k = 3*k + 1;
        }
        if (length > maxlength) {
            maxlength = length;
            maxstart = i;
        }
    }
    printf("%d, %d\n", maxlength, maxstart);
    return 0;
}

Haskell代码使用ghc -O3编译,C代码使用gcc -std = c99 -O3编译。

2 个答案:

答案 0 :(得分:5)

我注意到这个问题主要是一个转贴。请参阅here

代码的主要问题是ghc默认不优化整数除法。 要手动修复我的代码,

collatz c k                                                                     
    | k .&. 1 == 0 = collatz (c+1) (k `shiftR` 1)                                     
    | otherwise    = collatz (c+1) (3*k + 1) 

但是,如果计算机上安装了LLVM,则可以使用

编译原始代码
ghc -O2 -fllvm code.hs

LLVM进行必要的优化。这两种解决方案都使我的代码运行大约0.420秒,这更接近于可比较的C代码。

答案 1 :(得分:0)

以下是haskell wiki的解决方案:

import Data.Array
import Data.List
import Data.Ord (comparing)

syrs n =
    a
    where
    a = listArray (1,n) $ 0:[1 + syr n x | x <- [2..n]]
    syr n x =
        if x' <= n then a ! x' else 1 + syr n x'
        where
        x' = if even x then x `div` 2 else 3 * x + 1

main =
    print $ maximumBy (comparing snd) $ assocs $ syrs 1000000

我机器上的计算时间:

haskell|master⚡ ⇒ ghc -O2 prob14_memoize.hs
[1 of 1] Compiling Main             ( prob14_memoize.hs, prob14_memoize.o )
Linking prob14_memoize ...
haskell|master⚡ ⇒ time ./prob14_memoize
(837799,524)
./prob14_memoize  0.63s user 0.03s system 99% cpu 0.664 total

与原版相比:

haskell|master⚡ ⇒ ghc -O2 prob14_orig.hs
[1 of 1] Compiling Main             ( prob14_orig.hs, prob14_orig.o )
Linking prob14_orig ...
haskell|master⚡ ⇒ time ./prob14_orig
(525,837799)
./prob14_orig  2.77s user 0.01s system 99% cpu 2.777 total