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
|
module Events.SparkStats
( SparkStats(..)
, initial, create, rescale, aggregate, agEx
) where
import Data.Word (Word64)
-- | Sparks change state. Each state transition process has a duration.
-- Spark statistics, for a given duration, record the spark transition rate
-- (the number of sparks that enter a given state within the interval)
-- and the absolute mean, maximal and minimal number of sparks
-- in the spark pool within the duration.
data SparkStats =
SparkStats { rateCreated, rateDud, rateOverflowed,
rateConverted, rateFizzled, rateGCd,
meanPool, maxPool, minPool :: {-# UNPACK #-}!Double }
deriving (Show, Eq)
-- | Initial, default value of spark stats, at the start of runtime,
-- before any spark activity is recorded.
initial :: SparkStats
initial = SparkStats 0 0 0 0 0 0 0 0 0
-- | Create spark stats for a duration, given absolute
-- numbers of sparks in all categories at the start and end of the duration.
-- The units for spark transitions (first 6 counters) is [spark/duration]:
-- the fact that intervals may have different lenghts is ignored here.
-- The units for the pool stats are just [spark].
-- The values in the second counter have to be greater or equal
-- to the values in the first counter, except for the spark pool size.
-- For pool size, we take into account only the first sample,
-- to visualize more detail at high zoom levels, at the cost
-- of a slight shift of the graph. Mathematically, this corresponds
-- to taking the initial durations as centered around samples,
-- but to have the same tree for rates and pool sizes, we then have
-- to shift the durations by half interval size to the right
-- (which would be neglectable if the interval was small and even).
create :: (Word64, Word64, Word64, Word64, Word64, Word64, Word64)
-> (Word64, Word64, Word64, Word64, Word64, Word64, Word64)
-> SparkStats
create (crt1, dud1, ovf1, cnv1, fiz1, gcd1, remaining1)
(crt2, dud2, ovf2, cnv2, fiz2, gcd2, _remaining2) =
let (crt, dud, ovf, cnv, fiz, gcd) =
(fromIntegral $ crt2 - crt1,
fromIntegral $ dud2 - dud1,
fromIntegral $ ovf2 - ovf1,
fromIntegral $ cnv2 - cnv1,
fromIntegral $ fiz2 - fiz1,
fromIntegral $ gcd2 - gcd1)
p = fromIntegral remaining1
in SparkStats crt dud ovf cnv fiz gcd p p p
-- | Reduce a list of spark stats; spark pool stats are overwritten.
foldStats :: (Double -> Double -> Double)
-> Double -> Double -> Double
-> [SparkStats] -> SparkStats
foldStats f meanP maxP minP l
= SparkStats
(foldr f 0 (map rateCreated l))
(foldr f 0 (map rateDud l))
(foldr f 0 (map rateOverflowed l))
(foldr f 0 (map rateConverted l))
(foldr f 0 (map rateFizzled l))
(foldr f 0 (map rateGCd l))
meanP maxP minP
-- | Rescale the spark transition stats, e.g., to change their units.
rescale :: Double -> SparkStats -> SparkStats
rescale scale s =
let f w _ = scale * w
in foldStats f (meanPool s) (maxPool s) (minPool s) [s]
-- | Derive spark stats for an interval from a list of spark stats,
-- in reverse chronological order, of consecutive subintervals
-- that sum up to the original interval.
aggregate :: [SparkStats] -> SparkStats
aggregate [] = error "aggregate"
aggregate [s] = s -- optimization
aggregate l =
let meanP = sum (map meanPool l) / fromIntegral (length l) -- TODO: inaccurate
maxP = maximum (map maxPool l)
minP = minimum (map minPool l)
in foldStats (+) meanP maxP minP l
-- | Extrapolate spark stats from previous data.
-- Absolute pools size values extrapolate by staying constant,
-- rates of change of spark status extrapolate by dropping to 0
-- (which corresponds to absolute numbers of sparks staying constant).
extrapolate :: SparkStats -> SparkStats
extrapolate s =
let f w _ = 0 * w
in foldStats f (meanPool s) (maxPool s) (minPool s) [s]
-- | Aggregate, if any data provided. Extrapolate from previous data, otherwise.
-- In both cases, the second component is the new choice of "previous data".
-- The list of stats is expected in reverse chronological order,
-- as for aggregate.
agEx :: [SparkStats] -> SparkStats -> (SparkStats, SparkStats)
agEx [] s = (extrapolate s, s)
agEx l@(s:_) _ = (aggregate l, s)
|