1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -funbox-strict-fields #-}
#include "distrib.h"
-- | This module defines a type for tracking statistics about a series
-- of events. An event could be handling of a request and the value
-- associated with the event -- the value you'd pass to 'add' -- could
-- be the amount of time spent serving that request (e.g. in
-- milliseconds). All operations are thread safe.
module System.Metrics.Distribution
( Distribution
, new
, add
, addN
, read
-- * Gathered statistics
, 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 System.Metrics.Distribution.Internal (Stats(..))
import System.Metrics.ThreadId
-- | An metric for tracking events.
newtype Distribution = Distribution { unD :: Array Stripe }
data Stripe = Stripe
{ stripeFp :: !(ForeignPtr CDistrib)
}
data CDistrib = CDistrib
{ cCount :: !Int64
, cMean :: !Double
, cSumSqDelta :: !Double
, cSum :: !Double
, cMin :: !Double
, cMax :: !Double
, cLock :: !Int64 -- ^ 0 - unlocked, 1 - locked
}
instance Storable CDistrib where
sizeOf _ = (#size struct distrib)
alignment _ = alignment (undefined :: CInt)
peek p = do
cCount <- (#peek struct distrib, count) p
cMean <- (#peek struct distrib, mean) p
cSumSqDelta <- (#peek struct distrib, sum_sq_delta) p
cSum <- (#peek struct distrib, sum) p
cMin <- (#peek struct distrib, min) p
cMax <- (#peek struct distrib, max) p
cLock <- (#peek struct distrib, lock) p
return $! CDistrib
{ cCount = cCount
, cMean = cMean
, cSumSqDelta = cSumSqDelta
, cSum = cSum
, cMin = cMin
, cMax = cMax
, cLock = cLock
}
poke p CDistrib{..} = do
(#poke struct distrib, count) p cCount
(#poke struct distrib, mean) p cMean
(#poke struct distrib, sum_sq_delta) p cSumSqDelta
(#poke struct distrib, sum) p cSum
(#poke struct distrib, min) p cMin
(#poke struct distrib, max) p cMax
(#poke struct distrib, lock) p cLock
newCDistrib :: IO (ForeignPtr CDistrib)
newCDistrib = do
fp <- mallocForeignPtr
withForeignPtr fp $ \ p -> poke p $ CDistrib
{ cCount = 0
, cMean = 0.0
, cSumSqDelta = 0.0
, cSum = 0.0
, cMin = 0.0
, cMax = 0.0
, cLock = 0
}
return fp
newStripe :: IO Stripe
newStripe = do
fp <- newCDistrib
return $! Stripe
{ stripeFp = fp
}
-- | Number of lock stripes. Should be greater or equal to the number
-- of HECs.
numStripes :: Int
numStripes = 8
-- | Get the stripe to use for this thread.
myStripe :: Distribution -> IO Stripe
myStripe distrib = do
tid <- myCapability
return $! unD distrib `index` (tid `mod` numStripes)
------------------------------------------------------------------------
-- Exposed API
-- | Create a new distribution.
new :: IO Distribution
new = (Distribution . fromList numStripes) `fmap`
replicateM numStripes newStripe
-- | Add a value to the distribution.
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 ()
-- | Add the same value to the distribution N times.
addN :: Distribution -> Double -> Int64 -> IO ()
addN distrib val n = do
stripe <- myStripe distrib
withForeignPtr (stripeFp stripe) $ \ p ->
cDistribAddN p val n
foreign import ccall unsafe "hs_distrib_combine" combine
:: Ptr CDistrib -> Ptr CDistrib -> IO ()
-- | Get the current statistical summary for the event being tracked.
read :: Distribution -> IO Stats
read distrib = do
result <- newCDistrib
CDistrib{..} <- withForeignPtr result $ \ resultp -> do
forM_ (toList $ unD distrib) $ \ stripe ->
withForeignPtr (stripeFp stripe) $ \ p ->
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
}
|