Haskell:如何提高深度递归和随机数生成的性能

时间:2015-10-02 13:11:33

标签: performance haskell recursion random

感兴趣的问题是通过使其更快来增强以下Haskell程序的递归深度为100,000,000:

s :: Int32 -> Int32 -> IO Int32
s 0 acc = return $! acc
s n acc = do r <- randomRIO (0, maxBound :: Int32)
             s (n-1) $! (acc + r)

main = do z <- s 100000000 0
          putStrLn $ show z

这个程序在我的机器上大约需要70秒。 但是,相应的C程序只需一秒钟:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>    

int main(){
    double startTime;
    double timeEllapsed;
    unsigned int sum;
    long long i;    

    startTime = clock();
    srand(time(NULL));    

    sum = 0;
    i = 0;
    for(i=0; i<100000000;i++){
      sum += rand();
    }    

    timeEllapsed = (clock() - startTime) / (CLOCKS_PER_SEC);    

    printf("sum = %u, time ellapsed = %lfs\n", sum, timeEllapsed);    

    return EXIT_SUCCESS;    
}

这种差异来自哪里? Haskell标准库中随机数的实现是否较慢?或者你应该使用与randomRIO不同的功能吗?还是与懒惰评估有关? 您能否优化Haskell程序的任何内容并使其更快?

很明显,Haskell和C这样的高级语言之间可能存在性能差异,但我没想到它会慢大约70倍,所以我想知道原因。

1 个答案:

答案 0 :(得分:3)

是的,random库非常慢。 randomRIO在这种情况下并不理想,但使用其他功能random对速度无济于事。有几种选择,Bryan O'Sullivan mwc-random是最受欢迎的,但也有Don Stewart的mersenne-random和我自己的pcg-random。另请注意,C rand不是一个特别好的随机数生成器。

这是您的代码的类似mwc-random版本,在我的机器上以2.3s运行(ghc-7.10.2,-O2 -fllvm

import Control.Monad.ST
import qualified System.Random.MWC as MWC
import Data.Int

s :: Int32 -> Int32 -> Int32
s n s = runST $ do
  g <- MWC.create

  let go 0  !a = pure a
      go !i !a = do
        w <- MWC.uniformR (0, maxBound::Int32) g
        go (i-1) (a + w)

  go n s

main :: IO ()
main = print $ s' 100000000 0

使用System.Random.PCG的等效代码为我运行1s。在实验快速纯pcg代码上,它可以在0.28s运行:

import System.Random.PCG.Fast.Pure

s :: Int32
s = go (100000000::Word64) 0xcafef00dd15ea5e5 0
  where
  go 0   _ a = a
  go !i !s a = go (i-1) s' (a + fromIntegral r)
    where P s' r = bounded 2147483647 s

main = print s

所以Haskell肯定能够快速随机数生成。