packages feed

ppad-random-0.1.0: bench/Main.hs

{-# LANGUAGE BangPatterns #-}

module Main where

import Criterion.Main
import qualified Crypto.CSPRNG as CSPRNG
import qualified Crypto.PRNG as PRNG
import qualified Crypto.PRNG.Pure as Pure
import qualified Crypto.Random as Crypton
import qualified Data.ByteString as BS
import Data.Word (Word64)
import GHC.Exts (RealWorld)
import qualified System.Entropy as Entropy
import qualified System.Random.MWC as MWC
import qualified System.Random.PCG as PCG
import qualified System.Random.SplitMix as SM
import qualified System.Random.Stateful as R

main :: IO ()
main = do
  prng <- PRNG.new 42
  mc <- CSPRNG.new (BS.replicate 32 0)
  mwc <- MWC.create
  pcg <- PCG.create
  std <- R.newIOGenM (R.mkStdGen 42)
  drg <- Crypton.drgNew
  let !sm = SM.mkSMGen 42
      !pg = Pure.new 42
  case mc of
    Nothing -> error "ppad-random (bench): invalid seed"
    Just csprng -> defaultMain [
        bgroup "word64 (x1000)" [
          bench "ppad-random (PRNG)" $
            whnfIO (sum_n (PRNG.word64 prng) 1000)
        , bench "ppad-random (PRNG.Pure)" $
            whnf (sum_draws Pure.word64 pg) 1000
        , bench "ppad-random (CSPRNG)" $
            whnfIO (sum_n (CSPRNG.word64 csprng) 1000)
        , bench "mwc-random" $
            whnfIO (sum_n (MWC.uniform mwc :: IO Word64) 1000)
        , bench "pcg-random" $
            whnfIO (sum_n (PCG.uniform pcg :: IO Word64) 1000)
        , bench "random (IOGenM StdGen)" $
            whnfIO (sum_n (R.uniformM std :: IO Word64) 1000)
        , bench "splitmix (pure)" $
            whnf (sum_pure SM.nextWord64 sm) 1000
        ]
      , bgroup "double (x1000)" [
          bench "ppad-random (PRNG)" $
            whnfIO (sum_n (PRNG.double prng) 1000)
        , bench "ppad-random (PRNG.Pure)" $
            whnf (sum_draws Pure.double pg) 1000
        , bench "mwc-random" $
            whnfIO (sum_n (MWC.uniform mwc :: IO Double) 1000)
        , bench "pcg-random" $
            whnfIO (sum_n (PCG.uniform pcg :: IO Double) 1000)
        , bench "random (IOGenM StdGen)" $
            whnfIO (sum_n (R.uniformDouble01M std) 1000)
        , bench "splitmix (pure)" $
            whnf (sum_pure SM.nextDouble sm) 1000
        ]
      , bgroup "int_range 1 6 (x1000)" [
          bench "ppad-random (PRNG)" $
            whnfIO (sum_n (PRNG.int_range 1 6 prng) 1000)
        , bench "ppad-random (PRNG.Pure)" $
            whnf (sum_draws (Pure.int_range 1 6) pg) 1000
        , bench "ppad-random (CSPRNG)" $
            whnfIO (sum_n (CSPRNG.int_range 1 6 csprng) 1000)
        , bench "mwc-random" $
            whnfIO (sum_n (MWC.uniformR (1, 6) mwc :: IO Int) 1000)
        , bench "pcg-random" $
            whnfIO (sum_n (PCG.uniformR (1, 6) pcg :: IO Int) 1000)
        , bench "random (IOGenM StdGen)" $
            whnfIO (sum_n (R.uniformRM (1, 6) std :: IO Int) 1000)
        ]
      , bgroup "bytes (32B)" [
          bench "ppad-random (CSPRNG)" $ nfIO (CSPRNG.bytes 32 csprng)
        , bench "crypton (ChaChaDRG)" $ nf (crypton_bytes drg) 32
        , bench "entropy (getEntropy)" $ nfIO (Entropy.getEntropy 32)
        ]
      , bgroup "bytes (1024B)" [
          bench "ppad-random (CSPRNG)" $ nfIO (CSPRNG.bytes 1024 csprng)
        , bench "crypton (ChaChaDRG)" $ nf (crypton_bytes drg) 1024
        , bench "entropy (getEntropy)" $ nfIO (Entropy.getEntropy 1024)
        ]
      , bgroup "CSPRNG" [
          bench "split" $ whnfIO (split_csprng csprng)
        ]
      ]

-- sum the results of running an action n times
sum_n :: Num a => IO a -> Int -> IO a
sum_n gen = loop 0 where
  loop !acc n
    | n <= 0    = pure acc
    | otherwise = do
        !x <- gen
        loop (acc + x) (n - 1)
{-# INLINE sum_n #-}

-- sum the results of stepping a pure generator n times
sum_pure :: Num a => (g -> (a, g)) -> g -> Int -> a
sum_pure step = loop 0 where
  loop !acc !g n
    | n <= 0    = acc
    | otherwise =
        let !(x, ng) = step g
        in  loop (acc + x) ng (n - 1)
{-# INLINE sum_pure #-}

crypton_bytes :: Crypton.ChaChaDRG -> Int -> BS.ByteString
crypton_bytes drg n = fst (Crypton.randomBytesGenerate n drg)

split_csprng :: CSPRNG.Gen RealWorld -> IO ()
split_csprng g = do
  !_ <- CSPRNG.split g
  pure ()

-- sum the results of n draws from a pure generator
sum_draws :: Num a => (Pure.Gen -> Pure.Draw a) -> Pure.Gen -> Int -> a
sum_draws f = loop 0 where
  loop !acc !g n
    | n <= 0    = acc
    | otherwise = case f g of
        Pure.Draw x ng -> loop (acc + x) ng (n - 1)
{-# INLINE sum_draws #-}