Now, this will be hard to get close the the highly tuned C. Possibly its doable.
The main tricks are documented here: http://haskell.org/haskellwiki/Performance/GHC#Unboxed_types Inspecting the Core to ensure the math is being inlined and unboxed will be the most crucial issue, I'd imagine. Then again, an FFI binding to mersenne.c is also a good idea :) -- Don isto.aho: > Hi all, > > On HaWiki was an announcement of MersenneTwister made by Lennart > Augustsson. On a typical run to find out 10000000th rnd num the output > is (code shown below): > > $ time ./testMTla > Testing Mersenne Twister. > Result is [3063349438] > > real 0m4.925s > user 0m4.856s > > > I was exercising with the very same algorithm and tried to make it > efficient (by using IOUArray): now a typical run looks like (code shown > below): > > $ time ./testMT > Testing Mersenne Twister. > 3063349438 > > real 0m3.032s > user 0m3.004s > > > The original C-version (modified so that only the last number is > shown) gives typically > > $ time ./mt19937ar > outputs of genrand_int32() > 3063349438 > > real 0m0.624s > user 0m0.616s > > Results are similar with 64 bit IOUArray against 64 bit C variant. > C seems to work about 5 to 10 times faster in this case. > > I have tried to do different things but now I'm stuck. unsafeRead > and unsafeWrite improved a bit the lazy (STUArray-version) and > IOUArray-versions but not very much. I took a look of Core file but > then, I'm not sure where the boxed values are ok. E.g. should IOUArray > Int Word64 be replaced with something else? > > Any hints and comments on how to improve the efficiency and make > everything better will be appreciated a lot! > > br, Isto > > ----------------------------- testMTla.hs (MersenneTwister, see HaWiki) > module Main where > > -- ghc -O3 -optc-O3 -optc-ffast-math -fexcess-precision --make testMTla > > import MersenneTwister > > main = do > putStrLn "Testing Mersenne Twister." > let mt = mersenneTwister 100 > w = take 1 (drop 9999999 mt) > -- w = take 1 (drop 99 mt) > putStrLn $ "Result is " ++ (show w) > ----------------------------- > > ----------------------------- testMT.hs > module Main where > > -- Compile eg with > -- ghc -O3 -optc-O3 -optc-ffast-math -fexcess-precision --make testMT > > import Mersenne > > genRNums32 :: MT32 -> Int -> IO (MT32) > genRNums32 mt nCnt = gRN mt nCnt > where gRN :: MT32 -> Int -> IO (MT32) > gRN mt nCnt | mt `seq` nCnt `seq` False = undefined > gRN mt 1 = do > (r,mt') <- next32 mt > putStrLn $ (show r) > return mt' > gRN mt nCnt = do > (r,mt') <- next32 mt > gRN mt' $! (nCnt-1) > > > main = do > putStrLn "Testing Mersenne Twister." > mt32 <- initialiseGenerator32 100 > genRNums32 mt32 10000000 > ----------------------------- > > ----------------------------- Mersenne.hs (sorry for linewraps) > module Mersenne where > > import Data.Bits > import Data.Word > import Data.Array.Base > import Data.Array.MArray > import Data.Array.IO > -- import System.Random > > > 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 > iG mt s 1 > mt' <- generateNumbers32 mt > return (MT32 mt' 0) > where > iG :: (IOUArray Int Word32) -> Word32 -> Int -> IO (IOUArray Int > Word32) > iG mt lastNro n > | n == 624 = return mt > | otherwise = do let n1 = lastNro `xor` (shiftR lastNro > 30) > new = (1812433253 * n1 + > (fromIntegral n)::Word32) > unsafeWrite mt n new > iG mt new (n+1) > > > generateNumbers32 :: (IOUArray Int Word32) -> IO (IOUArray Int Word32) > generateNumbers32 mt = gLoop 0 mt > where > gLoop :: Int -> (IOUArray Int Word32) -> IO (IOUArray Int > Word32) > gLoop i mt > | i==623 = do > 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 mt > | otherwise = do > wi <- unsafeRead mt i > wi1 <- unsafeRead mt (i+1) > w3 <- unsafeRead mt ((i+397) `mod` 624) > 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) > gLoop (i+1) mt > > > next32 :: MT32 -> IO (Word32, MT32) > next32 (MT32 mt i) > | i >= 624 = do mt' <- generateNumbers32 mt > let m = MT32 mt' (i `mod` 624) > (w,m') <- next32 m > return (w,m') > | otherwise = 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, MT32 mt (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 > iG mt s 1 > generateNumbers64 mt > return (MT64 mt 0) > where > iG :: (IOUArray Int Word64) -> Word64 -> Int -> IO (IOUArray Int > Word64) > iG mt lN i | mt `seq` lN `seq` i `seq` False = undefined > iG mt lastNro 312 = return mt > iG mt lastNro n = do > let n1 = lastNro `xor` (shiftR lastNro 62) > new = (6364136223846793005 * n1 + > (fromIntegral > n)::Word64) > unsafeWrite mt n new > iG mt new $! (n+1) > > generateNumbers64 :: (IOUArray Int Word64) -> IO () > generateNumbers64 mt = gLoop 0 > where > gLoop :: Int -> IO () > gLoop i | i `seq` False = undefined > gLoop 311 = do > 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 () > gLoop i = do > wi <- unsafeRead mt i > wi1 <- unsafeRead mt (i+1) > w3 <- unsafeRead mt ((i+156) `mod` 312) > 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) > gLoop $! (i+1) > > > next64 :: MT64 -> IO (Word64, MT64) > next64 (MT64 mt 312) = do generateNumbers64 mt > let m = MT64 mt 0 > (w,m') <- next64 m > return (w,m') > next64 (MT64 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, MT64 mt (i+1)) > > > > > > _______________________________________________ > Haskell-Cafe mailing list > Haskell-Cafe@haskell.org > http://www.haskell.org/mailman/listinfo/haskell-cafe _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe