packages feed

rcu-0.2.6: examples/IncCounterExperiment.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
-----------------------------------------------------------------------------
-- |
-- Copyright   :  (C) 2015 Edward Kmett and Ted Cooper
-- License     :  BSD-style (see the file LICENSE)
-- Maintainer  :  Edward Kmett <ekmett@gmail.com>,
--                Ted Cooper <anthezium@gmail.com>
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Which counter increment is faster?
-----------------------------------------------------------------------------
module Main where

import Control.Monad (forM_)
import Control.Monad.Primitive (primitive)
import Criterion.Main (bench, bgroup, defaultMain, nfIO)
import Data.Word (Word64)
import Data.Primitive
import GHC.Prim --(MutableByteArray, plusWord, readWord64Array, writeWord64Array)
import GHC.Word (Word64 (W64#))

-- | Counter for causal ordering.
newtype Counter = Counter (MutableByteArray RealWorld)

instance Eq Counter where
  Counter m == Counter n = sameMutableByteArray m n

offline :: Word64
offline = 0

online :: Word64
online  = 1

-- counterInc :: Word64
-- counterInc = 2 -- online threads will never overflow to 0

newCounter :: IO Counter
newCounter = do
  b <- newByteArray 8
  writeByteArray b 0 online
  return (Counter b)
{-# INLINE newCounter #-}

readCounter :: Counter -> IO Word64
readCounter (Counter c) = readByteArray c 0
{-# INLINE readCounter #-}

writeCounter :: Counter -> Word64 -> IO ()
writeCounter (Counter c) w = writeByteArray c 0 w
{-# INLINE writeCounter #-}

incCounterAtomic :: Counter -> IO Word64
incCounterAtomic (Counter (MutableByteArray c)) = primitive $ \ s ->
  case fetchAddIntArray# c 0# 2# s of
       (# s', r #) ->
#if MIN_VERSION_base(4,17,0)
         (# s', W64# (wordToWord64# (int2Word# r)) #)
#else
         (# s', W64# (int2Word# r) #)
#endif
{-# INLINE incCounterAtomic #-}

incCounterNonAtomicFancy :: Counter -> IO Word64
incCounterNonAtomicFancy (Counter (MutableByteArray c)) = primitive $ \ s ->
  case readWord64Array# c 0# s of
       (# s', r #) ->
#if MIN_VERSION_base(4,17,0)
         case plusWord64# r (wordToWord64# 2##) of
#else
         case plusWord# r 2## of
#endif
              r' -> case writeWord64Array# c 0# r' s' of
                         s'' -> (# s'', W64# r' #)
{-# INLINE incCounterNonAtomicFancy #-}

incCounterNonAtomic :: Counter -> IO Word64
incCounterNonAtomic c = do
  x <- (+ 2) <$> readCounter c
  writeCounter c x
  return x
{-# INLINE incCounterNonAtomic #-}

main :: IO ()
main = defaultMain [ bgroup "incCounterAtomic"         $ bunches incCounterAtomic
                   , bgroup "incCounterNonAtomicFancy" $ bunches incCounterNonAtomicFancy
                   , bgroup "incCounterNonAtomic"      $ bunches incCounterNonAtomic ]
  where bunches m = [ bench (show n)
                    $ nfIO $ do c <- newCounter
                                forM_ [1..n] $ \ _ -> m c
                    | n <- map ((10 :: Word64) ^) [(6 :: Word64)..7] ]