packages feed

metrics 0.2.1.0 → 0.3.0.0

raw patch · 10 files changed

+187/−185 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Metrics.Reporter.StdOut: dumpMetrics :: MetricRegistry IO -> IO [(Text, String)]
- Data.Metrics.Reporter.StdOut: reportMetrics :: MetricRegistry IO -> IO ()
- Data.Metrics.Reservoir: _reservoirClear :: Reservoir -> !(NominalDiffTime -> s -> s)
- Data.Metrics.Reservoir: _reservoirSize :: Reservoir -> !(s -> Int)
- Data.Metrics.Reservoir: _reservoirSnapshot :: Reservoir -> !(s -> Snapshot)
- Data.Metrics.Reservoir: _reservoirState :: Reservoir -> !s
- Data.Metrics.Reservoir: _reservoirUpdate :: Reservoir -> !(Double -> NominalDiffTime -> s -> s)
- Data.Metrics.Timer.Internal: _timerHistogram :: Timer -> !Histogram
- Data.Metrics.Timer.Internal: _timerHistogramLens :: Lens' Timer Histogram
- Data.Metrics.Timer.Internal: _timerMeter :: Timer -> !Meter
- Data.Metrics.Timer.Internal: _timerMeterLens :: Lens' Timer Meter
+ Data.Metrics.Reservoir: reservoirClear :: Reservoir -> !(NominalDiffTime -> s -> s)
+ Data.Metrics.Reservoir: reservoirSize :: Reservoir -> !(s -> Int)
+ Data.Metrics.Reservoir: reservoirSnapshot :: Reservoir -> !(s -> Snapshot)
+ Data.Metrics.Reservoir: reservoirState :: Reservoir -> !s
+ Data.Metrics.Reservoir: reservoirUpdate :: Reservoir -> !(Double -> NominalDiffTime -> s -> s)
+ Data.Metrics.Reservoir.ExponentiallyDecaying: instance HasAlpha ExponentiallyDecayingReservoir Double
+ Data.Metrics.Reservoir.ExponentiallyDecaying: instance HasCount ExponentiallyDecayingReservoir Int
+ Data.Metrics.Reservoir.ExponentiallyDecaying: instance HasInnerReservoir ExponentiallyDecayingReservoir (Map Double Double)
+ Data.Metrics.Reservoir.ExponentiallyDecaying: instance HasInnerSize ExponentiallyDecayingReservoir Int
+ Data.Metrics.Reservoir.ExponentiallyDecaying: instance HasNextScaleTime ExponentiallyDecayingReservoir Word64
+ Data.Metrics.Reservoir.ExponentiallyDecaying: instance HasRescaleThreshold ExponentiallyDecayingReservoir Word64
+ Data.Metrics.Reservoir.ExponentiallyDecaying: instance HasSeed ExponentiallyDecayingReservoir Seed
+ Data.Metrics.Reservoir.ExponentiallyDecaying: instance HasStartTime ExponentiallyDecayingReservoir Word64
+ Data.Metrics.Reservoir.Uniform: instance HasCount UniformReservoir Int
+ Data.Metrics.Reservoir.Uniform: instance HasInnerReservoir UniformReservoir (Vector Double)
+ Data.Metrics.Reservoir.Uniform: instance HasSeed UniformReservoir Seed
+ Data.Metrics.Timer.Internal: timerHistogram :: Timer -> !Histogram
+ Data.Metrics.Timer.Internal: timerMeter :: Timer -> !Meter
- Data.Metrics.Meter.Internal: count :: HasCount c_a5Pa e_a5Pb => Lens' c_a5Pa e_a5Pb
+ Data.Metrics.Meter.Internal: count :: HasCount s a => Lens' s a
- Data.Metrics.Timer.Internal: class HasHistogram c_a87t e_a87u | c_a87t -> e_a87u
+ Data.Metrics.Timer.Internal: class HasHistogram s a | s -> a
- Data.Metrics.Timer.Internal: class HasMeter c_a87t e_a87u | c_a87t -> e_a87u
+ Data.Metrics.Timer.Internal: class HasMeter s a | s -> a
- Data.Metrics.Timer.Internal: histogram :: HasHistogram c_a87t e_a87u => Lens' c_a87t e_a87u
+ Data.Metrics.Timer.Internal: histogram :: HasHistogram s a => Lens' s a
- Data.Metrics.Timer.Internal: meter :: HasMeter c_a87t e_a87u => Lens' c_a87t e_a87u
+ Data.Metrics.Timer.Internal: meter :: HasMeter s a => Lens' s a

Files

metrics.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                metrics-version:             0.2.1.0+version:             0.3.0.0 synopsis:            High-performance application metric tracking description:   A port of Coda Hale's excellent metrics library for the JVM
src/Data/Metrics/Histogram/Internal.hs view
@@ -20,12 +20,12 @@  -- | A pure histogram that maintains a bounded reservoir of samples and basic statistical data about the samples. data Histogram = Histogram-  { _histogramReservoir :: !R.Reservoir-  , _histogramCount :: !Int-  , _histogramMinVal :: !Double-  , _histogramMaxVal :: !Double-  , _histogramSum :: !Double-  , _histogramVariance :: !(Double, Double)+  { histogramReservoir :: !R.Reservoir+  , histogramCount :: !Int+  , histogramMinVal :: !Double+  , histogramMaxVal :: !Double+  , histogramSum :: !Double+  , histogramVariance :: !(Double, Double)   }  -- | Create a histogram using a custom reservoir.@@ -40,12 +40,12 @@ clear = go   where     go t s = s-      { _histogramReservoir = R.clear t $ _histogramReservoir s-      , _histogramCount = 0-      , _histogramMinVal = nan-      , _histogramMaxVal = nan-      , _histogramSum = 0-      , _histogramVariance = (-1, 0)+      { histogramReservoir = R.clear t $ histogramReservoir s+      , histogramCount = 0+      , histogramMinVal = nan+      , histogramMaxVal = nan+      , histogramSum = 0+      , histogramVariance = (-1, 0)       }  -- | Update statistics and the reservoir with a new sample.@@ -53,16 +53,16 @@ update = go   where     go v t s = s-      { _histogramReservoir = updatedReservoir-      , _histogramCount = updatedCount-      , _histogramMinVal = updateMin (_histogramMinVal s) v-      , _histogramMaxVal = updateMax (_histogramMaxVal s) v-      , _histogramSum = _histogramSum s + v-      , _histogramVariance = updateVariance updatedCount v $ _histogramVariance s+      { histogramReservoir = updatedReservoir+      , histogramCount = updatedCount+      , histogramMinVal = updateMin (histogramMinVal s) v+      , histogramMaxVal = updateMax (histogramMaxVal s) v+      , histogramSum = histogramSum s + v+      , histogramVariance = updateVariance updatedCount v $ histogramVariance s       }       where -        updatedCount = succ $ _histogramCount s-        updatedReservoir = R.update v t $ _histogramReservoir s+        updatedCount = succ $ histogramCount s+        updatedReservoir = R.update v t $ histogramReservoir s  updateMin :: Double -> Double -> Double updateMin ox x = if isNaN ox || ox > x then x else ox@@ -74,8 +74,8 @@ mean :: Histogram -> Double mean = go   where-    go s = if _histogramCount s > 0-      then _histogramSum s / fromIntegral (_histogramCount s)+    go s = if histogramCount s > 0+      then histogramSum s / fromIntegral (histogramCount s)       else 0  -- | Get the standard deviation of all samples.@@ -83,9 +83,9 @@ stddev = go   where     go s = if c > 0-      then (calculateVariance c $ snd $ _histogramVariance s) ** 0.5+      then (calculateVariance c $ snd $ histogramVariance s) ** 0.5       else 0-      where c = _histogramCount s+      where c = histogramCount s  -- | Get the variance of all samples. variance :: Histogram -> Double@@ -93,24 +93,24 @@   where     go s = if c <= 1       then 0-      else calculateVariance c $ snd $ _histogramVariance s-      where c = _histogramCount s+      else calculateVariance c $ snd $ histogramVariance s+      where c = histogramCount s  -- | Get the minimum value of all samples. minVal :: Histogram -> Double-minVal = _histogramMinVal+minVal = histogramMinVal  -- | Get the maximum value of all samples maxVal :: Histogram -> Double-maxVal = _histogramMaxVal+maxVal = histogramMaxVal  -- | Get the number of samples that the histogram has been updated with. count :: Histogram -> Int-count = _histogramCount+count = histogramCount  -- | Get a snapshot of the current reservoir's samples. snapshot :: Histogram -> Snapshot-snapshot = R.snapshot . _histogramReservoir+snapshot = R.snapshot . histogramReservoir  calculateVariance :: Int -> Double -> Double calculateVariance c v = if c <= 1 then 0 else v / (fromIntegral c - 1)@@ -123,3 +123,4 @@     diff = c - x     l = x + diff / c'     r = y + diff * (c - l)+
src/Data/Metrics/Meter/Internal.hs view
@@ -21,24 +21,24 @@ import qualified Data.Metrics.MovingAverage as M  data Meter = Meter-  { _meterCount :: !Int-  , _meterOneMinuteRate :: !M.MovingAverage-  , _meterFiveMinuteRate :: !M.MovingAverage-  , _meterFifteenMinuteRate :: !M.MovingAverage-  , _meterStartTime :: !NominalDiffTime-  , _meterLastTick :: !NominalDiffTime+  { meterCount             :: !Int+  , meterOneMinuteRate     :: !M.MovingAverage+  , meterFiveMinuteRate    :: !M.MovingAverage+  , meterFifteenMinuteRate :: !M.MovingAverage+  , meterStartTime         :: !NominalDiffTime+  , meterLastTick          :: !NominalDiffTime   }  makeFields ''Meter  meterData :: (Int -> M.MovingAverage) -> NominalDiffTime -> Meter meterData f t = Meter-  { _meterCount = 0-  , _meterOneMinuteRate = f 1-  , _meterFiveMinuteRate = f 5-  , _meterFifteenMinuteRate = f 15-  , _meterStartTime = t-  , _meterLastTick = t+  { meterCount = 0+  , meterOneMinuteRate = f 1+  , meterFiveMinuteRate = f 5+  , meterFifteenMinuteRate = f 15+  , meterStartTime = t+  , meterLastTick = t   }  -- TODO: make moving average prism@@ -67,27 +67,28 @@  tickIfNecessary :: NominalDiffTime -> Meter -> Meter tickIfNecessary new d = if age >= 5-  then iterate tick (d { _meterLastTick = latest }) !! (truncate age `div` 5)+  then iterate tick (d { meterLastTick = latest }) !! (truncate age `div` 5)   else d   where-    age = new - _meterLastTick d-    swapped = _meterLastTick d < new-    latest = Prelude.max (_meterLastTick d) new+    age = new - meterLastTick d+    swapped = meterLastTick d < new+    latest = Prelude.max (meterLastTick d) new  meanRate :: NominalDiffTime -> Meter -> Double meanRate t d = if c == 0   then 0   else fromIntegral c / fromIntegral elapsed   where-    c = _meterCount d-    start = _meterStartTime d+    c = meterCount d+    start = meterStartTime d     elapsed = fromEnum t - fromEnum start  oneMinuteAverage :: Meter -> M.MovingAverage-oneMinuteAverage = _meterOneMinuteRate+oneMinuteAverage = meterOneMinuteRate  fiveMinuteAverage :: Meter -> M.MovingAverage-fiveMinuteAverage = _meterFiveMinuteRate+fiveMinuteAverage = meterFiveMinuteRate  fifteenMinuteAverage :: Meter -> M.MovingAverage-fifteenMinuteAverage = _meterFifteenMinuteRate+fifteenMinuteAverage = meterFifteenMinuteRate+
src/Data/Metrics/MovingAverage/ExponentiallyWeighted.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-} -- | An exponentially-weighted moving average. -- -- see /UNIX Load Average Part 1: How It Works/:@@ -38,11 +39,11 @@ -- -- This type encapsulates the state needed for the exponentially weighted "MovingAverage" implementation. data ExponentiallyWeightedMovingAverage = ExponentiallyWeightedMovingAverage-  { _ewmaUncounted :: !Double-  , _ewmaCurrentRate :: !Double-  , _ewmaInitialized :: !Bool-  , _ewmaInterval :: !Double-  , _ewmaAlpha :: !Double+  { exponentiallyWeightedMovingAverageUncounted   :: !Double+  , exponentiallyWeightedMovingAverageCurrentRate :: !Double+  , exponentiallyWeightedMovingAverageInitialized :: !Bool+  , exponentiallyWeightedMovingAverageInterval    :: !Double+  , exponentiallyWeightedMovingAverageAlpha       :: !Double   } deriving (Show)  makeFields ''ExponentiallyWeightedMovingAverage
src/Data/Metrics/Reporter/StdOut.hs view
@@ -4,41 +4,27 @@ -- For more meaningful access to statistics, metrics should be sent to something like Librato or Graphite. module Data.Metrics.Reporter.StdOut (   printHealthCheck,-  printHealthChecks,-  reportMetrics,-  dumpMetrics+  printHealthChecks ) where-import Control.Applicative-import Control.Concurrent.MVar import qualified Data.HashMap.Strict as H import Data.HealthCheck-import Data.Metrics+import Data.Metrics.Internal+import Data.Metrics.Types import qualified Data.Text as T import qualified Data.Text.IO as T import System.Console.ANSI -prettyPrintMetric (m, v) = T.putStr m >> T.putStr ": " >> putStrLn v--reportMetrics :: MetricRegistry IO -> IO ()-reportMetrics m = dumpMetrics m >>= mapM_ prettyPrintMetric+--prettyPrintMetric (m, v) = T.putStr m >> T.putStr ": " >> putStrLn (show v) -dumpMetrics :: MetricRegistry IO -> IO [(T.Text, String)]-dumpMetrics r = do-  ms <- readMVar $ metrics r-  let readRep (k, metricVariety) = do-        display <- case metricVariety of-          (MetricGauge g) -> show <$> value g-          (MetricCounter c) -> show <$> value c-          (MetricHistogram h) -> return ""-          (MetricMeter m) -> do-            one <- (\x -> "\t" ++ show x ++ "/minute\n") <$> oneMinuteRate m-            five <- (\x -> "\t" ++ show x ++ "/5 minutes\n") <$> fiveMinuteRate m-            fifteen <- (\x -> "\t" ++ show x ++ "/15 minutes\n") <$> fifteenMinuteRate m-            return ("\n" ++ one ++ five ++ fifteen)-          (MetricTimer t) -> (\x -> show x ++ " s") <$> mean t-        return (k, display)-  mapM readRep $ H.toList ms+--reportMetrics :: MetricRegistry -> IO ()+--reportMetrics m = dumpMetrics m >>= mapM_ prettyPrintMetric +--dumpMetrics :: MetricRegistry -> IO [(T.Text, Double)]+--dumpMetrics r = do+--  ms <- readMVar $ metrics r+--  -- let readRep (k, (repAction, _)) = repAction >>= \rep -> return (k, rep)+--  -- mapM readRep $ H.toList ms+--  return [] fg = SetColor Foreground Vivid  -- | Pretty-print a single HealthCheck to the console using ANSI colors.
src/Data/Metrics/Reservoir.hs view
@@ -11,18 +11,18 @@ -- -- The two standard implementations are the ExponentiallyDecayingReservoir and the UniformReservoir. data Reservoir = forall s. Reservoir-  { _reservoirClear :: !(NominalDiffTime -> s -> s)+  { reservoirClear :: !(NominalDiffTime -> s -> s)   -- ^ An operation that resets a reservoir to its initial state-  , _reservoirSize :: !(s -> Int)+  , reservoirSize :: !(s -> Int)   -- ^ Retrieve current size of the reservoir.   -- This may or may not be constant depending on the specific implementation.-  , _reservoirSnapshot :: !(s -> Snapshot)+  , reservoirSnapshot :: !(s -> Snapshot)   -- ^ Take snapshot of the current reservoir.   --   -- The number of items in the snapshot should always match the reservoir's size.-  , _reservoirUpdate :: !(Double -> NominalDiffTime -> s -> s)+  , reservoirUpdate :: !(Double -> NominalDiffTime -> s -> s)   -- ^ Add a new value to the reservoir, potentially evicting old values in the prcoess.-  , _reservoirState :: !s+  , reservoirState :: !s   -- ^ The internal state of the reservoir.   } 
src/Data/Metrics/Reservoir/ExponentiallyDecaying.hs view
@@ -1,3 +1,9 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE Rank2Types #-} -- | A histogram with an exponentially decaying reservoir produces quantiles which are representative of (roughly) the last five minutes of data. -- It does so by using a forward-decaying priority reservoir with an exponential weighting towards newer data.@@ -13,13 +19,15 @@   rescale,   update ) where+import Control.Lens+import Control.Lens.TH import Control.Monad.Primitive import Control.Monad.ST import Data.Time.Clock import Data.Time.Clock.POSIX import Data.Metrics.Internal+import qualified Data.Map as M import qualified Data.Metrics.Reservoir as R-import qualified Data.Map.Strict as M import Data.Metrics.Snapshot (Snapshot(..), takeSnapshot) import Data.Primitive.MutVar import qualified Data.Vector.Unboxed as V@@ -29,23 +37,25 @@ import System.Random.MWC  -- hours in seconds-rescaleThreshold :: Word64-rescaleThreshold = 60 * 60+baseRescaleThreshold :: Word64+baseRescaleThreshold = 60 * 60  -- | A forward-decaying priority reservoir -- -- <http://dimacs.rutgers.edu/~graham/pubs/papers/fwddecay.pdf> data ExponentiallyDecayingReservoir = ExponentiallyDecayingReservoir-  { _edrSize :: !Int-  , _edrAlpha :: !Double-  , _edrRescaleThreshold :: !Word64-  , _edrReservoir :: !(M.Map Double Double)-  , _edrCount :: !Int-  , _edrStartTime :: !Word64-  , _edrNextScaleTime :: !Word64-  , _edrSeed :: !Seed+  { exponentiallyDecayingReservoirInnerSize        :: {-# UNPACK #-} !Int+  , exponentiallyDecayingReservoirAlpha            :: {-# UNPACK #-} !Double+  , exponentiallyDecayingReservoirRescaleThreshold :: {-# UNPACK #-} !Word64+  , exponentiallyDecayingReservoirInnerReservoir   :: {-# UNPACK #-} !(M.Map Double Double)+  , exponentiallyDecayingReservoirCount            :: {-# UNPACK #-} !Int+  , exponentiallyDecayingReservoirStartTime        :: {-# UNPACK #-} !Word64+  , exponentiallyDecayingReservoirNextScaleTime    :: {-# UNPACK #-} !Word64+  , exponentiallyDecayingReservoirSeed :: !Seed   } deriving (Show) +makeFields ''ExponentiallyDecayingReservoir+ -- | An exponentially decaying reservoir with an alpha value of 0.015 and a 1028 sample cap. -- -- This offers a 99.9% confidence level with a 5% margin of error assuming a normal distribution,@@ -59,24 +69,24 @@   -> NominalDiffTime -- ^ creation time for the reservoir   -> Seed -> R.Reservoir reservoir a r t s = R.Reservoir-  { R._reservoirClear = clear-  , R._reservoirSize = size-  , R._reservoirSnapshot = snapshot-  , R._reservoirUpdate = update-  , R._reservoirState = ExponentiallyDecayingReservoir r a rescaleThreshold M.empty 0 c c' s+  { R.reservoirClear = clear+  , R.reservoirSize = size+  , R.reservoirSnapshot = snapshot+  , R.reservoirUpdate = update+  , R.reservoirState = ExponentiallyDecayingReservoir r a baseRescaleThreshold M.empty 0 c c' s   }   where     c = truncate t-    c' = c + rescaleThreshold+    c' = c + baseRescaleThreshold  -- | Reset the reservoir clear :: NominalDiffTime -> ExponentiallyDecayingReservoir -> ExponentiallyDecayingReservoir clear = go   where-    go t c = c { _edrStartTime = t', _edrNextScaleTime = t'', _edrCount = 0, _edrReservoir = M.empty }+    go t c = c & startTime .~ t' & nextScaleTime .~ t'' & count .~ 0 & innerReservoir .~ M.empty       where         t' = truncate t-        t'' = t' + _edrRescaleThreshold c+        t'' = t' + c ^. rescaleThreshold  -- | Get the current size of the reservoir. size :: ExponentiallyDecayingReservoir -> Int@@ -84,13 +94,13 @@   where     go r = min c s       where-        c = _edrCount r-        s = _edrSize r+        c = r ^. count+        s = r ^. innerSize  -- | Get a snapshot of the current reservoir snapshot :: ExponentiallyDecayingReservoir -> Snapshot snapshot r = runST $ do-  let svals = V.fromList $ M.elems $ _edrReservoir $ r+  let svals = V.fromList $ M.elems $ r ^. innerReservoir   mvals <- V.unsafeThaw svals   takeSnapshot mvals @@ -115,20 +125,15 @@ -- landmark L′ (and then use this new L′ at query time). This can be done with -- a linear pass over whatever data structure is being used.\" rescale :: Word64 -> ExponentiallyDecayingReservoir -> ExponentiallyDecayingReservoir-rescale now c = c-  { _edrReservoir = adjustedReservoir-  , _edrStartTime = now-  , _edrCount = M.size adjustedReservoir-  , _edrNextScaleTime = st-  }+rescale now c = c & startTime .~ now & nextScaleTime .~ st & count .~ M.size adjustedReservoir & innerReservoir .~ adjustedReservoir   where-    potentialScaleTime = now + rescaleThreshold-    currentScaleTime = _edrNextScaleTime c+    potentialScaleTime = now + baseRescaleThreshold+    currentScaleTime = c ^. nextScaleTime     st = if potentialScaleTime > currentScaleTime then potentialScaleTime else currentScaleTime-    diff = now - _edrStartTime c-    adjustKey x = x * exp (-alpha * fromIntegral diff)-    adjustedReservoir = M.mapKeys adjustKey $ _edrReservoir c-    alpha = _edrAlpha c+    diff = now - c ^. startTime+    adjustKey x = x * exp (-_alpha * fromIntegral diff)+    adjustedReservoir = M.mapKeys adjustKey $ c ^. innerReservoir+    _alpha = c ^. alpha  -- | Insert a new sample into the reservoir. This may cause old sample values to be evicted -- based upon the probabilistic weighting given to the key at insertion time.@@ -136,29 +141,25 @@   -> NominalDiffTime -- ^ time of update   -> ExponentiallyDecayingReservoir   -> ExponentiallyDecayingReservoir-update v t c = rescaled-  { _edrSeed = s'-  , _edrCount = newCount-  , _edrReservoir = addValue r-  } +update v t c = rescaled & seed .~ s' & count .~ newCount & innerReservoir .~ addValue r   where-    rescaled = if seconds >= _edrNextScaleTime c+    rescaled = if seconds >= c ^. nextScaleTime       then rescale seconds c       else c     seconds = truncate t-    priority = weight (_edrAlpha c) (seconds - _edrStartTime c) / priorityDenom-    addValue r = if newCount <= _edrSize c+    priority = weight (c ^. alpha) (seconds - c ^. startTime) / priorityDenom+    addValue r = if newCount <= (c ^. innerSize)       then M.insert priority v r       else if firstKey < priority         -- it should be safe to use head here since we are over our reservoir capacity at this point         -- caveat: reservoir capped at 0 max size         then M.delete firstKey $ M.insertWith const priority v r         else r-    r = _edrReservoir c+    r = c ^. innerReservoir     firstKey = head $ M.keys r-    newCount = 1 + _edrCount c+    newCount = 1 + c ^. count     (priorityDenom, s') = runST $ do-      g <- restore $ _edrSeed c+      g <- restore $ c ^. seed       p <- uniform g       s' <- save g       return (p :: Double, s')
src/Data/Metrics/Reservoir/Uniform.hs view
@@ -1,3 +1,9 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-} -- | A histogram with a uniform reservoir produces quantiles which are valid for the entirely of the histogram’s lifetime. -- It will return a median value, for example, which is the median of all the values the histogram has ever been updated with. -- It does this by using an algorithm called Vitter’s R), which randomly selects values for the reservoir with linearly-decreasing probability.@@ -15,6 +21,8 @@   update,   unsafeUpdate ) where+import Control.Lens+import Control.Lens.TH import Control.Monad.ST import Data.Metrics.Internal import Data.Time.Clock@@ -25,16 +33,27 @@ import qualified Data.Vector.Unboxed as I import qualified Data.Vector.Unboxed.Mutable as V +-- | A reservoir in which all samples are equally likely to be evicted when the reservoir is at full capacity.+--+-- This is conceptually simpler than the "ExponentiallyDecayingReservoir", but at the expense of providing a less accurate sample.+data UniformReservoir = UniformReservoir+  { uniformReservoirCount          :: {-# UNPACK #-} !Int+  , uniformReservoirInnerReservoir :: {-# UNPACK #-} !(I.Vector Double)+  , uniformReservoirSeed           :: {-# UNPACK #-} !Seed+  }++makeFields ''UniformReservoir+ -- | Make a safe uniform reservoir. This variant provides safe access at the expense of updates costing O(n) reservoir :: Seed   -> Int -- ^ maximum reservoir size   -> R.Reservoir reservoir g r = R.Reservoir-  { R._reservoirClear = clear-  , R._reservoirSize = size-  , R._reservoirSnapshot = snapshot-  , R._reservoirUpdate = update-  , R._reservoirState = UniformReservoir 0 (I.replicate r 0) g+  { R.reservoirClear = clear+  , R.reservoirSize = size+  , R.reservoirSnapshot = snapshot+  , R.reservoirUpdate = update+  , R.reservoirState = UniformReservoir 0 (I.replicate r 0) g   }  -- | Using this variant requires that you ensure that there is no sharing of the reservoir itself.@@ -44,27 +63,18 @@ -- In return, updating the reservoir becomes an O(1) operation and clearing the reservoir avoids extra allocations. unsafeReservoir :: Seed -> Int -> R.Reservoir unsafeReservoir g r = R.Reservoir-  { R._reservoirClear = unsafeClear-  , R._reservoirSize = size-  , R._reservoirSnapshot = snapshot-  , R._reservoirUpdate = unsafeUpdate-  , R._reservoirState = UniformReservoir 0 (I.replicate r 0) g-  }---- | A reservoir in which all samples are equally likely to be evicted when the reservoir is at full capacity.------ This is conceptually simpler than the "ExponentiallyDecayingReservoir", but at the expense of providing a less accurate sample.-data UniformReservoir = UniformReservoir-  { _urCount :: !Int-  , _urReservoir :: !(I.Vector Double)-  , _urSeed :: !Seed+  { R.reservoirClear = unsafeClear+  , R.reservoirSize = size+  , R.reservoirSnapshot = snapshot+  , R.reservoirUpdate = unsafeUpdate+  , R.reservoirState = UniformReservoir 0 (I.replicate r 0) g   }  -- | Reset the reservoir to empty. clear :: NominalDiffTime -> UniformReservoir -> UniformReservoir clear = go   where-    go _ c = c { _urCount = 0, _urReservoir = newRes $ _urReservoir c }+    go _ c = c & count .~ 0 & innerReservoir %~ newRes     newRes v = runST $ do       v' <- I.thaw v       V.set v' 0@@ -74,7 +84,7 @@ unsafeClear :: NominalDiffTime -> UniformReservoir -> UniformReservoir unsafeClear = go   where-    go _ c = c { _urCount = 0, _urReservoir = newRes $ _urReservoir c }+    go _ c = c & count .~ 0 & innerReservoir %~ newRes     newRes v = runST $ do       v' <- I.unsafeThaw v       V.set v' 0@@ -84,7 +94,7 @@ size :: UniformReservoir -> Int size = go   where-    go c = min (_urCount c) (I.length $ _urReservoir c)+    go c = min (c ^. count) (I.length $ c ^. innerReservoir)  -- | Take a snapshot of the reservoir by doing an in-place unfreeze. --@@ -93,21 +103,21 @@ snapshot = go   where     go c = runST $ do-      v' <- I.unsafeThaw $ _urReservoir c+      v' <- I.unsafeThaw $ c ^. innerReservoir       S.takeSnapshot $ V.slice 0 (size c) v'  -- | Perform an update of the reservoir by copying the internal vector. O(n) update :: Double -> NominalDiffTime -> UniformReservoir -> UniformReservoir update = go   where-    go x _ c = c { _urCount = newCount, _urReservoir = newRes, _urSeed = newSeed }+    go x _ c = c & count .~ newCount & innerReservoir .~ newRes & seed .~ newSeed       where-        newCount = succ $ _urCount c+        newCount = c ^. count . to succ         (newSeed, newRes) = runST $ do-          v' <- I.thaw $ _urReservoir c-          g <- restore (_urSeed c)+          v' <- I.thaw $ c ^. innerReservoir+          g <- restore $ c ^. seed           if newCount <= V.length v'-            then V.unsafeWrite v' (_urCount c) x+            then V.unsafeWrite v' (c ^. count) x             else do               i <- uniformR (0, newCount) g               if i < V.length v'@@ -121,14 +131,14 @@ unsafeUpdate :: Double -> NominalDiffTime -> UniformReservoir -> UniformReservoir unsafeUpdate = go   where-    go x _ c = c { _urCount = newCount, _urReservoir = newRes, _urSeed = newSeed }+    go x _ c = c & count .~ newCount & innerReservoir .~ newRes & seed .~ newSeed       where-        newCount = succ $ _urCount c+        newCount = c ^. count . to succ         (newSeed, newRes) = runST $ do-          v' <- I.unsafeThaw $ _urReservoir c-          g <- restore (_urSeed c)+          v' <- I.unsafeThaw $ c ^. innerReservoir+          g <- restore (uniformReservoirSeed c)           if newCount <= V.length v'-            then V.unsafeWrite v' (_urCount c) x+            then V.unsafeWrite v' (c ^. count) x             else do               i <- uniformR (0, newCount) g               if i < V.length v'@@ -137,3 +147,4 @@           v'' <- I.unsafeFreeze v'           s <- save g           return (s, v'')+
src/Data/Metrics/Timer.hs view
@@ -29,19 +29,19 @@ -- | A measure of time statistics for the duration of an event data Timer m = Timer   { fromTimer :: !(MutVar (PrimState m) P.Timer) -- ^ A reference to the pure timer internals-  , _timerGetTime :: !(m NominalDiffTime) -- ^ The function that provides time differences for the timer. In practice, this is usually just "getPOSIXTime"+  , timerGetTime :: !(m NominalDiffTime) -- ^ The function that provides time differences for the timer. In practice, this is usually just "getPOSIXTime"   }  makeFields ''Timer  instance PrimMonad m => Clear m (Timer m) where   clear t = do-    ts <- _timerGetTime t+    ts <- timerGetTime t     updateRef (fromTimer t) $ P.clear ts  instance PrimMonad m => Update m (Timer m) Double where   update t x = do-    ts <- _timerGetTime t+    ts <- timerGetTime t     updateRef (fromTimer t) $ P.update ts x  instance PrimMonad m => Count m (Timer m) where@@ -56,16 +56,16 @@  instance PrimMonad m => Rate m (Timer m) where   oneMinuteRate t = do-    ts <- _timerGetTime t+    ts <- timerGetTime t     updateAndApplyToRef (fromTimer t) (P.tickIfNecessary ts) P.oneMinuteRate   fiveMinuteRate t = do-    ts <- _timerGetTime t+    ts <- timerGetTime t     updateAndApplyToRef (fromTimer t) (P.tickIfNecessary ts) P.fiveMinuteRate   fifteenMinuteRate t = do-    ts <- _timerGetTime t+    ts <- timerGetTime t     updateAndApplyToRef (fromTimer t) (P.tickIfNecessary ts) P.fifteenMinuteRate   meanRate t = do-    ts <- _timerGetTime t+    ts <- timerGetTime t     applyWithRef (fromTimer t) (P.meanRate ts)  instance PrimMonad m => TakeSnapshot m (Timer m) where
src/Data/Metrics/Timer/Internal.hs view
@@ -16,8 +16,8 @@ import qualified Data.Metrics.Snapshot as S  data Timer = Timer-  { _timerMeter :: !M.Meter-  , _timerHistogram :: !H.Histogram+  { timerMeter :: !M.Meter+  , timerHistogram :: !H.Histogram   }  makeFields ''Timer@@ -26,19 +26,19 @@ tickIfNecessary t = meter %~ M.tickIfNecessary t  snapshot :: Timer -> S.Snapshot-snapshot = H.snapshot . _timerHistogram+snapshot = H.snapshot . timerHistogram  oneMinuteRate :: Timer -> Double-oneMinuteRate = A.rate . M.oneMinuteAverage . _timerMeter+oneMinuteRate = A.rate . M.oneMinuteAverage . timerMeter  fiveMinuteRate :: Timer -> Double-fiveMinuteRate = A.rate . M.fiveMinuteAverage . _timerMeter+fiveMinuteRate = A.rate . M.fiveMinuteAverage . timerMeter  fifteenMinuteRate :: Timer -> Double-fifteenMinuteRate = A.rate . M.fifteenMinuteAverage . _timerMeter+fifteenMinuteRate = A.rate . M.fifteenMinuteAverage . timerMeter  meanRate :: NominalDiffTime -> Timer -> Double-meanRate t = M.meanRate t . _timerMeter+meanRate t = M.meanRate t . timerMeter  count :: Timer -> Int count = H.count . view histogram@@ -50,16 +50,17 @@ update t x = (histogram %~ H.update x t) . (meter %~ M.mark t 1)  mean :: Timer -> Double-mean = H.mean . _timerHistogram+mean = H.mean . timerHistogram  stddev :: Timer -> Double-stddev = H.stddev . _timerHistogram+stddev = H.stddev . timerHistogram  variance :: Timer -> Double-variance = H.variance . _timerHistogram+variance = H.variance . timerHistogram  maxVal :: Timer -> Double-maxVal = H.maxVal . _timerHistogram+maxVal = H.maxVal . timerHistogram  minVal :: Timer -> Double-minVal = H.minVal . _timerHistogram+minVal = H.minVal . timerHistogram+