Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions slidingwindow.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ func (sw *SlidingWindow) Average(window time.Duration) float64 {
return float64(total) / float64(sampleCount)
}

// RangeAverage returns the unweighted mean of the window
// from start to end
func (sw *SlidingWindow) RangeAverage(start, end time.Duration) float64 {
total, sampleCount := sw.RangeTotal(start, end)
if sampleCount == 0 {
return 0
}
return float64(total) / float64(sampleCount)
}

// Reset the samples in this sliding time window.
func (sw *SlidingWindow) Reset() {
sw.Lock()
Expand Down Expand Up @@ -139,3 +149,30 @@ func (sw *SlidingWindow) Total(window time.Duration) (int64, int) {

return total, sampleCount
}

// RangeTotal returns the sum of all values from start to end, as well as
// the number of samples.
func (sw *SlidingWindow) RangeTotal(start, end time.Duration) (int64, int) {
if start > end {
start = end
}
window := end - start
sampleCount := int(window / sw.granularity)
if sampleCount > sw.size {
sampleCount = sw.size
}
sw.RLock()
defer sw.RUnlock()

var total int64
endPos := sw.pos - sw.size + int( end / sw.granularity)
for i := 1; i <= sampleCount; i++ {
pos := endPos - i
if pos < 0 {
pos += len(sw.samples)
}

total += sw.samples[pos]
}
return total, sampleCount
}
64 changes: 64 additions & 0 deletions slidingwindow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,29 @@ func TestAverage(t *testing.T) {
}
}

func TestRangeAverage(t *testing.T) {
sw := &SlidingWindow{
window: 10 * time.Second,
granularity: time.Second,
samples: []int64{1, 2, 5, 0, 0, 0, 0, 0, 4, 0},
pos: 2,
size: 10,
}

if v := sw.RangeAverage(0, 0); v != 0 {
t.Errorf("expected the average with a window of 0 seconds be 0, not %f", v)
}
if v := sw.RangeAverage(9 * time.Second, 10 * time.Second); v != 2 {
t.Errorf("expected the average from second 9 to 10 to be 2, not %f", v)
}
if v := sw.RangeAverage(8 * time.Second, 10 * time.Second); v != 1.5 {
t.Errorf("expected the average from second 8 to 10 to be 1.5, not %f", v)
}
if v := sw.RangeAverage(4 * time.Second, 9 * time.Second); v != 1 {
t.Errorf("expected the average from second 4 to 9 to be 1, not %f", v)
}
}

func TestReset(t *testing.T) {
sw := MustNew(2*time.Second, time.Second)
defer sw.Stop()
Expand Down Expand Up @@ -131,3 +154,44 @@ func TestTotal(t *testing.T) {
t.Errorf("expected the total over the last 10 seconds to be 12, not %d", v)
}
}

func TestRangeTotal(t *testing.T) {
sw := &SlidingWindow{
window: 10 * time.Second,
granularity: time.Second,
samples: []int64{1, 2, 5, 0, 0, 0, 0, 0, 4, 8},
pos: 10,
size: 10,
}

if v, _ := sw.RangeTotal(time.Second, 2 * time.Second); v != 2 {
t.Errorf("expected the total from second 1 to 2 to be 2, not %d", v)
}
if v, _ := sw.RangeTotal(time.Second, 3 * time.Second); v != 7 {
t.Errorf("expected the total from second 1 to 3 to be 7, not %d", v)
}
if v, _ := sw.RangeTotal(time.Second * 3, time.Second * 8); v != 0 {
t.Errorf("expected the total from second 3 to 8 to be 0, not %d", v)
}
if v, _ := sw.RangeTotal(0, time.Second * 2); v != 3 {
t.Errorf("expected the total from second 0 to 2 to be 3, not %d", v)
}
if v, _ := sw.RangeTotal(9 * time.Second, 10 * time.Second); v != 8 {
t.Errorf("expected the total from second 9 to 10 to be 8, not %d", v)
}

sw = &SlidingWindow{
window: 10 * time.Second,
granularity: time.Second,
samples: []int64{1, 2, 5, 0, 0, 0, 0, 0, 4, 8},
pos: 2,
size: 10,
}

if v, _ := sw.RangeTotal(9 * time.Second, 10 * time.Second); v != 2 {
t.Errorf("expected the total from second 9 to 10 to be 2, not %d", v)
}
if v, _ := sw.RangeTotal(7 * time.Second, 9 * time.Second); v != 9 {
t.Errorf("expected the total from second 7 to 9 to be 9, not %d", v)
}
}