Skip to content

Commit

Permalink
Third implementation, with xoshiro256**
Browse files Browse the repository at this point in the history
❯ time .stack-work/install/x86_64-osx/lts-13.21/8.6.5/bin/bingo-sim 100000
Trials:   100000
Bingos:   3670
Hit rate: 0.0367
    0.11s user 0.01s system 90% cpu 0.126 total
  • Loading branch information
jez committed May 19, 2019
1 parent 8886a66 commit 0a04839
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 13 deletions.
5 changes: 3 additions & 2 deletions bingo-sim.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ cabal-version: 1.12
--
-- see: https://github.com/sol/hpack
--
-- hash: 26378589030ced4d8f54066cd640d122b18b9474d2082849b5a315cade4a9319
-- hash: 3fa6868be54f56e844ce88095718d2f5be4a44d809a636babdeadda66d9faeba

name: bingo-sim
version: 0.0.2.0
version: 0.0.3.0
synopsis: A small playground to learn about profiling Haskell.

description: This package simulates the probability of scoring a bingo at a particular children's carnival game. I've been using it to learn how profiling in Haskell works.
Expand All @@ -25,6 +25,7 @@ extra-source-files:
library
exposed-modules:
BingoSim.Board
BingoSim.Prng
BingoSim.Simulation
other-modules:
Paths_bingo_sim
Expand Down
2 changes: 1 addition & 1 deletion package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: bingo-sim
version: 0.0.2.0
version: 0.0.3.0
homepage: https://github.com/jez/bingo-sim
author: Jake Zimmerman
maintainer: zimmerman.jake@gmail.com
Expand Down
91 changes: 91 additions & 0 deletions prof/bingo-sim.3.prof
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
Mon May 20 01:36 2019 Time and Allocation Profiling Report (Final)

bingo-sim +RTS -p -RTS 100000

total time = 0.49 secs (487 ticks @ 1000 us, 1 processor)
total alloc = 976,788,160 bytes (excludes profiling overheads)

COST CENTRE MODULE SRC %time %alloc

next BingoSim.Prng src/BingoSim/Prng.hs:(69,1)-(81,39) 39.2 48.7
shuffleBits BingoSim.Simulation src/BingoSim/Simulation.hs:(121,1)-(127,37) 26.5 34.4
shuffleBits.bs' BingoSim.Simulation src/BingoSim/Simulation.hs:126:7-52 15.0 5.7
swapBits BingoSim.Simulation src/BingoSim/Simulation.hs:(139,1)-(142,46) 8.8 5.2
next.result BingoSim.Prng src/BingoSim/Prng.hs:70:7-41 3.7 5.7
shuffleBits.gen' BingoSim.Simulation src/BingoSim/Simulation.hs:124:7-29 1.4 0.0
shuffleBits.(...) BingoSim.Simulation src/BingoSim/Simulation.hs:124:7-29 1.4 0.0


individual inherited
COST CENTRE MODULE SRC no. entries %time %alloc %time %alloc

MAIN MAIN <built-in> 193 0 0.0 0.0 100.0 100.0
CAF GHC.Conc.Signal <entire-module> 290 0 0.0 0.0 0.0 0.0
CAF GHC.Float <entire-module> 282 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding <entire-module> 272 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding.Iconv <entire-module> 270 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Handle.FD <entire-module> 261 0 0.0 0.0 0.0 0.0
CAF Text.Printf <entire-module> 224 0 0.0 0.0 0.0 0.0
CAF Text.Read.Lex <entire-module> 222 0 0.0 0.0 0.0 0.0
CAF:col1 BingoSim.Board src/BingoSim/Board.hs:148:1-4 345 0 0.0 0.0 0.0 0.0
col1 BingoSim.Board src/BingoSim/Board.hs:148:1-45 421 1 0.0 0.0 0.0 0.0
CAF:col2 BingoSim.Board src/BingoSim/Board.hs:151:1-4 344 0 0.0 0.0 0.0 0.0
col2 BingoSim.Board src/BingoSim/Board.hs:151:1-45 422 1 0.0 0.0 0.0 0.0
CAF:col3 BingoSim.Board src/BingoSim/Board.hs:154:1-4 343 0 0.0 0.0 0.0 0.0
col3 BingoSim.Board src/BingoSim/Board.hs:154:1-45 423 1 0.0 0.0 0.0 0.0
CAF:col4 BingoSim.Board src/BingoSim/Board.hs:157:1-4 342 0 0.0 0.0 0.0 0.0
col4 BingoSim.Board src/BingoSim/Board.hs:157:1-45 424 1 0.0 0.0 0.0 0.0
CAF:col5 BingoSim.Board src/BingoSim/Board.hs:160:1-4 341 0 0.0 0.0 0.0 0.0
col5 BingoSim.Board src/BingoSim/Board.hs:160:1-45 425 1 0.0 0.0 0.0 0.0
CAF:col6 BingoSim.Board src/BingoSim/Board.hs:163:1-4 339 0 0.0 0.0 0.0 0.0
col6 BingoSim.Board src/BingoSim/Board.hs:163:1-45 426 1 0.0 0.0 0.0 0.0
CAF:dia1 BingoSim.Board src/BingoSim/Board.hs:124:1-4 354 0 0.0 0.0 0.0 0.0
dia1 BingoSim.Board src/BingoSim/Board.hs:124:1-45 413 1 0.0 0.0 0.0 0.0
CAF:dia2 BingoSim.Board src/BingoSim/Board.hs:127:1-4 353 0 0.0 0.0 0.0 0.0
dia2 BingoSim.Board src/BingoSim/Board.hs:127:1-45 414 1 0.0 0.0 0.0 0.0
CAF:lvl1_r631 BingoSim.Simulation <no location info> 311 0 0.0 0.0 0.0 0.0
CAF:lvl3_r633 BingoSim.Simulation <no location info> 312 0 0.0 0.0 0.0 0.0
CAF:lvl5_r635 BingoSim.Simulation <no location info> 313 0 0.0 0.0 0.0 0.0
CAF:lvl6_r636 BingoSim.Simulation <no location info> 314 0 0.0 0.0 0.0 0.0
runSimulation BingoSim.Simulation src/BingoSim/Simulation.hs:(68,1)-(84,41) 397 0 0.0 0.0 0.0 0.0
mkState BingoSim.Prng src/BingoSim/Prng.hs:(55,1)-(57,39) 398 1 0.0 0.0 0.0 0.0
CAF:main1 Main <no location info> 384 0 0.0 0.0 0.0 0.0
main Main app/Main.hs:(10,1)-(12,30) 386 1 0.0 0.0 0.0 0.0
CAF:main4 Main <no location info> 381 0 0.0 0.0 0.0 0.0
CAF:row1 BingoSim.Board src/BingoSim/Board.hs:130:1-4 351 0 0.0 0.0 0.0 0.0
row1 BingoSim.Board src/BingoSim/Board.hs:130:1-45 415 1 0.0 0.0 0.0 0.0
CAF:row2 BingoSim.Board src/BingoSim/Board.hs:133:1-4 350 0 0.0 0.0 0.0 0.0
row2 BingoSim.Board src/BingoSim/Board.hs:133:1-45 416 1 0.0 0.0 0.0 0.0
CAF:row3 BingoSim.Board src/BingoSim/Board.hs:136:1-4 349 0 0.0 0.0 0.0 0.0
row3 BingoSim.Board src/BingoSim/Board.hs:136:1-45 417 1 0.0 0.0 0.0 0.0
CAF:row4 BingoSim.Board src/BingoSim/Board.hs:139:1-4 348 0 0.0 0.0 0.0 0.0
row4 BingoSim.Board src/BingoSim/Board.hs:139:1-45 418 1 0.0 0.0 0.0 0.0
CAF:row5 BingoSim.Board src/BingoSim/Board.hs:142:1-4 347 0 0.0 0.0 0.0 0.0
row5 BingoSim.Board src/BingoSim/Board.hs:142:1-45 419 1 0.0 0.0 0.0 0.0
CAF:row6 BingoSim.Board src/BingoSim/Board.hs:145:1-4 346 0 0.0 0.0 0.0 0.0
row6 BingoSim.Board src/BingoSim/Board.hs:145:1-45 420 1 0.0 0.0 0.0 0.0
main Main app/Main.hs:(10,1)-(12,30) 387 0 0.0 0.0 100.0 100.0
main.\ Main app/Main.hs:11:13-37 388 1 0.0 0.0 100.0 100.0
runSimulation BingoSim.Simulation src/BingoSim/Simulation.hs:(68,1)-(84,41) 389 1 0.6 0.0 100.0 100.0
hasBingo BingoSim.Board src/BingoSim/Board.hs:(82,1)-(96,32) 412 100000 0.6 0.0 0.6 0.0
randomBoard BingoSim.Simulation src/BingoSim/Simulation.hs:(108,1)-(110,35) 390 100000 0.6 0.2 98.8 100.0
shuffleBits BingoSim.Simulation src/BingoSim/Simulation.hs:(121,1)-(127,37) 391 3600000 26.5 34.4 98.2 99.8
shuffleBits.(...) BingoSim.Simulation src/BingoSim/Simulation.hs:124:7-29 394 3500000 1.4 0.0 45.6 54.5
next BingoSim.Prng src/BingoSim/Prng.hs:(69,1)-(81,39) 395 3500000 39.2 48.7 44.1 54.5
next.result BingoSim.Prng src/BingoSim/Prng.hs:70:7-41 406 3500000 3.7 5.7 3.7 5.7
next.s0' BingoSim.Prng src/BingoSim/Prng.hs:77:7-27 402 3499999 0.2 0.0 0.2 0.0
next.s1' BingoSim.Prng src/BingoSim/Prng.hs:76:7-27 403 3499999 0.4 0.0 0.4 0.0
next.s2' BingoSim.Prng src/BingoSim/Prng.hs:74:7-26 400 3499999 0.2 0.0 0.2 0.0
next.s2'' BingoSim.Prng src/BingoSim/Prng.hs:79:7-26 404 3499999 0.0 0.0 0.0 0.0
next.s3' BingoSim.Prng src/BingoSim/Prng.hs:75:7-26 399 3499999 0.2 0.0 0.2 0.0
next.s3'' BingoSim.Prng src/BingoSim/Prng.hs:80:7-31 405 3499999 0.0 0.0 0.0 0.0
next.t BingoSim.Prng src/BingoSim/Prng.hs:72:7-35 401 3499999 0.2 0.0 0.2 0.0
shuffleBits.bs' BingoSim.Simulation src/BingoSim/Simulation.hs:126:7-52 408 3500000 15.0 5.7 24.2 10.9
swapBits BingoSim.Simulation src/BingoSim/Simulation.hs:(139,1)-(142,46) 410 3500000 8.8 5.2 9.2 5.2
swapBits.x BingoSim.Simulation src/BingoSim/Simulation.hs:141:7-53 411 3182752 0.4 0.0 0.4 0.0
shuffleBits.i BingoSim.Simulation src/BingoSim/Simulation.hs:125:7-48 407 3500000 0.2 0.0 0.2 0.0
shuffleBits.n' BingoSim.Simulation src/BingoSim/Simulation.hs:123:7-26 392 3500000 0.2 0.0 0.2 0.0
shuffleBits.rand BingoSim.Simulation src/BingoSim/Simulation.hs:124:7-29 393 3500000 0.0 0.0 0.0 0.0
shuffleBits.gen' BingoSim.Simulation src/BingoSim/Simulation.hs:124:7-29 396 3499999 1.4 0.0 1.4 0.0
randomBoard.board BingoSim.Simulation src/BingoSim/Simulation.hs:109:7-26 409 100000 0.0 0.0 0.0 0.0
runSimulation.rate BingoSim.Simulation src/BingoSim/Simulation.hs:81:7-58 427 1 0.0 0.0 0.0 0.0
98 changes: 98 additions & 0 deletions src/BingoSim/Prng.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
-- |
-- This is a port of
-- [xoshiro256**](http://xoshiro.di.unimi.it/xoshiro256starstar.c) from C to
-- Haskell. The documentation and comments below come from the original C
-- sources.
--
-- = License
--
-- Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
--
-- To the extent possible under law, the author has dedicated all copyright
-- and related and neighboring rights to this software to the public domain
-- worldwide. This software is distributed without any warranty.
--
-- See <http://creativecommons.org/publicdomain/zero/1.0/>.

module BingoSim.Prng
( State
, mkState
, next
, jump
, longJump
)
where

import Data.Bits
import Data.Word

-- | The state of the generator.
--
-- The state must be seeded so that it is not everywhere zero. If you have
-- a 64-bit seed, we suggest to seed a splitmix64 generator and use its
-- output to fill s.
--
-- In Haskell, we've made this type opaque. See 'mkState' to construct a 'State'.
data State = State
{-# UNPACK #-} !Word64
{-# UNPACK #-} !Word64
{-# UNPACK #-} !Word64
{-# UNPACK #-} !Word64

instance Show State where
show (State s0 s1 s2 s3) =
"mkState " ++ (show s0) ++ " " ++ (show s1) ++ " " ++ (show s2) ++ " " ++ (show s3)

-- | Create an initial state from a seed.
--
-- Raises an exception if the initial seed is all zeros.
--
-- >>> mkState 0 0 0 0
-- *** Exception: The state must be seeded so that it is not zero everywhere.
-- >>> mkState 1 2 3 4
-- mkState 1 2 3 4
mkState :: Word64 -> Word64 -> Word64 -> Word64 -> State
mkState 0 0 0 0 =
error "The state must be seeded so that it is not zero everywhere."
mkState s0 s1 s2 s3 = State s0 s1 s2 s3

-- | This is xoshiro256** 1.0, our all-purpose, rock-solid generator. It has
-- excellent (sub-ns) speed, a state (256 bits) that is large enough for
-- any parallel application, and it passes all tests we are aware of.
--
-- For generating just floating-point numbers, xoshiro256+ is even faster.
--
-- >>> let state = mkState 1 2 3 4
-- >>> next state
-- (11520,mkState 7 0 262146 211106232532992)
next :: State -> (Word64, State)
next (State s0 s1 s2 s3) =
let result = ((s1 * 5) `rotateL` 7) * 9

t = s1 `unsafeShiftL` 17

s2' = s2 `xor` s0
s3' = s3 `xor` s1
s1' = s1 `xor` s2'
s0' = s0 `xor` s3'

s2'' = s2' `xor` t
s3'' = s3' `rotateL` 45
in (result, State s0' s1' s2'' s3'')

-- | This is the jump function for the generator. It is equivalent
-- to 2^128 calls to next(); it can be used to generate 2^128
-- non-overlapping subsequences for parallel computations.
--
-- Note: This function is not yet implemented in Haskell.
jump :: State -> State
jump (State _s0 _s1 _s2 _s3) = undefined

-- | This is the long-jump function for the generator. It is equivalent to
-- 2^192 calls to next(); it can be used to generate 2^64 starting points,
-- from each of which jump() will generate 2^64 non-overlapping
-- subsequences for parallel distributed computations.
--
-- Note: This function is not yet implemented in Haskell.
longJump :: State -> State
longJump (State _s0 _s1 _s2 _s3) = undefined
21 changes: 11 additions & 10 deletions src/BingoSim/Simulation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ import Control.Monad
import Data.Bits
import Data.IORef
import Data.Word
import System.Random
import Text.Printf

import BingoSim.Board
import BingoSim.Prng (mkState, next)
import qualified BingoSim.Prng as Prng

-- | Run the entire simulation, consisting of @trials@ trials.
--
Expand All @@ -66,7 +67,7 @@ runSimulation
-> IO ()
runSimulation trials = do
count <- newIORef 0
genRef <- newStdGen >>= newIORef
genRef <- newIORef (mkState 111 222 333 444)

replicateM_ trials $ do
gen <- readIORef genRef
Expand Down Expand Up @@ -103,7 +104,7 @@ runSimulation trials = do
--
-- The sacrifice is that the naive strategy nearly exactly matches our
-- intuition for how this game works in the real world.
randomBoard :: RandomGen g => g -> IO (Board, g)
randomBoard :: Prng.State -> IO (Board, Prng.State)
randomBoard gen = do
let board = Board 0x7fff
return $ shuffleBits gen board 36
Expand All @@ -113,16 +114,16 @@ randomBoard gen = do
-- Uses recursion to swap the current bit into place, from most to least
-- significant.
shuffleBits
:: RandomGen g
=> g
:: Prng.State
-> Board
-> Int -- ^ @n@: The current bit we're considering swapping or leaving alone.
-> (Board, g)
-> Int -- ^ @n@: The current bit we're considering swapping or leaving alone (1-indexed).
-> (Board, Prng.State)
shuffleBits gen board 1 = (board, gen)
shuffleBits gen (Board bs) n =
let n' = n - 1
(i, gen') = randomR (0, n') gen
bs' = swapBits bs n' i
let n' = n - 1
(rand, gen') = next gen
i = rand `mod` (fromIntegral n)
bs' = swapBits bs n' (fromIntegral i)
in shuffleBits gen' (Board bs') n'

-- | Helper for swapping two specific bits.
Expand Down

0 comments on commit 0a04839

Please sign in to comment.