This is way too big to leave as a comment, but it's not really an answer, just some data. I took the main loop and tested it with criterion
. Then a few variations.
import Control.Applicative
import System.Random
import Criterion.Main
import Data.List
iters :: Int
iters = 10000
loop1 :: IO Int
loop1 = go iters 0
where
go n acc | n < 0 = return acc
| otherwise = do
x <- randomRIO (0, 1 :: Double)
let acc' = acc + round x
acc' `seq` go (n - 1) acc'
loop1' :: IO Int
loop1' = go iters 0
where
go n acc | n < 0 = return acc
| otherwise = do
x <- randomRIO (0, 1)
let acc' = acc + x
acc' `seq` go (n - 1) acc'
loop2 :: IO Int
loop2 = do
g <- newStdGen
let s = foldl' (+) 0 . take iters . map round $ randomRs (0, 1 :: Double) g
s `seq` return s
loop2' :: IO Int
loop2' = do
g <- newStdGen
let s = foldl' (+) 0 . take iters $ randomRs (0, 1) g
s `seq` return s
loop3 :: IO Int
loop3 = do
g0 <- newStdGen
let go n acc g | n < 0 = acc
| otherwise = let (x, g') = randomR (0, 1 :: Double) g
acc' = acc + round x
in acc' `seq` go (n - 1) acc g'
return $! go iters 0 g0
loop3':: IO Int
loop3'= do
g0 <- newStdGen
let go n acc g | n < 0 = acc
| otherwise = let (x, g') = randomR (0, 1) g
acc' = acc + x
in acc' `seq` go (n - 1) acc g'
return $! go iters 0 g0
main :: IO ()
main = defaultMain $
[ bench "loop1" $ whnfIO loop1
, bench "loop2" $ whnfIO loop2
, bench "loop3" $ whnfIO loop3
, bench "loop1'" $ whnfIO loop1'
, bench "loop2'" $ whnfIO loop2'
, bench "loop3'" $ whnfIO loop3'
]
And here are the timings: Except, well. I'm running on a virtualbox vm, and performance is kinda random. The overall trends were consistent, though.
carl@debian:~/hask$ ghc -Wall -O2 randspeed.hs
[1 of 1] Compiling Main ( randspeed.hs, randspeed.o )
Linking randspeed ...
carl@debian:~/hask$ ./randspeed
warming up
estimating clock resolution...
mean is 3.759461 us (160001 iterations)
found 3798 outliers among 159999 samples (2.4%)
1210 (0.8%) low severe
2492 (1.6%) high severe
estimating cost of a clock call...
mean is 2.152186 us (14 iterations)
found 3 outliers among 14 samples (21.4%)
1 (7.1%) low mild
1 (7.1%) high mild
1 (7.1%) high severe
benchmarking loop1
mean: 15.88793 ms, lb 15.41649 ms, ub 16.37845 ms, ci 0.950
std dev: 2.472512 ms, lb 2.332036 ms, ub 2.650680 ms, ci 0.950
variance introduced by outliers: 90.466%
variance is severely inflated by outliers
benchmarking loop2
mean: 26.44217 ms, lb 26.28822 ms, ub 26.64457 ms, ci 0.950
std dev: 905.7558 us, lb 713.3236 us, ub 1.165090 ms, ci 0.950
found 8 outliers among 100 samples (8.0%)
6 (6.0%) high mild
2 (2.0%) high severe
variance introduced by outliers: 30.636%
variance is moderately inflated by outliers
benchmarking loop3
mean: 18.43004 ms, lb 18.29330 ms, ub 18.60769 ms, ci 0.950
std dev: 794.3779 us, lb 628.6630 us, ub 1.043238 ms, ci 0.950
found 5 outliers among 100 samples (5.0%)
4 (4.0%) high mild
1 (1.0%) high severe
variance introduced by outliers: 40.516%
variance is moderately inflated by outliers
benchmarking loop1'
mean: 4.579197 ms, lb 4.494131 ms, ub 4.677335 ms, ci 0.950
std dev: 468.0648 us, lb 406.8328 us, ub 558.5602 us, ci 0.950
found 2 outliers among 100 samples (2.0%)
2 (2.0%) high mild
variance introduced by outliers: 80.019%
variance is severely inflated by outliers
benchmarking loop2'
mean: 4.473382 ms, lb 4.386545 ms, ub 4.567254 ms, ci 0.950
std dev: 460.5377 us, lb 410.1520 us, ub 543.1835 us, ci 0.950
found 1 outliers among 100 samples (1.0%)
variance introduced by outliers: 80.033%
variance is severely inflated by outliers
benchmarking loop3'
mean: 3.577855 ms, lb 3.490043 ms, ub 3.697916 ms, ci 0.950
std dev: 522.4125 us, lb 416.7015 us, ub 755.3713 us, ci 0.950
found 5 outliers among 100 samples (5.0%)
4 (4.0%) high mild
1 (1.0%) high severe
variance introduced by outliers: 89.406%
variance is severely inflated by outliers
Here are my conclusions:
- Never benchmark anything in virtualbox if you want reliable timing.
randomRs
is slow, due to list creation overhead. Too bad lists don't fuse with foldl'
, that'd make this faster and simpler.
- There's very little overhead associated with doing the recursion in
IO
. It shows up the version that uses Int
instead of double, but that's it.
- The
Random
instance for Double
is super-slow. Or else round
is super-slow. Or maybe the combination of the two is.
System.Random
is slow. Try using themwc-random
package. – ErikR