Hi again,

Still playing with the Mersenne Twister and here is the
updated 64 bit version so that there are not so many 
constructor calls on next64 (together with updated
compiling flags). 

I was wondering why different runs can have such different
run times and the cause was found to be my system: also the
C version running times can vary (usually 0.65 but sometimes 0.3).

The 64 bit version took usually about 1.1 or 1.2 seconds while
32bit version required only 0.78 (against 0.65 with C for both
32 and 64 bit versions).

Since the real work horse here is the next64 function, I took a
look of Core.  There seems to be an extra case-statement in
64bit version and this might explain the performance drop (about
6 or 7 lines below _DEFAULT text on both versions below). 
Relevant parts of the Core below, code attached.  It is very 
possible that I'm missing something obvious here.

So what is happening here?  :)  

Thanks again for any comments!  

br, Isto

-------------------------------------------- Core (32 and 64 nexts)
Rec {
Mersenne.$wnext64 :: Data.Array.IO.Internals.IOUArray GHC.Base.Int
                                                      GHC.Word.Word64
                     -> GHC.Prim.Int#
                     -> GHC.Prim.State# GHC.Prim.RealWorld
                     -> (# GHC.Prim.State# GHC.Prim.RealWorld, (GHC.Word.Word64,
GHC.Base.Int) #)
[GlobalId]
[Arity 3
 Str: DmdType LLL]
Mersenne.$wnext64 =
  \ (w_s2Zq :: Data.Array.IO.Internals.IOUArray GHC.Base.Int
GHC.Word.Word64)
    (ww_s2Zt :: GHC.Prim.Int#)
    (w1_s2Zv :: GHC.Prim.State# GHC.Prim.RealWorld) ->
    case ww_s2Zt of ds_X2F1 {
      __DEFAULT ->
        case w_s2Zq
        of wild_a2Pp { Data.Array.Base.STUArray ds2_a2Pr ds3_a2Ps marr#_a2Pt ->
        case GHC.Prim.readWord64Array# @ GHC.Prim.RealWorld marr#_a2Pt ds_X2F1
w1_s2Zv
        of wild2_a2PA { (# s2#_a2PC, e#_a2PD #) ->
        (# s2#_a2PC,
           ((case lit_r34C of wild1_a2Ol { GHC.Word.W64# y#_a2On ->
             let {
               ww1_a2NY [Just L] :: GHC.Prim.Word#
               [Str: DmdType]
               ww1_a2NY =
                 GHC.Prim.xor#
                   e#_a2PD
                   (GHC.Prim.and#
                      (GHC.Prim.uncheckedShiftRL# e#_a2PD 29) __word
6148914691236517205) } in
             let {
               ww2_X2Q0 [Just L] :: GHC.Prim.Word#
               [Str: DmdType]
               ww2_X2Q0 =
                 GHC.Prim.xor#
                   ww1_a2NY
                   (GHC.Prim.and#
                      (GHC.Prim.uncheckedShiftL# ww1_a2NY 17) __word
8202884508482404352) } in
             let {
               ww3_X2QE [Just L] :: GHC.Prim.Word#
               [Str: DmdType]
               ww3_X2QE =
                 GHC.Prim.xor#
                   ww2_X2Q0 (GHC.Prim.and# (GHC.Prim.uncheckedShiftL# ww2_X2Q0 
37)
y#_a2On)
             } in 
               GHC.Word.W64# (GHC.Prim.xor# ww3_X2QE
(GHC.Prim.uncheckedShiftRL# ww3_X2QE 43))
             }),
            (GHC.Base.I# (GHC.Prim.+# ds_X2F1 1))) #)
        }
        };
      312 ->
        case Mersenne.generateNumbers64 w_s2Zq w1_s2Zv
        of wild_a2DL { (# new_s_a2DN, a87_a2DO #) ->
        case Mersenne.$wnext64 w_s2Zq 0 new_s_a2DN
        of wild1_X2Fy { (# new_s1_X2FB, a871_X2FD #) ->
        case a871_X2FD of wild2_Xar { (w2_aU2, iN_aU3) -> (# new_s1_X2FB,
wild2_Xar #) }
        }
        }
    }
end Rec }

Mersenne.next64 :: Data.Array.IO.Internals.IOUArray GHC.Base.Int
GHC.Word.Word64
                   -> GHC.Base.Int
                   -> GHC.IOBase.IO (GHC.Word.Word64, GHC.Base.Int)
[GlobalId]
[Arity 3
 Worker Mersenne.$wnext64
 Str: DmdType LU(L)L]
Mersenne.next64 =
  __inline_me (\ (w_s2Zq :: Data.Array.IO.Internals.IOUArray
GHC.Base.Int
                                                             GHC.Word.Word64)
                 (w1_s2Zr :: GHC.Base.Int)
                 (w2_s2Zv :: GHC.Prim.State# GHC.Prim.RealWorld) ->
                 case w1_s2Zr of w3_X30R { GHC.Base.I# ww_s2Zt ->
                 Mersenne.$wnext64 w_s2Zq ww_s2Zt w2_s2Zv
                 })





Rec {
Mersenne.$wnext32 :: Data.Array.IO.Internals.IOUArray GHC.Base.Int
                                                      GHC.Word.Word32
                     -> GHC.Prim.Int#
                     -> GHC.Prim.State# GHC.Prim.RealWorld
                     -> (# GHC.Prim.State# GHC.Prim.RealWorld, (GHC.Word.Word32,
GHC.Base.Int) #)
[GlobalId]
[Arity 3
 NoCafRefs
 Str: DmdType LLL]
Mersenne.$wnext32 =
  \ (w_s2YJ :: Data.Array.IO.Internals.IOUArray GHC.Base.Int
GHC.Word.Word32)
    (ww_s2YM :: GHC.Prim.Int#)
    (w1_s2YO :: GHC.Prim.State# GHC.Prim.RealWorld) ->
    case ww_s2YM of ds_X2CS {
      __DEFAULT ->
        case w_s2YJ
        of wild_a2Hd { Data.Array.Base.STUArray ds2_a2Hf ds3_a2Hj marr#_a2Hk ->
        case GHC.Prim.readWord32Array# @ GHC.Prim.RealWorld marr#_a2Hk ds_X2CS
w1_s2YO
        of wild2_a2Hr { (# s2#_a2Ht, e#_a2Hu #) ->
        (# s2#_a2Ht,
           ((let {
               ww1_a2Fr [Just L] :: GHC.Prim.Word#
               [Str: DmdType]
               ww1_a2Fr = GHC.Prim.xor# e#_a2Hu (GHC.Prim.uncheckedShiftRL#
e#_a2Hu 11) } in
             let {
               ww2_X2GX [Just L] :: GHC.Prim.Word#
               [Str: DmdType]
               ww2_X2GX =
                 GHC.Prim.xor#
                   ww1_a2Fr
                   (GHC.Prim.and#
                      (GHC.Prim.narrow32Word# (GHC.Prim.uncheckedShiftL# 
ww1_a2Fr 7))
                      __word 2636928640) } in
             let {
               ww3_X2Hp [Just L] :: GHC.Prim.Word#
               [Str: DmdType]
               ww3_X2Hp =
                 GHC.Prim.xor#
                   ww2_X2GX
                   (GHC.Prim.and#
                      (GHC.Prim.narrow32Word# (GHC.Prim.uncheckedShiftL# 
ww2_X2GX 15))
                      __word 4022730752)
             } in 
               GHC.Word.W32#
                 (GHC.Prim.xor# ww3_X2Hp (GHC.Prim.uncheckedShiftRL# ww3_X2Hp 
18))),
            (GHC.Base.I# (GHC.Prim.+# ds_X2CS 1))) #)
        }
        };
      624 ->
        case Mersenne.generateNumbers32 w_s2YJ w1_s2YO
        of wild_a2DL { (# new_s_a2DN, a87_a2DO #) ->
        case Mersenne.$wnext32 w_s2YJ 0 new_s_a2DN
        of wild1_X2F2 { (# new_s1_X2F5, a871_X2F7 #) ->
        case a871_X2F7 of wild2_X80 { (w2_aSH, iN_aSI) -> (# new_s1_X2F5,
wild2_X80 #) }
        }
        }
    }
end Rec }

Mersenne.next32 :: Data.Array.IO.Internals.IOUArray GHC.Base.Int
GHC.Word.Word32
                   -> GHC.Base.Int
                   -> GHC.IOBase.IO (GHC.Word.Word32, GHC.Base.Int)
[GlobalId]
[Arity 3
 Worker Mersenne.$wnext32
 NoCafRefs
 Str: DmdType LU(L)L]
Mersenne.next32 =
  __inline_me (\ (w_s2YJ :: Data.Array.IO.Internals.IOUArray
GHC.Base.Int
                                                             GHC.Word.Word32)
                 (w1_s2YK :: GHC.Base.Int)
                 (w2_s2YO :: GHC.Prim.State# GHC.Prim.RealWorld) ->
                 case w1_s2YK of w3_X2ZO { GHC.Base.I# ww_s2YM ->
                 Mersenne.$wnext32 w_s2YJ ww_s2YM w2_s2YO
                 })


module Mersenne where

import Data.Bits
import Data.Word
import Data.Array.Base
import Data.Array.MArray
import Data.Array.IO

data MT32 = MT32 (IOUArray Int Word32) Int
data MT64 = MT64 (IOUArray Int Word64) Int

last32bitsof :: Word32 -> Word32 
last32bitsof a = a .&. 0xffffffff -- == (2^32-1)  

lm32 = 0x7fffffff :: Word32
um32 = 0x80000000 :: Word32
mA32 = 0x9908b0df :: Word32 -- == 2567483615

-- Array of length 624.
initialiseGenerator32 :: Int -> IO MT32 
initialiseGenerator32 seed = do
	let s = last32bitsof (fromIntegral seed)::Word32
	mt <- newArray (0,623) (0::Word32)
	unsafeWrite mt 0 s
	mtLoop32 mt s 1
	generateNumbers32 mt
	return (MT32 mt 0)

mtLoop32 :: (IOUArray Int Word32) -> Word32 -> Int -> IO () 
mtLoop32 mt lastNro n = loop lastNro n
	where
		loop :: Word32 -> Int -> IO () 
		loop lastNro 624 = return ()
		loop lastNro n   = do 
				let n1 = lastNro `xor` (shiftR lastNro 30)
				    new = (1812433253 * n1 + (fromIntegral n)::Word32) 
				unsafeWrite mt n new
				loop new $! (n+1)


generateNumbers32 :: (IOUArray Int Word32) -> IO ()
generateNumbers32 mt = do
	gLoop1 0 
	gLoop2 227
	wL <- unsafeRead mt 623
	w0 <- unsafeRead mt 0
	w396 <- unsafeRead mt 396
	let y = (wL .&. um32) .|. (w0 .&. lm32) :: Word32
	if even y 
		then unsafeWrite mt 623 (w396 `xor` (shiftR y 1))
		else unsafeWrite mt 623 (w396 `xor` (shiftR y 1) `xor` mA32)
	return ()
	where
		gLoop1 :: Int -> IO ()
		gLoop1 227  = return ()
		gLoop1 i    = do 
			wi  <- unsafeRead mt i
			wi1 <- unsafeRead mt (i+1) 
			w3  <- unsafeRead mt (i+397)
			let y = (wi .&. um32) .|. (wi1 .&. lm32)
			if even y 
			   then unsafeWrite mt i (w3 `xor` (shiftR y 1))
			   else unsafeWrite mt i (w3 `xor` (shiftR y 1) `xor` mA32)
			gLoop1 $! (i+1)
		gLoop2 :: Int -> IO ()
		gLoop2 623  = return ()
		gLoop2 i    = do 
			wi  <- unsafeRead mt i
			wi1 <- unsafeRead mt (i+1) 
			w3  <- unsafeRead mt (i-227)
			let y = (wi .&. um32) .|. (wi1 .&. lm32)
			if even y 
			   then unsafeWrite mt i (w3 `xor` (shiftR y 1))
			   else unsafeWrite mt i (w3 `xor` (shiftR y 1) `xor` mA32)
			gLoop2 $! (i+1)

next32 :: IOUArray Int Word32 -> Int -> IO (Word32, Int)
next32 mt 624 = do 
	generateNumbers32 mt
	(w,iN) <- next32 mt 0
	return (w,iN)
next32 mt i   = do 
	y <- unsafeRead mt i
	let y1 = y  `xor`  (shiftR y  11)
	    y2 = y1 `xor` ((shiftL y1 7 ) .&. 0x9d2c5680) -- == 2636928640
	    y3 = y2 `xor` ((shiftL y2 15) .&. 0xefc60000) -- == 4022730752
	    y4 = y3 `xor`  (shiftR y3 18) 
	return $ (y4, (i+1))



mA64 = 0xB5026F5AA96619E9 :: Word64
um64 = 0xFFFFFFFF80000000 :: Word64
lm64 = 0x7FFFFFFF :: Word64

initialiseGenerator64 :: Int -> IO (MT64)
initialiseGenerator64 seed = do
	let s = (fromIntegral seed)::Word64
	mt <- newArray (0,311) (0::Word64)
	unsafeWrite mt 0 s
	mtLoop64 mt s 1
	generateNumbers64 mt
	return (MT64 mt 0)

mtLoop64 :: (IOUArray Int Word64) -> Word64 -> Int -> IO () 
mtLoop64 mt lastNro n = loop lastNro n
	where
		loop :: Word64 -> Int -> IO ()
		loop lastNro 312 = return ()
		loop lastNro n   = do 
			let n1 = lastNro `xor` (shiftR lastNro 62)
            		    new = (6364136223846793005 * n1 + (fromIntegral n)::Word64) 
			unsafeWrite mt n new
			loop new $! (n+1)

generateNumbers64 :: (IOUArray Int Word64)  -> IO ()
generateNumbers64 mt = do
	gLoop1 0 
	gLoop2 156 
	wL <- unsafeRead mt 311
	w0 <- unsafeRead mt 0
	w155 <- unsafeRead mt 155
	let y = (wL .&. um64) .|. (w0 .&. lm64) :: Word64
	if even y  
		then unsafeWrite mt 311 (w155 `xor` (shiftR y 1))
		else unsafeWrite mt 311 (w155 `xor` (shiftR y 1) `xor` mA64)
	return ()
	where
		gLoop1 :: Int -> IO ()
		gLoop1 156 = return ()
		gLoop1 i   = do
			wi  <- unsafeRead mt i
			wi1 <- unsafeRead mt (i+1) 
			w3  <- unsafeRead mt (i+156)
			let y = (wi .&. um64) .|. (wi1 .&. lm64)
			if even y 
			   then unsafeWrite mt i (w3 `xor` (shiftR y 1))
			   else unsafeWrite mt i (w3 `xor` (shiftR y 1) `xor` mA64)
			gLoop1 $! (i+1) 

		gLoop2 :: Int -> IO ()
		gLoop2 311 = return ()
		gLoop2 i   = do
			wi  <- unsafeRead mt i
			wi1 <- unsafeRead mt (i+1) 
			w3  <- unsafeRead mt (i-156)
			let y = (wi .&. um64) .|. (wi1 .&. lm64)
			if even y 
			   then unsafeWrite mt i (w3 `xor` (shiftR y 1))
			   else unsafeWrite mt i (w3 `xor` (shiftR y 1) `xor` mA64)
			gLoop2 $! (i+1) 


next64 :: IOUArray Int Word64 -> Int -> IO (Word64, Int)
next64 mt 312 = do 
	generateNumbers64 mt
	(w,iN) <- next64 mt 0
	return (w,iN)
next64  mt i = do
	y <- unsafeRead mt i
	let y1 = y  `xor` ((shiftR y  29) .&. 0x5555555555555555)
	    y2 = y1 `xor` ((shiftL y1 17) .&. 0x71D67FFFEDA60000) 
	    y3 = y2 `xor` ((shiftL y2 37) .&. 0xFFF7EEE000000000)
	    y4 = y3 `xor`  (shiftR y3 43) 
	return $! (y4, (i+1))



module Main where

-- Compile eg with
-- ghc -O3 -optc-O3 -optc-ffast-math -fexcess-precision -funfolding-use-threshold=16 --make testMT
-- 
-- -ddump-simpl makes the Core.

import Mersenne

genRNums32 :: MT32 -> Int -> IO MT32
genRNums32 (MT32 mt pos) nCnt = gRN nCnt pos
	where 
	      gRN :: Int -> Int -> IO MT32
	      gRN 1 iCurr = do 
			(r,iNew) <- next32 mt iCurr
			putStrLn $ (show r)
			return (MT32 mt iNew)
	      gRN nCnt iCurr = do
			(_,iNew) <- next32 mt iCurr
			gRN (nCnt-1) $! iNew

genRNums64 :: MT64 -> Int -> IO MT64
genRNums64 (MT64 mt pos) nCnt = gRN nCnt pos
	where 
	      gRN :: Int -> Int -> IO MT64
	      gRN 1 iCurr = do 
			(r,iNew) <- next64 mt iCurr
			putStrLn $ (show r)
			return (MT64 mt iNew)
	      gRN nCnt iCurr = do
			(_,iNew) <- next64 mt iCurr
			gRN (nCnt-1) $! iNew

main = do
	putStrLn "Testing Mersenne Twister."
	-- mt32 <- initialiseGenerator32 100
	-- genRNums32 mt32 10000000
	mt64 <- initialiseGenerator64 100
	genRNums64 mt64 10000000
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to