Skip to content

Commit

Permalink
optimized interval_meter
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenButtolph committed Sep 4, 2020
1 parent 881df7f commit 54d9362
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 17 deletions.
55 changes: 40 additions & 15 deletions utils/uptime/interval_meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

const (
maxSkippedIntervals = 5
maxSkippedIntervals = 32
)

type intervalMeter struct {
Expand Down Expand Up @@ -55,26 +55,51 @@ func (a *intervalMeter) Read() float64 {
if !currentTime.After(a.lastUpdated) {
return a.value
}
if totalTime := currentTime.Sub(a.lastUpdated); totalTime > maxSkippedIntervals*a.halflife {

// try to finish the current round
if currentTime.After(a.nextHalvening) {
if a.running {
a.value = 1
} else {
a.value = 0
additionalRunningTime := float64(a.nextHalvening.Sub(a.lastUpdated)) / float64(a.halflife)
a.value += additionalRunningTime / 2
}
} else {
for !currentTime.Before(a.nextHalvening) {
a.lastUpdated = a.nextHalvening
a.nextHalvening = a.nextHalvening.Add(a.halflife)
a.value /= 2

// try to skip future rounds
if totalTime := currentTime.Sub(a.lastUpdated); totalTime > a.halflife {
numSkippedPeriods := totalTime / a.halflife
if numSkippedPeriods > maxSkippedIntervals {
// If this meter hasn't been read in a long time, avoid
// potential shifting overflow issues and just jump to a
// reasonable value.
if a.running {
a.value = 1
} else {
a.value = 0
}
a.lastUpdated = currentTime
a.nextHalvening = a.lastUpdated.Add(a.halflife)
return a.value
}

invFactor := 1 << uint(numSkippedPeriods)
factor := 1 / float64(invFactor)
a.value *= factor
if a.running {
additionalRunningTime := float64(a.nextHalvening.Sub(a.lastUpdated)) / float64(a.halflife)
a.value += additionalRunningTime / 2
a.value += 1 - factor
}
a.lastUpdated = a.nextHalvening
a.nextHalvening = a.nextHalvening.Add(a.halflife)
a.value /= 2
skippedDuration := a.halflife * numSkippedPeriods
a.lastUpdated = a.lastUpdated.Add(skippedDuration)
a.nextHalvening = a.nextHalvening.Add(skippedDuration)
}
if a.running {
additionalRunningTime := float64(currentTime.Sub(a.lastUpdated)) / float64(a.halflife)
a.value += additionalRunningTime / 2
}
}

// increment the value for the current round
if a.running {
additionalRunningTime := float64(currentTime.Sub(a.lastUpdated)) / float64(a.halflife)
a.value += additionalRunningTime / 2
}
a.lastUpdated = currentTime
return a.value
Expand Down
2 changes: 1 addition & 1 deletion utils/uptime/interval_meter_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func BenchmarkIntervalMeterSeconds(b *testing.B) {

b.ResetTimer()
for i := 0; i < b.N; i++ {
currentTime = currentTime.Add(4 * time.Second)
currentTime = currentTime.Add(10*time.Second + 500*time.Millisecond)
m.clock.Set(currentTime)
time.Now()

Expand Down
16 changes: 16 additions & 0 deletions utils/uptime/interval_meter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@ func TestIntervalMeter(t *testing.T) {
if uptime := m.Read(); math.Abs(uptime-.625) > epsilon {
t.Fatalf("Wrong uptime value. Expected %f got %f", .625, uptime)
}

currentTime = currentTime.Add((maxSkippedIntervals + 2) * halflife)
m.clock.Set(currentTime)

if uptime := m.Read(); math.Abs(uptime-1) > epsilon {
t.Fatalf("Wrong uptime value. Expected %d got %f", 1, uptime)
}

m.Stop()

currentTime = currentTime.Add((maxSkippedIntervals + 2) * halflife)
m.clock.Set(currentTime)

if uptime := m.Read(); math.Abs(uptime-0) > epsilon {
t.Fatalf("Wrong uptime value. Expected %d got %f", 0, uptime)
}
}

func TestIntervalMeterTimeTravel(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion utils/uptime/meter_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func BenchmarkMeterSeconds(b *testing.B) {

b.ResetTimer()
for i := 0; i < b.N; i++ {
currentTime = currentTime.Add(4 * time.Second)
currentTime = currentTime.Add(10*time.Second + 500*time.Millisecond)
m.clock.Set(currentTime)
time.Now()

Expand Down

0 comments on commit 54d9362

Please sign in to comment.