File: Range.hs

package info (click to toggle)
haskell-splitmix 0.1.0.5-2
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 196 kB
  • sloc: haskell: 1,366; ansic: 151; sh: 53; makefile: 9
file content (108 lines) | stat: -rw-r--r-- 2,780 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
-- http://www.pcg-random.org/posts/bounded-rands.html
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP          #-}
module Main where

import Data.Bits
import Data.Bits.Compat
import Data.List        (unfoldr)
import Data.Word        (Word32, Word64)

import qualified System.Random.SplitMix32 as SM

#if defined(__GHCJS__)
#else
import System.Clock (Clock (Monotonic), getTime, toNanoSecs)
import Text.Printf  (printf)
#endif

main :: IO ()
main = do
    gen <- SM.newSMGen

    -- bench gen (\g h -> R (0, pred h) g)
    bench gen classicMod
    bench gen intMult
    bench gen bitmaskWithRejection

bench :: g -> (g -> Word32 -> (Word32, g)) -> IO ()
bench gen next = do
    print $ take 70 $ unfoldr (\g -> Just (next g 10)) gen
    clocked $ do
        let x = sumOf next gen
        print x

sumOf :: (g -> Word32 -> (Word32, g)) -> g -> Word32
sumOf next = go 0 2
  where
    go !acc !n g | n > 0xfffff = acc
                 | otherwise    = let (w, g') = next g n in go (acc + w) (succ n) g'

classicMod :: SM.SMGen -> Word32 -> (Word32, SM.SMGen)
classicMod g h =
    let (w32, g') = SM.nextWord32 g in (w32 `mod` h, g')


-- @
-- uint32_t bounded_rand(rng_t& rng, uint32_t range) {
--     uint32_t x = rng();
--     uint64_t m = uint64_t(x) * uint64_t(range);
--     return m >> 32;
-- }
-- @
--
intMult :: SM.SMGen -> Word32 -> (Word32, SM.SMGen)
intMult g h =
    (fromIntegral $ (fromIntegral w32 * fromIntegral h :: Word64) `shiftR` 32, g')
  where
    (w32, g') = SM.nextWord32 g

-- @
-- uint32_t bounded_rand(rng_t& rng, uint32_t range) {
--     uint32_t mask = ~uint32_t(0);
--     --range;
--     mask >>= __builtin_clz(range|1);
--     uint32_t x;
--     do {
--         x = rng() & mask;
--     } while (x > range);
--     return x;
-- }
-- @@
bitmaskWithRejection :: SM.SMGen -> Word32 -> (Word32, SM.SMGen)
bitmaskWithRejection g0 range = go g0
  where
    mask = complement zeroBits `shiftR` countLeadingZeros (range .|. 1)
    go g = let (x, g') = SM.nextWord32 g
               x' = x .&. mask
           in if x' >= range
              then go g'
              else (x', g')

-------------------------------------------------------------------------------
-- Poor man benchmarking with GHC and GHCJS
-------------------------------------------------------------------------------

clocked :: IO () -> IO ()
#if defined(__GHCJS__)
clocked action = do
    start
    action
    stop

foreign import javascript unsafe
    "console.time('loop');"
    start :: IO ()

foreign import javascript unsafe
    "console.timeEnd('loop');"
    stop :: IO ()
#else
clocked action =  do
    start <- getTime Monotonic
    action
    end <- getTime Monotonic
    printf "loop: %.03fms\n"
        $ fromIntegral (toNanoSecs (end - start))
        / (1e6 :: Double)
#endif