Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance #103

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions System/Random.hs
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,14 @@ import qualified System.Random.SplitMix as SM
-- @since 1.2
uniform :: (RandomGen g, Uniform a) => g -> (a, g)
uniform g = runGenState g uniformM

{-# INLINE uniform #-}

-- | Pure version of `uniformRM` that works with instances of `RandomGen`
--
-- @since 1.2
uniformR :: (RandomGen g, UniformRange a) => g -> (a, a) -> (a, g)
uniformR g r = runGenState g (uniformRM r)
uniformR :: (RandomGen g, UniformRange a) => (a, a) -> g -> (a, g)
uniformR r g = runGenState g (uniformRM r)
{-# INLINE uniformR #-}

-- | Generates a 'ByteString' of the specified size using a pure pseudo-random
-- number generator. See 'uniformByteString' for the monadic version.
Expand Down Expand Up @@ -138,7 +139,7 @@ class Random a where
{-# INLINE randomR #-}
randomR :: RandomGen g => (a, a) -> g -> (a, g)
default randomR :: (RandomGen g, UniformRange a) => (a, a) -> g -> (a, g)
randomR r g = runGenState g (uniformRM r)
randomR = uniformR

-- | The same as 'randomR', but using a default range determined by the type:
--
Expand All @@ -152,7 +153,7 @@ class Random a where
{-# INLINE random #-}
random :: RandomGen g => g -> (a, g)
default random :: (RandomGen g, Uniform a) => g -> (a, g)
random g = runGenState g uniformM
random = uniform

-- | Plural variant of 'randomR', producing an infinite list of
-- pseudo-random values instead of returning a new generator.
Expand Down
85 changes: 45 additions & 40 deletions System/Random/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -518,108 +518,108 @@ instance UniformRange Word64 where
instance Uniform CBool where
uniformM = fmap CBool . uniformM
instance UniformRange CBool where
uniformRM (CBool b, CBool t) = fmap CBool . uniformRM (b, t)
uniformRM r = fmap CBool . uniformRM (coerce r)

instance Uniform CChar where
uniformM = fmap CChar . uniformM
instance UniformRange CChar where
uniformRM (CChar b, CChar t) = fmap CChar . uniformRM (b, t)
uniformRM r = fmap CChar . uniformRM (coerce r)

instance Uniform CSChar where
uniformM = fmap CSChar . uniformM
instance UniformRange CSChar where
uniformRM (CSChar b, CSChar t) = fmap CSChar . uniformRM (b, t)
uniformRM r = fmap CSChar . uniformRM (coerce r)

instance Uniform CUChar where
uniformM = fmap CUChar . uniformM
instance UniformRange CUChar where
uniformRM (CUChar b, CUChar t) = fmap CUChar . uniformRM (b, t)
uniformRM r = fmap CUChar . uniformRM (coerce r)

instance Uniform CShort where
uniformM = fmap CShort . uniformM
instance UniformRange CShort where
uniformRM (CShort b, CShort t) = fmap CShort . uniformRM (b, t)
uniformRM r = fmap CShort . uniformRM (coerce r)

instance Uniform CUShort where
uniformM = fmap CUShort . uniformM
instance UniformRange CUShort where
uniformRM (CUShort b, CUShort t) = fmap CUShort . uniformRM (b, t)
uniformRM r = fmap CUShort . uniformRM (coerce r)

instance Uniform CInt where
uniformM = fmap CInt . uniformM
instance UniformRange CInt where
uniformRM (CInt b, CInt t) = fmap CInt . uniformRM (b, t)
uniformRM r = fmap CInt . uniformRM (coerce r)

instance Uniform CUInt where
uniformM = fmap CUInt . uniformM
instance UniformRange CUInt where
uniformRM (CUInt b, CUInt t) = fmap CUInt . uniformRM (b, t)
uniformRM r = fmap CUInt . uniformRM (coerce r)

instance Uniform CLong where
uniformM = fmap CLong . uniformM
instance UniformRange CLong where
uniformRM (CLong b, CLong t) = fmap CLong . uniformRM (b, t)
uniformRM r = fmap CLong . uniformRM (coerce r)

instance Uniform CULong where
uniformM = fmap CULong . uniformM
instance UniformRange CULong where
uniformRM (CULong b, CULong t) = fmap CULong . uniformRM (b, t)
uniformRM r = fmap CULong . uniformRM (coerce r)

instance Uniform CPtrdiff where
uniformM = fmap CPtrdiff . uniformM
instance UniformRange CPtrdiff where
uniformRM (CPtrdiff b, CPtrdiff t) = fmap CPtrdiff . uniformRM (b, t)
uniformRM r = fmap CPtrdiff . uniformRM (coerce r)

instance Uniform CSize where
uniformM = fmap CSize . uniformM
instance UniformRange CSize where
uniformRM (CSize b, CSize t) = fmap CSize . uniformRM (b, t)
uniformRM r = fmap CSize . uniformRM (coerce r)

instance Uniform CWchar where
uniformM = fmap CWchar . uniformM
instance UniformRange CWchar where
uniformRM (CWchar b, CWchar t) = fmap CWchar . uniformRM (b, t)
uniformRM r = fmap CWchar . uniformRM (coerce r)

instance Uniform CSigAtomic where
uniformM = fmap CSigAtomic . uniformM
instance UniformRange CSigAtomic where
uniformRM (CSigAtomic b, CSigAtomic t) = fmap CSigAtomic . uniformRM (b, t)
uniformRM r = fmap CSigAtomic . uniformRM (coerce r)

instance Uniform CLLong where
uniformM = fmap CLLong . uniformM
instance UniformRange CLLong where
uniformRM (CLLong b, CLLong t) = fmap CLLong . uniformRM (b, t)
uniformRM r = fmap CLLong . uniformRM (coerce r)

instance Uniform CULLong where
uniformM = fmap CULLong . uniformM
instance UniformRange CULLong where
uniformRM (CULLong b, CULLong t) = fmap CULLong . uniformRM (b, t)
uniformRM r = fmap CULLong . uniformRM (coerce r)

instance Uniform CIntPtr where
uniformM = fmap CIntPtr . uniformM
instance UniformRange CIntPtr where
uniformRM (CIntPtr b, CIntPtr t) = fmap CIntPtr . uniformRM (b, t)
uniformRM r = fmap CIntPtr . uniformRM (coerce r)

instance Uniform CUIntPtr where
uniformM = fmap CUIntPtr . uniformM
instance UniformRange CUIntPtr where
uniformRM (CUIntPtr b, CUIntPtr t) = fmap CUIntPtr . uniformRM (b, t)
uniformRM r = fmap CUIntPtr . uniformRM (coerce r)

instance Uniform CIntMax where
uniformM = fmap CIntMax . uniformM
instance UniformRange CIntMax where
uniformRM (CIntMax b, CIntMax t) = fmap CIntMax . uniformRM (b, t)
uniformRM r = fmap CIntMax . uniformRM (coerce r)

instance Uniform CUIntMax where
uniformM = fmap CUIntMax . uniformM
instance UniformRange CUIntMax where
uniformRM (CUIntMax b, CUIntMax t) = fmap CUIntMax . uniformRM (b, t)
uniformRM r = fmap CUIntMax . uniformRM (coerce r)

instance UniformRange CFloat where
uniformRM (CFloat l, CFloat h) = fmap CFloat . uniformRM (l, h)
uniformRM r = fmap CFloat . uniformRM (coerce r)

instance UniformRange CDouble where
uniformRM (CDouble l, CDouble h) = fmap CDouble . uniformRM (l, h)
uniformRM r = fmap CDouble . uniformRM (coerce r)


-- The `chr#` and `ord#` are the prim functions that will be called, regardless of which
Expand Down Expand Up @@ -736,19 +736,22 @@ randomIvalInteger (l,h) rng
-- | Generate an 'Integer' in the range @[l, h]@ if @l <= h@ and @[h, l]@
-- otherwise.
uniformIntegerM :: (MonadRandom g s m) => (Integer, Integer) -> g s -> m Integer
uniformIntegerM (l, h) gen = case l `compare` h of
LT -> do
let limit = h - l
let limitAsWord64 :: Word64 = fromIntegral limit
uniformIntegerM (l, h) gen
| l == h = pure l
| otherwise = do
let (limit, low) =
if l < h
then (h - l, l)
else (l - h, h)
limitAsWord64 :: Word64 = fromIntegral limit
bounded <-
if (toInteger limitAsWord64) == limit
if toInteger limitAsWord64 == limit
-- Optimisation: if 'limit' fits into 'Word64', generate a bounded
-- 'Word64' and then convert to 'Integer'
then toInteger <$> unsignedBitmaskWithRejectionM uniformWord64 limitAsWord64 gen
then toInteger <$>
unsignedBitmaskWithRejectionM uniformWord64 limitAsWord64 gen
else boundedExclusiveIntegerM (limit + 1) gen
return $ l + bounded
GT -> uniformIntegerM (h, l) gen
EQ -> pure l
return $ low + bounded
{-# INLINE uniformIntegerM #-}

-- | Generate an 'Integer' in the range @[0, s)@ using a variant of Lemire's
Expand Down Expand Up @@ -837,6 +840,7 @@ unbiasedWordMult32Exclusive r g = go
l :: Word32
l = fromIntegral m
if (l >= t) then return (fromIntegral $ m `shiftR` 32) else go
{-# INLINE unbiasedWordMult32Exclusive #-}

-- | This only works for unsigned integrals
unsignedBitmaskWithRejectionRM ::
Expand All @@ -845,11 +849,10 @@ unsignedBitmaskWithRejectionRM ::
-> g s
-> m a
unsignedBitmaskWithRejectionRM (bottom, top) gen
| bottom > top = unsignedBitmaskWithRejectionRM (top, bottom) gen
| bottom == top = pure top
| otherwise = (bottom +) <$> unsignedBitmaskWithRejectionM uniformM range gen
| otherwise = (b +) <$> unsignedBitmaskWithRejectionM uniformM r gen
where
range = top - bottom
(b, r) = if bottom > top then (top, bottom - top) else (bottom, top - bottom)
{-# INLINE unsignedBitmaskWithRejectionRM #-}

-- | This works for signed integrals by explicit conversion to unsigned and abusing overflow
Expand All @@ -861,13 +864,15 @@ signedBitmaskWithRejectionRM ::
-> g s
-> f b
signedBitmaskWithRejectionRM toUnsigned fromUnsigned (bottom, top) gen
| bottom > top = signedBitmaskWithRejectionRM toUnsigned fromUnsigned (top, bottom) gen
| bottom == top = pure top
| otherwise = (bottom +) . fromUnsigned <$>
unsignedBitmaskWithRejectionM uniformM range gen
where
-- This works in all cases, see Appendix 1 at the end of the file.
range = toUnsigned top - toUnsigned bottom
| otherwise =
(b +) . fromUnsigned <$> unsignedBitmaskWithRejectionM uniformM r gen
-- This works in all cases, see Appendix 1 at the end of the file.
where
(b, r) =
if bottom > top
then (top, toUnsigned bottom - toUnsigned top)
else (bottom, toUnsigned top - toUnsigned bottom)
{-# INLINE signedBitmaskWithRejectionRM #-}

unsignedBitmaskWithRejectionM :: (Ord a, FiniteBits a, Num a, MonadRandom g s m) => (g s -> m a) -> a -> g s -> m a
Expand Down
2 changes: 1 addition & 1 deletion bench/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ pureUniformRIncludeHalfEnumBench =
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)
in pureBench @a (genMany (uniformR range) stdGen)

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