github.com/codingfuture/orig-energi3@v0.8.4/metrics/resetting_timer.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2018 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 package metrics 19 20 import ( 21 "math" 22 "sort" 23 "sync" 24 "time" 25 ) 26 27 // Initial slice capacity for the values stored in a ResettingTimer 28 const InitialResettingTimerSliceCap = 10 29 30 // ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval. 31 type ResettingTimer interface { 32 Values() []int64 33 Snapshot() ResettingTimer 34 Percentiles([]float64) []int64 35 Mean() float64 36 Time(func()) 37 Update(time.Duration) 38 UpdateSince(time.Time) 39 } 40 41 // GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a 42 // new StandardResettingTimer. 43 func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer { 44 if nil == r { 45 r = DefaultRegistry 46 } 47 return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer) 48 } 49 50 // NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer. 51 func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer { 52 c := NewResettingTimer() 53 if nil == r { 54 r = DefaultRegistry 55 } 56 r.Register(name, c) 57 return c 58 } 59 60 // NewResettingTimer constructs a new StandardResettingTimer 61 func NewResettingTimer() ResettingTimer { 62 if !Enabled { 63 return NilResettingTimer{} 64 } 65 return &StandardResettingTimer{ 66 values: make([]int64, 0, InitialResettingTimerSliceCap), 67 } 68 } 69 70 // NilResettingTimer is a no-op ResettingTimer. 71 type NilResettingTimer struct { 72 } 73 74 // Values is a no-op. 75 func (NilResettingTimer) Values() []int64 { return nil } 76 77 // Snapshot is a no-op. 78 func (NilResettingTimer) Snapshot() ResettingTimer { 79 return &ResettingTimerSnapshot{ 80 values: []int64{}, 81 } 82 } 83 84 // Time is a no-op. 85 func (NilResettingTimer) Time(func()) {} 86 87 // Update is a no-op. 88 func (NilResettingTimer) Update(time.Duration) {} 89 90 // Percentiles panics. 91 func (NilResettingTimer) Percentiles([]float64) []int64 { 92 panic("Percentiles called on a NilResettingTimer") 93 } 94 95 // Mean panics. 96 func (NilResettingTimer) Mean() float64 { 97 panic("Mean called on a NilResettingTimer") 98 } 99 100 // UpdateSince is a no-op. 101 func (NilResettingTimer) UpdateSince(time.Time) {} 102 103 // StandardResettingTimer is the standard implementation of a ResettingTimer. 104 // and Meter. 105 type StandardResettingTimer struct { 106 values []int64 107 mutex sync.Mutex 108 } 109 110 // Values returns a slice with all measurements. 111 func (t *StandardResettingTimer) Values() []int64 { 112 return t.values 113 } 114 115 // Snapshot resets the timer and returns a read-only copy of its contents. 116 func (t *StandardResettingTimer) Snapshot() ResettingTimer { 117 t.mutex.Lock() 118 defer t.mutex.Unlock() 119 currentValues := t.values 120 t.values = make([]int64, 0, InitialResettingTimerSliceCap) 121 122 return &ResettingTimerSnapshot{ 123 values: currentValues, 124 } 125 } 126 127 // Percentiles panics. 128 func (t *StandardResettingTimer) Percentiles([]float64) []int64 { 129 panic("Percentiles called on a StandardResettingTimer") 130 } 131 132 // Mean panics. 133 func (t *StandardResettingTimer) Mean() float64 { 134 panic("Mean called on a StandardResettingTimer") 135 } 136 137 // Record the duration of the execution of the given function. 138 func (t *StandardResettingTimer) Time(f func()) { 139 ts := time.Now() 140 f() 141 t.Update(time.Since(ts)) 142 } 143 144 // Record the duration of an event. 145 func (t *StandardResettingTimer) Update(d time.Duration) { 146 t.mutex.Lock() 147 defer t.mutex.Unlock() 148 t.values = append(t.values, int64(d)) 149 } 150 151 // Record the duration of an event that started at a time and ends now. 152 func (t *StandardResettingTimer) UpdateSince(ts time.Time) { 153 t.mutex.Lock() 154 defer t.mutex.Unlock() 155 t.values = append(t.values, int64(time.Since(ts))) 156 } 157 158 // ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer. 159 type ResettingTimerSnapshot struct { 160 values []int64 161 mean float64 162 thresholdBoundaries []int64 163 calculated bool 164 } 165 166 // Snapshot returns the snapshot. 167 func (t *ResettingTimerSnapshot) Snapshot() ResettingTimer { return t } 168 169 // Time panics. 170 func (*ResettingTimerSnapshot) Time(func()) { 171 panic("Time called on a ResettingTimerSnapshot") 172 } 173 174 // Update panics. 175 func (*ResettingTimerSnapshot) Update(time.Duration) { 176 panic("Update called on a ResettingTimerSnapshot") 177 } 178 179 // UpdateSince panics. 180 func (*ResettingTimerSnapshot) UpdateSince(time.Time) { 181 panic("UpdateSince called on a ResettingTimerSnapshot") 182 } 183 184 // Values returns all values from snapshot. 185 func (t *ResettingTimerSnapshot) Values() []int64 { 186 return t.values 187 } 188 189 // Percentiles returns the boundaries for the input percentiles. 190 func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []int64 { 191 t.calc(percentiles) 192 193 return t.thresholdBoundaries 194 } 195 196 // Mean returns the mean of the snapshotted values 197 func (t *ResettingTimerSnapshot) Mean() float64 { 198 if !t.calculated { 199 t.calc([]float64{}) 200 } 201 202 return t.mean 203 } 204 205 func (t *ResettingTimerSnapshot) calc(percentiles []float64) { 206 sort.Sort(Int64Slice(t.values)) 207 208 count := len(t.values) 209 if count > 0 { 210 min := t.values[0] 211 max := t.values[count-1] 212 213 cumulativeValues := make([]int64, count) 214 cumulativeValues[0] = min 215 for i := 1; i < count; i++ { 216 cumulativeValues[i] = t.values[i] + cumulativeValues[i-1] 217 } 218 219 t.thresholdBoundaries = make([]int64, len(percentiles)) 220 221 thresholdBoundary := max 222 223 for i, pct := range percentiles { 224 if count > 1 { 225 var abs float64 226 if pct >= 0 { 227 abs = pct 228 } else { 229 abs = 100 + pct 230 } 231 // poor man's math.Round(x): 232 // math.Floor(x + 0.5) 233 indexOfPerc := int(math.Floor(((abs / 100.0) * float64(count)) + 0.5)) 234 if pct >= 0 && indexOfPerc > 0 { 235 indexOfPerc -= 1 // index offset=0 236 } 237 thresholdBoundary = t.values[indexOfPerc] 238 } 239 240 t.thresholdBoundaries[i] = thresholdBoundary 241 } 242 243 sum := cumulativeValues[count-1] 244 t.mean = float64(sum) / float64(count) 245 } else { 246 t.thresholdBoundaries = make([]int64, len(percentiles)) 247 t.mean = 0 248 } 249 250 t.calculated = true 251 } 252 253 // Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order. 254 type Int64Slice []int64 255 256 func (s Int64Slice) Len() int { return len(s) } 257 func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] } 258 func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }