go.mway.dev/chrono@v0.6.1-0.20240126030049-189c5aef20d2/rate/recorder.go (about) 1 // Copyright (c) 2023 Matt Way 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to 5 // deal in the Software without restriction, including without limitation the 6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 // sell copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 // IN THE THE SOFTWARE. 20 21 package rate 22 23 import ( 24 "time" 25 26 "go.mway.dev/chrono/clock" 27 "go.uber.org/atomic" 28 ) 29 30 // A Recorder records added counts and reports the rate of the total count over 31 // the elapsed time. 32 type Recorder struct { 33 clock clock.Clock 34 count atomic.Int64 35 epoch atomic.Int64 36 } 37 38 // NewRecorder creates a new [Recorder] that uses the system's monotonic clock. 39 func NewRecorder() *Recorder { 40 return NewRecorderWithClock(clock.NewMonotonicClock()) 41 } 42 43 // NewRecorderWithClock returns a new [Recorder] that uses the given clock. 44 func NewRecorderWithClock(clk clock.Clock) *Recorder { 45 r := &Recorder{ 46 clock: clk, 47 } 48 r.Reset() 49 return r 50 } 51 52 // Add adds n to the running count. 53 func (r *Recorder) Add(n int) { 54 r.count.Add(int64(n)) 55 } 56 57 // Rate returns a [Rate] that represents the running count and time elapsed 58 // since the recorder's clock started. 59 func (r *Recorder) Rate() Rate { 60 return Rate{ 61 count: r.count.Load(), 62 elapsed: r.clock.SinceNanotime(r.epoch.Load()), 63 } 64 } 65 66 // Reset returns the current [Rate] and resets the recorder's running count and 67 // epoch. 68 func (r *Recorder) Reset() Rate { 69 var ( 70 now = r.clock.Nanotime() 71 elapsed = time.Duration(now - r.epoch.Swap(now)) 72 ) 73 return Rate{ 74 count: r.count.Swap(0), 75 elapsed: elapsed, 76 } 77 } 78 79 // A Rate is a count over a period of time. 80 type Rate struct { 81 count int64 82 elapsed time.Duration 83 } 84 85 // Per returns the rate's count over the given period of time. 86 func (r Rate) Per(d time.Duration) float64 { 87 return (float64(r.count) / float64(r.elapsed)) * float64(d) 88 }