id	summary	reporter	owner	description	type	status	priority	milestone	version	resolution	keywords	cc
33	Strange performance with mutable vectors	anonymous		"I run into some strange performance problem with mutable vectors. Function uniform below performs very poorly (with ~60x slowdown) but simple and not very logical changes return performance to normal

{{{
newtype Gen s = Gen (M.MVector s Word32)

class Variate a where
    uniform :: (PrimMonad m) => Gen (PrimState m) -> m a
instance Variate Word32 where
    uniform = uniformWord32
    {-# INLINE uniform #-}

uniformWord32 :: PrimMonad m => Gen (PrimState m) -> m Word32
uniformWord32 (Gen q) = do
  i <- nextIndex `liftM` M.unsafeRead q 256
  t <- M.unsafeRead q i
  M.unsafeWrite q 256 (fromIntegral i)
  return t
{-# INLINE uniformWord32 #-}
}}}

First option is to add `Unbox a' constraint to the Variate type class. Another option is to replace function uniformWord32 with:

{{{
uniformWord32 (Gen q) = do
  i <- nextIndex `liftM` M.unsafeRead q 256
  M.unsafeWrite q 256 (fromIntegral i)
  M.unsafeRead q i
}}}

Tested with GHC6.12.1 (debian) and vector-0.6.0.2 and current darcs head. 

File attachments doesn't work so I'm pasting code inline:

MWC.hs
{{{
module MWC ( Gen
           , Variate(..)
           , create
           ) where

import Control.Monad           (liftM)
import Control.Monad.Primitive (PrimMonad, PrimState)
import Data.Bits               (shiftR)
import Data.Word               (Word8,Word32,Word64)
import qualified Data.Vector.Generic         as G
import qualified Data.Vector.Unboxed         as I
import qualified Data.Vector.Unboxed.Mutable as M

-- class M.Unbox a => Variate a where
class Variate a where
    uniform :: (PrimMonad m) => Gen (PrimState m) -> m a
instance Variate Word32 where
    uniform = uniformWord32
    {-# INLINE uniform #-}

-- | State of the pseudo-random number generator.
newtype Gen s = Gen (M.MVector s Word32)

-- | Create a generator for variates using a fixed seed.
create :: PrimMonad m => m (Gen (PrimState m))
create = do
  q <- M.unsafeNew 257
  G.copy q defaultSeed
  return (Gen q)
{-# INLINE create #-}

-- | Compute the next index into the state pool.  This is simply
-- addition modulo 256.
nextIndex :: Integral a => a -> Int
nextIndex i = fromIntegral j
    where j = fromIntegral (i+1) :: Word8
-- {-# INLINE nextIndex #-}

uniformWord32 :: PrimMonad m => Gen (PrimState m) -> m Word32
uniformWord32 (Gen q) = do
  i <- nextIndex `liftM` M.unsafeRead q 256
  t <- M.unsafeRead q i
  M.unsafeWrite q 256 (fromIntegral i)
  return t
{-# INLINE uniformWord32 #-}

{- This variant is fast:
  M.unsafeWrite q 256 (fromIntegral i)
  M.unsafeRead q i
-}

defaultSeed :: I.Vector Word32
defaultSeed = I.fromList $ reverse [0..256]
}}}

benchmark.hs:
{{{
import Data.Word
import Criterion.Main
import MWC

main = do
  gen <- create
  defaultMain [ bench ""mwc-Double"" (uniform gen :: IO Word32) ]
}}}
This test case is stripped down code from mwc-random."	defect	closed	minor	0.7.1		wontfix		alexey.skladnoy@…
