感兴趣的问题是通过使其更快来增强以下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倍,所以我想知道原因。
答案 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肯定能够快速随机数生成。