1// Copyright (C) 2016 Space Monkey, Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14// 15// WARNING: THE NON-M4 VERSIONS OF THIS FILE ARE GENERATED BY GO GENERATE! 16// ONLY MAKE CHANGES TO THE M4 FILE 17// 18 19package monkit 20 21import ( 22 "sort" 23) 24 25// IntDist keeps statistics about values such as 26// low/high/recent/average/quantiles. Not threadsafe. Construct with 27// NewIntDist(). Fields are expected to be read from but not written to. 28type IntDist struct { 29 // Low and High are the lowest and highest values observed since 30 // construction or the last reset. 31 Low, High int64 32 33 // Recent is the last observed value. 34 Recent int64 35 36 // Count is the number of observed values since construction or the last 37 // reset. 38 Count int64 39 40 // Sum is the sum of all the observed values since construction or the last 41 // reset. 42 Sum int64 43 44 key SeriesKey 45 reservoir [ReservoirSize]float32 46 rng xorshift128 47 sorted bool 48} 49 50func initIntDist(v *IntDist, key SeriesKey) { 51 v.key = key 52 v.rng = newXORShift128() 53} 54 55// NewIntDist creates a distribution of int64s. 56func NewIntDist(key SeriesKey) (d *IntDist) { 57 d = &IntDist{} 58 initIntDist(d, key) 59 return d 60} 61 62// Insert adds a value to the distribution, updating appropriate values. 63func (d *IntDist) Insert(val int64) { 64 if d.Count != 0 { 65 if val < d.Low { 66 d.Low = val 67 } 68 if val > d.High { 69 d.High = val 70 } 71 } else { 72 d.Low = val 73 d.High = val 74 } 75 d.Recent = val 76 d.Sum += val 77 78 index := d.Count 79 d.Count += 1 80 81 if index < ReservoirSize { 82 d.reservoir[index] = float32(val) 83 d.sorted = false 84 } else { 85 window := d.Count 86 // careful, the capitalization of Window is important 87 if Window > 0 && window > Window { 88 window = Window 89 } 90 // fast, but kind of biased. probably okay 91 j := d.rng.Uint64() % uint64(window) 92 if j < ReservoirSize { 93 d.reservoir[int(j)] = float32(val) 94 d.sorted = false 95 } 96 } 97} 98 99// FullAverage calculates and returns the average of all inserted values. 100func (d *IntDist) FullAverage() int64 { 101 if d.Count > 0 { 102 return d.Sum / int64(d.Count) 103 } 104 return 0 105} 106 107// ReservoirAverage calculates the average of the current reservoir. 108func (d *IntDist) ReservoirAverage() int64 { 109 amount := ReservoirSize 110 if d.Count < int64(amount) { 111 amount = int(d.Count) 112 } 113 if amount <= 0 { 114 return 0 115 } 116 var sum float32 117 for i := 0; i < amount; i++ { 118 sum += d.reservoir[i] 119 } 120 return int64(sum / float32(amount)) 121} 122 123// Query will return the approximate value at the given quantile from the 124// reservoir, where 0 <= quantile <= 1. 125func (d *IntDist) Query(quantile float64) int64 { 126 rlen := int(ReservoirSize) 127 if int64(rlen) > d.Count { 128 rlen = int(d.Count) 129 } 130 131 if rlen < 2 { 132 return int64(d.reservoir[0]) 133 } 134 135 reservoir := d.reservoir[:rlen] 136 if !d.sorted { 137 sort.Sort(float32Slice(reservoir)) 138 d.sorted = true 139 } 140 141 if quantile <= 0 { 142 return int64(reservoir[0]) 143 } 144 if quantile >= 1 { 145 return int64(reservoir[rlen-1]) 146 } 147 148 idx_float := quantile * float64(rlen-1) 149 idx := int(idx_float) 150 151 diff := idx_float - float64(idx) 152 prior := float64(reservoir[idx]) 153 return int64(prior + diff*(float64(reservoir[idx+1])-prior)) 154} 155 156// Copy returns a full copy of the entire distribution. 157func (d *IntDist) Copy() *IntDist { 158 cp := *d 159 cp.rng = newXORShift128() 160 return &cp 161} 162 163func (d *IntDist) Reset() { 164 d.Low, d.High, d.Recent, d.Count, d.Sum = 0, 0, 0, 0, 0 165 // resetting count will reset the quantile reservoir 166} 167 168func (d *IntDist) Stats(cb func(key SeriesKey, field string, val float64)) { 169 count := d.Count 170 cb(d.key, "count", float64(count)) 171 if count > 0 { 172 cb(d.key, "sum", d.toFloat64(d.Sum)) 173 cb(d.key, "min", d.toFloat64(d.Low)) 174 cb(d.key, "avg", d.toFloat64(d.FullAverage())) 175 cb(d.key, "max", d.toFloat64(d.High)) 176 cb(d.key, "rmin", d.toFloat64(d.Query(0))) 177 cb(d.key, "ravg", d.toFloat64(d.ReservoirAverage())) 178 cb(d.key, "r10", d.toFloat64(d.Query(.1))) 179 cb(d.key, "r50", d.toFloat64(d.Query(.5))) 180 cb(d.key, "r90", d.toFloat64(d.Query(.9))) 181 cb(d.key, "rmax", d.toFloat64(d.Query(1))) 182 cb(d.key, "recent", d.toFloat64(d.Recent)) 183 } 184} 185