timeseries.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*
  2. *
  3. * Copyright 2017 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. // Copyright (c) 2015 Arista Networks, Inc.
  19. // Use of this source code is governed by the Apache License 2.0
  20. // that can be found in the COPYING file.
  21. package stats
  22. import (
  23. "math"
  24. "time"
  25. )
  26. // timeseries holds the history of a changing value over a predefined period of
  27. // time.
  28. type timeseries struct {
  29. size int // The number of time slots. Equivalent to len(slots).
  30. resolution time.Duration // The time resolution of each slot.
  31. stepCount int64 // The number of intervals seen since creation.
  32. head int // The position of the current time in slots.
  33. time time.Time // The time at the beginning of the current time slot.
  34. slots []int64 // A circular buffer of time slots.
  35. }
  36. // newTimeSeries returns a newly allocated timeseries that covers the requested
  37. // period with the given resolution.
  38. func newTimeSeries(initialTime time.Time, period, resolution time.Duration) *timeseries {
  39. size := int(period.Nanoseconds()/resolution.Nanoseconds()) + 1
  40. return &timeseries{
  41. size: size,
  42. resolution: resolution,
  43. stepCount: 1,
  44. time: initialTime,
  45. slots: make([]int64, size),
  46. }
  47. }
  48. // advanceTimeWithFill moves the timeseries forward to time t and fills in any
  49. // slots that get skipped in the process with the given value. Values older than
  50. // the timeseries period are lost.
  51. func (ts *timeseries) advanceTimeWithFill(t time.Time, value int64) {
  52. advanceTo := t.Truncate(ts.resolution)
  53. if !advanceTo.After(ts.time) {
  54. // This is shortcut for the most common case of a busy counter
  55. // where updates come in many times per ts.resolution.
  56. ts.time = advanceTo
  57. return
  58. }
  59. steps := int(advanceTo.Sub(ts.time).Nanoseconds() / ts.resolution.Nanoseconds())
  60. ts.stepCount += int64(steps)
  61. if steps > ts.size {
  62. steps = ts.size
  63. }
  64. for steps > 0 {
  65. ts.head = (ts.head + 1) % ts.size
  66. ts.slots[ts.head] = value
  67. steps--
  68. }
  69. ts.time = advanceTo
  70. }
  71. // advanceTime moves the timeseries forward to time t and fills in any slots
  72. // that get skipped in the process with the head value. Values older than the
  73. // timeseries period are lost.
  74. func (ts *timeseries) advanceTime(t time.Time) {
  75. ts.advanceTimeWithFill(t, ts.slots[ts.head])
  76. }
  77. // set sets the current value of the timeseries.
  78. func (ts *timeseries) set(value int64) {
  79. ts.slots[ts.head] = value
  80. }
  81. // incr sets the current value of the timeseries.
  82. func (ts *timeseries) incr(delta int64) {
  83. ts.slots[ts.head] += delta
  84. }
  85. // headValue returns the latest value from the timeseries.
  86. func (ts *timeseries) headValue() int64 {
  87. return ts.slots[ts.head]
  88. }
  89. // headTime returns the time of the latest value from the timeseries.
  90. func (ts *timeseries) headTime() time.Time {
  91. return ts.time
  92. }
  93. // tailValue returns the oldest value from the timeseries.
  94. func (ts *timeseries) tailValue() int64 {
  95. if ts.stepCount < int64(ts.size) {
  96. return 0
  97. }
  98. return ts.slots[(ts.head+1)%ts.size]
  99. }
  100. // tailTime returns the time of the oldest value from the timeseries.
  101. func (ts *timeseries) tailTime() time.Time {
  102. size := int64(ts.size)
  103. if ts.stepCount < size {
  104. size = ts.stepCount
  105. }
  106. return ts.time.Add(-time.Duration(size-1) * ts.resolution)
  107. }
  108. // delta returns the difference between the newest and oldest values from the
  109. // timeseries.
  110. func (ts *timeseries) delta() int64 {
  111. return ts.headValue() - ts.tailValue()
  112. }
  113. // rate returns the rate of change between the oldest and newest values from
  114. // the timeseries in units per second.
  115. func (ts *timeseries) rate() float64 {
  116. deltaTime := ts.headTime().Sub(ts.tailTime()).Seconds()
  117. if deltaTime == 0 {
  118. return 0
  119. }
  120. return float64(ts.delta()) / deltaTime
  121. }
  122. // min returns the smallest value from the timeseries.
  123. func (ts *timeseries) min() int64 {
  124. to := ts.size
  125. if ts.stepCount < int64(ts.size) {
  126. to = ts.head + 1
  127. }
  128. tail := (ts.head + 1) % ts.size
  129. min := int64(math.MaxInt64)
  130. for b := 0; b < to; b++ {
  131. if b != tail && ts.slots[b] < min {
  132. min = ts.slots[b]
  133. }
  134. }
  135. return min
  136. }
  137. // max returns the largest value from the timeseries.
  138. func (ts *timeseries) max() int64 {
  139. to := ts.size
  140. if ts.stepCount < int64(ts.size) {
  141. to = ts.head + 1
  142. }
  143. tail := (ts.head + 1) % ts.size
  144. max := int64(math.MinInt64)
  145. for b := 0; b < to; b++ {
  146. if b != tail && ts.slots[b] > max {
  147. max = ts.slots[b]
  148. }
  149. }
  150. return max
  151. }
  152. // reset resets the timeseries to an empty state.
  153. func (ts *timeseries) reset(t time.Time) {
  154. ts.head = 0
  155. ts.time = t
  156. ts.stepCount = 1
  157. ts.slots = make([]int64, ts.size)
  158. }