-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathhistogram.go
More file actions
113 lines (102 loc) · 2.47 KB
/
Copy pathhistogram.go
File metadata and controls
113 lines (102 loc) · 2.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package agilepool
type bucketDef struct {
low int64
high int64 // -1 means infinity
}
// histogram implements a fixed-bucket histogram with FIFO eviction.
// It tracks the frequency distribution of samples over the last N windows.
type histogram struct {
buckets []bucketDef
counts []int64 // current count per bucket
total int64 // total samples currently tracked
samples []int // ring buffer of bucket index for each sample
pos int // next write position
filled bool // true once ring buffer has wrapped
}
func newHistogram(buckets []bucketDef, windowSize int) *histogram {
return &histogram{
buckets: buckets,
counts: make([]int64, len(buckets)),
samples: make([]int, windowSize),
}
}
func (h *histogram) add(value int64) {
idx := h.bucketIndex(value)
// If we're overwriting an old sample, decrement its bucket count
if h.filled {
oldIdx := h.samples[h.pos]
h.counts[oldIdx]--
h.total--
}
// Record new sample
h.samples[h.pos] = idx
h.counts[idx]++
h.total++
h.pos++
if h.pos >= len(h.samples) {
h.pos = 0
h.filled = true
}
}
// median returns the approximate median value by finding which bucket
// contains the middle element, then returning the bucket midpoint.
func (h *histogram) median() float64 {
if h.total == 0 {
return 0
}
mid := h.total / 2
var cum int64
for i, cnt := range h.counts {
cum += cnt
if cum > mid {
b := h.buckets[i]
if b.high == -1 {
return float64(b.low) // bottom of open-ended bucket
}
return float64(b.low+b.high) / 2.0
}
}
return 0
}
func (h *histogram) bucketIndex(value int64) int {
for i, b := range h.buckets {
if value >= b.low && (b.high == -1 || value <= b.high) {
return i
}
}
// Fallback: last bucket
return len(h.buckets) - 1
}
// default bucket definitions (counts per 100ms sample window)
var (
submitBuckets = []bucketDef{
{low: 0, high: 0},
{low: 1, high: 5},
{low: 6, high: 20},
{low: 21, high: 100},
{low: 101, high: 500},
{low: 501, high: 2000},
{low: 2001, high: 10000},
{low: 10001, high: -1},
}
consumeBuckets = []bucketDef{
{low: 0, high: 0},
{low: 1, high: 5},
{low: 6, high: 20},
{low: 21, high: 100},
{low: 101, high: 500},
{low: 501, high: 2000},
{low: 2001, high: 10000},
{low: 10001, high: -1},
}
exitBuckets = []bucketDef{
{low: 0, high: 0},
{low: 1, high: 2},
{low: 3, high: 5},
{low: 6, high: 10},
{low: 11, high: 20},
{low: 21, high: 50},
{low: 51, high: 100},
{low: 101, high: -1},
}
)