module System.Metrics.Distribution
( Distribution
, new
, add
, addN
, read
, Stats
, mean
, variance
, count
, sum
, min
, max
) where
import Control.Monad (forM_, replicateM)
import Data.Int (Int64)
import Foreign.C.Types (CInt)
import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtr, withForeignPtr)
import Foreign.Ptr (Ptr)
import Foreign.Storable (Storable(alignment, peek, poke, sizeOf), peekByteOff,
pokeByteOff)
import Prelude hiding (max, min, read, sum)
import Data.Array
import qualified Data.Mutex as Mutex
import System.Metrics.ThreadId
newtype Distribution = Distribution { unD :: Array Stripe }
data Stripe = Stripe
{ stripeFp :: !(ForeignPtr CDistrib)
, stripeMutex :: !Mutex.Mutex
}
withMutex :: Mutex.Mutex -> IO a -> IO a
withMutex lock m = do
Mutex.lock lock
a <- m
Mutex.unlock lock
return a
data CDistrib = CDistrib
{ cCount :: !Int64
, cMean :: !Double
, cSumSqDelta :: !Double
, cSum :: !Double
, cMin :: !Double
, cMax :: !Double
}
instance Storable CDistrib where
sizeOf _ = ((48))
alignment _ = alignment (undefined :: CInt)
peek p = do
cCount <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
cMean <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) p
cSumSqDelta <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) p
cSum <- ((\hsc_ptr -> peekByteOff hsc_ptr 24)) p
cMin <- ((\hsc_ptr -> peekByteOff hsc_ptr 32)) p
cMax <- ((\hsc_ptr -> peekByteOff hsc_ptr 40)) p
return $! CDistrib
{ cCount = cCount
, cMean = cMean
, cSumSqDelta = cSumSqDelta
, cSum = cSum
, cMin = cMin
, cMax = cMax
}
poke p CDistrib{..} = do
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) p cCount
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) p cMean
((\hsc_ptr -> pokeByteOff hsc_ptr 16)) p cSumSqDelta
((\hsc_ptr -> pokeByteOff hsc_ptr 24)) p cSum
((\hsc_ptr -> pokeByteOff hsc_ptr 32)) p cMin
((\hsc_ptr -> pokeByteOff hsc_ptr 40)) p cMax
newCDistrib :: IO (ForeignPtr CDistrib)
newCDistrib = do
fp <- mallocForeignPtr
withForeignPtr fp $ \ p -> poke p $ CDistrib 0 0.0 0.0 0.0 0.0 0.0
return fp
newStripe :: IO Stripe
newStripe = do
fp <- newCDistrib
mutex <- Mutex.new
return $! Stripe
{ stripeFp = fp
, stripeMutex = mutex
}
numStripes :: Int
numStripes = 8
myStripe :: Distribution -> IO Stripe
myStripe distrib = do
tid <- myCapability
return $! unD distrib `index` (tid `mod` numStripes)
new :: IO Distribution
new = (Distribution . fromList numStripes) `fmap`
replicateM numStripes newStripe
add :: Distribution -> Double -> IO ()
add distrib val = addN distrib val 1
foreign import ccall unsafe "hs_distrib_add_n" cDistribAddN
:: Ptr CDistrib -> Double -> Int64 -> IO ()
addN :: Distribution -> Double -> Int64 -> IO ()
addN distrib val n = do
stripe <- myStripe distrib
withForeignPtr (stripeFp stripe) $ \ p ->
withMutex (stripeMutex stripe) $ cDistribAddN p val n
foreign import ccall unsafe "hs_distrib_combine" combine
:: Ptr CDistrib -> Ptr CDistrib -> IO ()
read :: Distribution -> IO Stats
read distrib = do
result <- newCDistrib
CDistrib{..} <- withForeignPtr result $ \ resultp -> do
forM_ (toList $ unD distrib) $ \ stripe ->
withForeignPtr (stripeFp stripe) $ \ p ->
withMutex (stripeMutex stripe) $
combine p resultp
peek resultp
return $! Stats
{ mean = cMean
, variance = if cCount == 0 then 0.0
else cSumSqDelta / fromIntegral cCount
, count = cCount
, sum = cSum
, min = cMin
, max = cMax
}
data Stats = Stats
{ mean :: !Double
, variance :: !Double
, count :: !Int64
, sum :: !Double
, min :: !Double
, max :: !Double
} deriving Show