Skip to content

Commit

Permalink
Merge pull request #112 from idontgetoutmuch/range-benchmarks
Browse files Browse the repository at this point in the history
Add uniformR benchmarks
  • Loading branch information
idontgetoutmuch authored Apr 23, 2020
2 parents df6e46f + 625fadd commit d03e325
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 20 deletions.
18 changes: 13 additions & 5 deletions System/Random/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -492,22 +492,21 @@ instance Uniform Word8 where
uniformM = uniformWord8
instance UniformRange Word8 where
{-# INLINE uniformRM #-}
uniformRM = unsignedBitmaskWithRejectionRM
uniformRM = unbiasedWordMult32RM

instance Uniform Word16 where
{-# INLINE uniformM #-}
uniformM = uniformWord16
instance UniformRange Word16 where
{-# INLINE uniformRM #-}
uniformRM = unsignedBitmaskWithRejectionRM
uniformRM = unbiasedWordMult32RM

instance Uniform Word32 where
{-# INLINE uniformM #-}
uniformM = uniformWord32
instance UniformRange Word32 where
{-# INLINE uniformRM #-}
uniformRM (b, t) g | b > t = (+t) <$> unbiasedWordMult32 (b - t) g
| otherwise = (+b) <$> unbiasedWordMult32 (t - b) g
uniformRM = unbiasedWordMult32RM

instance Uniform Word64 where
{-# INLINE uniformM #-}
Expand Down Expand Up @@ -642,7 +641,7 @@ instance Uniform Char where
{-# INLINE uniformM #-}
instance UniformRange Char where
uniformRM (l, h) g =
word32ToChar <$> unsignedBitmaskWithRejectionRM (charToWord32 l, charToWord32 h) g
word32ToChar <$> unbiasedWordMult32RM (charToWord32 l, charToWord32 h) g
{-# INLINE uniformRM #-}

instance Uniform Bool where
Expand Down Expand Up @@ -804,6 +803,15 @@ uniformIntegerWords n gen = go 0 n
go ((acc `shiftL` WORD_SIZE_IN_BITS) .|. (fromIntegral w)) (i - 1)
{-# INLINE uniformIntegerWords #-}

-- | Uniformly generate an 'Integral' in an inclusive-inclusive range.
--
-- Only use for integrals size less than or equal to that of 'Word32'.
unbiasedWordMult32RM :: (MonadRandom g s m, Integral a) => (a, a) -> g s -> m a
unbiasedWordMult32RM (b, t) g
| b <= t = ((+b) . fromIntegral) <$> unbiasedWordMult32 (fromIntegral (t - b)) g
| otherwise = ((+t) . fromIntegral) <$> unbiasedWordMult32 (fromIntegral (b - t)) g
{-# SPECIALIZE unbiasedWordMult32RM :: MonadRandom g s m => (Word8, Word8) -> g s -> m Word8 #-}

-- | Uniformly generate Word32 in @[0, s]@.
unbiasedWordMult32 :: MonadRandom g s m => Word32 -> g s -> m Word32
unbiasedWordMult32 s g
Expand Down
12 changes: 6 additions & 6 deletions System/Random/Monad.hs
Original file line number Diff line number Diff line change
Expand Up @@ -126,26 +126,26 @@ import System.Random.Internal
-- probabilistic computation as follows:
--
-- >>> monadicGen <- MWC.create
-- >>> rolls 10 monadicGen :: IO [Word8]
-- [2,3,6,6,4,4,3,1,5,4]
-- >>> rolls 12 monadicGen :: IO [Word8]
-- [4,1,2,4,4,5,2,1,5,4,6,6]
--
-- Given a /pure/ pseudo-random number generator, you can run it in an 'IO' or
-- 'ST' context by first applying a monadic adapter like 'AtomicGen', 'IOGen'
-- or 'STGen' and then running it with 'runGenM'.
--
-- >>> let pureGen = mkStdGen 42
-- >>> let pureGen = mkStdGen 41
-- >>> runGenM_ (IOGen pureGen) (rolls 10) :: IO [Word8]
-- [1,1,3,2,4,5,3,4,6,2]
-- [6,4,5,1,1,3,2,4,5,5]
--
-- == How to generate pseudo-random values in pure code
--
-- In pure code, use 'runGenState' and its variants to extract the pure
-- pseudo-random value from a monadic computation based on a pure pseudo-random
-- number generator.
--
-- >>> let pureGen = mkStdGen 42
-- >>> let pureGen = mkStdGen 41
-- >>> runGenState_ pureGen (rolls 10) :: [Word8]
-- [1,1,3,2,4,5,3,4,6,2]
-- [6,4,5,1,1,3,2,4,5,5]

-------------------------------------------------------------------------------
-- Pseudo-random number generator interfaces
Expand Down
145 changes: 136 additions & 9 deletions bench/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import System.Random

main :: IO ()
main = do
let !sz = 1000000
let !sz = 100000
defaultMain
[ bgroup "baseline"
[ let !stdGen = mkStdGen 1337 in bench "nextWord32" $ nf (genMany SM.nextWord32 stdGen) sz
Expand Down Expand Up @@ -67,23 +67,150 @@ main = do
, pureUniformBench @CIntMax sz
, pureUniformBench @CUIntMax sz
]
, bgroup "uniformR"
[ bgroup "full"
[ pureUniformRFullBench @Word8 sz
, pureUniformRFullBench @Word16 sz
, pureUniformRFullBench @Word32 sz
, pureUniformRFullBench @Word64 sz
, pureUniformRFullBench @Word sz
, pureUniformRFullBench @Int8 sz
, pureUniformRFullBench @Int16 sz
, pureUniformRFullBench @Int32 sz
, pureUniformRFullBench @Int64 sz
, pureUniformRFullBench @Int sz
, pureUniformRFullBench @Char sz
, pureUniformRFullBench @Bool sz
, pureUniformRFullBench @CBool sz
, pureUniformRFullBench @CChar sz
, pureUniformRFullBench @CSChar sz
, pureUniformRFullBench @CUChar sz
, pureUniformRFullBench @CShort sz
, pureUniformRFullBench @CUShort sz
, pureUniformRFullBench @CInt sz
, pureUniformRFullBench @CUInt sz
, pureUniformRFullBench @CLong sz
, pureUniformRFullBench @CULong sz
, pureUniformRFullBench @CPtrdiff sz
, pureUniformRFullBench @CSize sz
, pureUniformRFullBench @CWchar sz
, pureUniformRFullBench @CSigAtomic sz
, pureUniformRFullBench @CLLong sz
, pureUniformRFullBench @CULLong sz
, pureUniformRFullBench @CIntPtr sz
, pureUniformRFullBench @CUIntPtr sz
, pureUniformRFullBench @CIntMax sz
, pureUniformRFullBench @CUIntMax sz
]
, bgroup "excludeMax"
[ pureUniformRExcludeMaxBench @Word8 sz
, pureUniformRExcludeMaxBench @Word16 sz
, pureUniformRExcludeMaxBench @Word32 sz
, pureUniformRExcludeMaxBench @Word64 sz
, pureUniformRExcludeMaxBench @Word sz
, pureUniformRExcludeMaxBench @Int8 sz
, pureUniformRExcludeMaxBench @Int16 sz
, pureUniformRExcludeMaxBench @Int32 sz
, pureUniformRExcludeMaxBench @Int64 sz
, pureUniformRExcludeMaxBench @Int sz
, pureUniformRExcludeMaxEnumBench @Char sz
, pureUniformRExcludeMaxEnumBench @Bool sz
, pureUniformRExcludeMaxBench @CBool sz
, pureUniformRExcludeMaxBench @CChar sz
, pureUniformRExcludeMaxBench @CSChar sz
, pureUniformRExcludeMaxBench @CUChar sz
, pureUniformRExcludeMaxBench @CShort sz
, pureUniformRExcludeMaxBench @CUShort sz
, pureUniformRExcludeMaxBench @CInt sz
, pureUniformRExcludeMaxBench @CUInt sz
, pureUniformRExcludeMaxBench @CLong sz
, pureUniformRExcludeMaxBench @CULong sz
, pureUniformRExcludeMaxBench @CPtrdiff sz
, pureUniformRExcludeMaxBench @CSize sz
, pureUniformRExcludeMaxBench @CWchar sz
, pureUniformRExcludeMaxBench @CSigAtomic sz
, pureUniformRExcludeMaxBench @CLLong sz
, pureUniformRExcludeMaxBench @CULLong sz
, pureUniformRExcludeMaxBench @CIntPtr sz
, pureUniformRExcludeMaxBench @CUIntPtr sz
, pureUniformRExcludeMaxBench @CIntMax sz
, pureUniformRExcludeMaxBench @CUIntMax sz
]
, bgroup "includeHalf"
[ pureUniformRIncludeHalfBench @Word8 sz
, pureUniformRIncludeHalfBench @Word16 sz
, pureUniformRIncludeHalfBench @Word32 sz
, pureUniformRIncludeHalfBench @Word64 sz
, pureUniformRIncludeHalfBench @Word sz
, pureUniformRIncludeHalfBench @Int8 sz
, pureUniformRIncludeHalfBench @Int16 sz
, pureUniformRIncludeHalfBench @Int32 sz
, pureUniformRIncludeHalfBench @Int64 sz
, pureUniformRIncludeHalfBench @Int sz
, pureUniformRIncludeHalfEnumBench @Char sz
, pureUniformRIncludeHalfEnumBench @Bool sz
, pureUniformRIncludeHalfBench @CBool sz
, pureUniformRIncludeHalfBench @CChar sz
, pureUniformRIncludeHalfBench @CSChar sz
, pureUniformRIncludeHalfBench @CUChar sz
, pureUniformRIncludeHalfBench @CShort sz
, pureUniformRIncludeHalfBench @CUShort sz
, pureUniformRIncludeHalfBench @CInt sz
, pureUniformRIncludeHalfBench @CUInt sz
, pureUniformRIncludeHalfBench @CLong sz
, pureUniformRIncludeHalfBench @CULong sz
, pureUniformRIncludeHalfBench @CPtrdiff sz
, pureUniformRIncludeHalfBench @CSize sz
, pureUniformRIncludeHalfBench @CWchar sz
, pureUniformRIncludeHalfBench @CSigAtomic sz
, pureUniformRIncludeHalfBench @CLLong sz
, pureUniformRIncludeHalfBench @CULLong sz
, pureUniformRIncludeHalfBench @CIntPtr sz
, pureUniformRIncludeHalfBench @CUIntPtr sz
, pureUniformRIncludeHalfBench @CIntMax sz
, pureUniformRIncludeHalfBench @CUIntMax sz
]
, bgroup "unbounded"
[ pureUniformRBench @Float (1.23e-4, 5.67e8) sz
, pureUniformRBench @Double (1.23e-4, 5.67e8) sz
, let !i = (10 :: Integer) ^ (100 :: Integer)
!range = (-i - 1, i + 1)
in pureUniformRBench @Integer range sz
]
]
]
]

pureRandomBench :: forall a. (Typeable a, Random a) => Int -> Benchmark
pureRandomBench = let !stdGen = mkStdGen 1337 in pureBench @a (genManyRandom @a stdGen)
pureRandomBench = let !stdGen = mkStdGen 1337 in pureBench @a (genMany (random @a) stdGen)

pureUniformBench :: forall a. (Typeable a, Uniform a) => Int -> Benchmark
pureUniformBench = let !stdGen = mkStdGen 1337 in pureBench @a (genManyUniform @a stdGen)
pureUniformBench = let !stdGen = mkStdGen 1337 in pureBench @a (genMany (uniform @_ @a) stdGen)

pureBench :: forall a. (Typeable a) => (Int -> ()) -> Int -> Benchmark
pureBench f sz = bench (showsTypeRep (typeRep (Proxy :: Proxy a)) "") $ nf f sz
pureUniformRFullBench :: forall a. (Typeable a, UniformRange a, Bounded a) => Int -> Benchmark
pureUniformRFullBench = let !range = (minBound @a, maxBound @a) in pureUniformRBench range

pureUniformRExcludeMaxBench :: forall a. (Typeable a, UniformRange a, Bounded a, Num a) => Int -> Benchmark
pureUniformRExcludeMaxBench = let !range = (minBound @a, maxBound @a - 1) in pureUniformRBench range

pureUniformRExcludeMaxEnumBench :: forall a. (Typeable a, UniformRange a, Bounded a, Enum a) => Int -> Benchmark
pureUniformRExcludeMaxEnumBench = let !range = (minBound @a, pred (maxBound @a)) in pureUniformRBench range

genManyRandom :: forall a g. (Random a, RandomGen g) => g -> Int -> ()
genManyRandom = genMany (random @a)
pureUniformRIncludeHalfBench :: forall a. (Typeable a, UniformRange a, Bounded a, Integral a) => Int -> Benchmark
pureUniformRIncludeHalfBench = let !range = (minBound @a, (maxBound @a `div` 2) + 1) in pureUniformRBench range

genManyUniform :: forall a g. (Uniform a, RandomGen g) => g -> Int -> ()
genManyUniform = genMany (uniform @g @a)
pureUniformRIncludeHalfEnumBench :: forall a. (Typeable a, UniformRange a, Bounded a, Enum a) => Int -> Benchmark
pureUniformRIncludeHalfEnumBench =
let !range = (succ (minBound @a), toEnum ((fromEnum (maxBound @a) `div` 2) + 1))
in pureUniformRBench range

pureUniformRBench :: forall a. (Typeable a, UniformRange a) => (a, a) -> Int -> Benchmark
pureUniformRBench range =
let !stdGen = mkStdGen 1337
in pureBench @a (genMany (flip uniformR range) stdGen)

pureBench :: forall a. (Typeable a) => (Int -> ()) -> Int -> Benchmark
pureBench f sz = bench (showsTypeRep (typeRep (Proxy :: Proxy a)) "") $ nf f sz

genMany :: (g -> (a, g)) -> g -> Int -> ()
genMany f g0 n = go g0 0
Expand Down

0 comments on commit d03e325

Please sign in to comment.