joshbohde/hdr-histogram

View on GitHub
src/Data/HdrHistogram/Mutable.hs

Summary

Maintainability
Test Coverage
{-|
Module      : Data.HdrHistogram.Mutable
Copyright   : (c) Josh Bohde, 2015
License     : GPL-3
Maintainer  : josh@joshbohde.com
Stability   : experimental
Portability : POSIX

A Haskell implementation of <http://www.hdrhistogram.org/ HdrHistogram>.
It allows storing counts of observed values within a range,
while maintaining precision to a configurable number of significant
digits.

The mutable histogram allows only writes, and conversion to and from
pure histograms. It follows the original implementation, and has
similar performance characteristics. Current recording benchmarks take
about 9ns, and allocates 16 bytes.

-}
{-# LANGUAGE DeriveGeneric       #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.HdrHistogram.Mutable (
  -- * Histogram
  Histogram(..), new, fromConfig,

  -- * Writing
  record, recordValues,

  -- * Converting
  freeze, unsafeFreeze, thaw, unsafeThaw,

  -- * Re-exports
  Config, HasConfig
  ) where

import           Control.DeepSeq                   (NFData, deepseq, rnf)
import           Control.Monad.Primitive           (PrimMonad, PrimState)
import           Data.Bits                         (FiniteBits)
import qualified Data.HdrHistogram                 as H
import           Data.HdrHistogram.Config
import           Data.HdrHistogram.Config.Internal
import           Data.Primitive.MutVar             (MutVar, modifyMutVar',
                                                    newMutVar, readMutVar)
import           Data.Proxy                        (Proxy (Proxy))
import           Data.Tagged                       (Tagged (Tagged))
import qualified Data.Vector.Unboxed               as U
import qualified Data.Vector.Unboxed.Mutable       as MU
import           GHC.Generics                      (Generic)

-- | A mutable 'Histogram'
data Histogram s c value count = Histogram {
  _config    :: HistogramConfig value,
  totalCount :: MutVar s count,
  counts     :: U.MVector s count
} deriving (Generic)

instance (NFData value, NFData count) => NFData (Histogram s config value count) where
  rnf (Histogram c _ vec) = deepseq c $ deepseq vec ()

-- | Construct a 'Histogram'
new :: forall m config a count.
      (PrimMonad m, HasConfig config,
       Integral a, FiniteBits a,
       U.Unbox count, Integral count) =>
      m (Histogram (PrimState m) config a count)
new = fromConfig (Tagged c :: Tagged config (HistogramConfig a))
  where
    c = getConfig p
    p = Proxy :: Proxy config

-- | Construct a 'Histogram' from the given 'HistogramConfig'. In this
-- case 'c' is a phantom type.
fromConfig :: (PrimMonad m, U.Unbox count, Integral count) => Tagged c (HistogramConfig value) -> m (Histogram (PrimState m) c value count)
fromConfig (Tagged c) = do
  vect <- MU.replicate (size c) 0
  totals <- newMutVar 0
  return Histogram {
    _config = c,
    totalCount = totals,
    counts = vect
  }


{-# INLINEABLE record #-}
-- | Record value single value to the 'Histogram'
record :: (Integral value, Integral count, FiniteBits value, U.Unbox count, PrimMonad m) =>
         Histogram (PrimState m) c value count -> value -> m ()
record h val = recordValues h val 1

{-# INLINEABLE recordValues #-}
-- | Record a multiple instances of a value value to the 'Histogram'
recordValues :: (Integral value, Integral count, FiniteBits value, U.Unbox count, PrimMonad m) =>
               Histogram (PrimState m) config value count -> value -> count -> m ()
recordValues h val count = do
  modifyMutVar' (totalCount h) (+ count)
  modify (counts h) (+ count) (indexForValue c val)
  where
    c = _config h
    modify v f i = do
      a <- MU.unsafeRead v i
      MU.unsafeWrite v i (f a)

-- | Convert a mutable 'Histogram' to a pure 'Histogram'
freeze :: (MU.Unbox count, PrimMonad m) => Histogram (PrimState m) config value count -> m (H.Histogram config value count)
freeze (Histogram c total vec) = do
  t <- readMutVar total
  v <- U.freeze vec
  return $ H.Histogram c t v

-- | Convert a mutable 'Histogram' to a pure 'Histogram'. The mutable cannot counte reused after this.
unsafeFreeze :: (MU.Unbox count, PrimMonad m) => Histogram (PrimState m) config value count -> m (H.Histogram config value count)
unsafeFreeze (Histogram c total vec) = do
  t <- readMutVar total
  v <- U.unsafeFreeze vec
  return $ H.Histogram c t v

-- | Convert a pure 'Histogram' to a mutable 'Histogram'.
thaw :: (MU.Unbox count, PrimMonad m) => H.Histogram config value count -> m (Histogram (PrimState m) config value count)
thaw (H.Histogram c total vec) = do
  t <- newMutVar total
  v <- U.thaw vec
  return $ Histogram c t v

-- | Convert a pure 'Histogram' to a mutable 'Histogram'. The pure cannot counte reused after this.
unsafeThaw :: (MU.Unbox count, PrimMonad m) => H.Histogram config value count -> m (Histogram (PrimState m) config value count)
unsafeThaw (H.Histogram c total vec) = do
  t <- newMutVar total
  v <- U.unsafeThaw vec
  return $ Histogram c t v