17
"golang.org/x/net/internal/timeseries"
27
type histogram struct {
36
func (h *histogram) addMeasurement(value int64) {
39
h.sumOfSquares += float64(value) * float64(value)
41
bucketIndex := getBucket(value)
43
if h.valueCount == 0 || (h.valueCount > 0 && h.value == bucketIndex) {
48
h.buckets[bucketIndex]++
52
func (h *histogram) allocateBuckets() {
54
h.buckets = make([]int64, bucketCount)
55
h.buckets[h.value] = h.valueCount
61
func log2(i int64) int {
63
for ; i >= 0x100; i >>= 8 {
66
for ; i > 0; i >>= 1 {
72
func getBucket(i int64) (index int) {
77
if index >= bucketCount {
78
index = bucketCount - 1
84
func (h *histogram) total() (total int64) {
85
if h.valueCount >= 0 {
88
for _, val := range h.buckets {
95
func (h *histogram) average() float64 {
100
return float64(h.sum) / float64(t)
104
func (h *histogram) variance() float64 {
105
t := float64(h.total())
109
s := float64(h.sum) / t
110
return h.sumOfSquares/t - s*s
114
func (h *histogram) standardDeviation() float64 {
115
return math.Sqrt(h.variance())
120
func (h *histogram) percentileBoundary(percentile float64) int64 {
126
} else if total == 1 {
127
return int64(h.average())
130
percentOfTotal := round(float64(total) * percentile)
131
var runningTotal int64
133
for i := range h.buckets {
134
value := h.buckets[i]
135
runningTotal += value
136
if runningTotal == percentOfTotal {
143
min := bucketBoundary(j)
144
if runningTotal < total {
145
for h.buckets[j] == 0 {
149
max := bucketBoundary(j)
150
return min + round(float64(max-min)/2)
151
} else if runningTotal > percentOfTotal {
153
delta := runningTotal - percentOfTotal
154
percentBucket := float64(value-delta) / float64(value)
155
bucketMin := bucketBoundary(uint8(i))
156
nextBucketMin := bucketBoundary(uint8(i + 1))
157
bucketSize := nextBucketMin - bucketMin
158
return bucketMin + round(percentBucket*float64(bucketSize))
161
return bucketBoundary(bucketCount - 1)
165
func (h *histogram) median() int64 {
166
return h.percentileBoundary(0.5)
170
func (h *histogram) Add(other timeseries.Observable) {
171
o := other.(*histogram)
172
if o.valueCount == 0 {
174
} else if h.valueCount >= 0 && o.valueCount > 0 && h.value == o.value {
176
h.valueCount += o.valueCount
180
if o.valueCount >= 0 {
181
h.buckets[o.value] += o.valueCount
183
for i := range h.buckets {
184
h.buckets[i] += o.buckets[i]
188
h.sumOfSquares += o.sumOfSquares
193
func (h *histogram) Clear() {
202
func (h *histogram) CopyFrom(other timeseries.Observable) {
203
o := other.(*histogram)
204
if o.valueCount == -1 {
206
copy(h.buckets, o.buckets)
209
h.sumOfSquares = o.sumOfSquares
211
h.valueCount = o.valueCount
215
func (h *histogram) Multiply(ratio float64) {
216
if h.valueCount == -1 {
217
for i := range h.buckets {
218
h.buckets[i] = int64(float64(h.buckets[i]) * ratio)
221
h.valueCount = int64(float64(h.valueCount) * ratio)
223
h.sum = int64(float64(h.sum) * ratio)
224
h.sumOfSquares = h.sumOfSquares * ratio
228
func (h *histogram) New() timeseries.Observable {
234
func (h *histogram) String() string {
235
return fmt.Sprintf("%d, %f, %d, %d, %v",
236
h.sum, h.sumOfSquares, h.value, h.valueCount, h.buckets)
240
func round(in float64) int64 {
241
return int64(math.Floor(in + 0.5))
245
func bucketBoundary(bucket uint8) int64 {
253
type bucketData struct {
256
Pct, CumulativePct float64
262
Buckets []*bucketData
264
Mean, StandardDeviation float64
268
const maxHTMLBarWidth = 350.0
271
func (h *histogram) newData() *data {
276
maxBucket := int64(0)
277
for _, n := range h.buckets {
283
barsizeMult := maxHTMLBarWidth / float64(maxBucket)
288
pctMult = 100.0 / float64(total)
291
buckets := make([]*bucketData, len(h.buckets))
292
runningTotal := int64(0)
293
for i, n := range h.buckets {
299
if i < bucketCount-1 {
300
upperBound = bucketBoundary(uint8(i + 1))
302
upperBound = math.MaxInt64
304
buckets[i] = &bucketData{
305
Lower: bucketBoundary(uint8(i)),
308
Pct: float64(n) * pctMult,
309
CumulativePct: float64(runningTotal) * pctMult,
310
GraphWidth: int(float64(n) * barsizeMult),
318
StandardDeviation: h.standardDeviation(),
322
func (h *histogram) html() template.HTML {
323
buf := new(bytes.Buffer)
324
if err := distTmpl().Execute(buf, h.newData()); err != nil {
326
log.Printf("net/trace: couldn't execute template: %v", err)
328
return template.HTML(buf.String())
331
var distTmplCache *template.Template
332
var distTmplOnce sync.Once
334
func distTmpl() *template.Template {
335
distTmplOnce.Do(func() {
337
distTmplCache = template.Must(template.New("distTmpl").Parse(`
340
<td style="padding:0.25em">Count: {{.Count}}</td>
341
<td style="padding:0.25em">Mean: {{printf "%.0f" .Mean}}</td>
342
<td style="padding:0.25em">StdDev: {{printf "%.0f" .StandardDeviation}}</td>
343
<td style="padding:0.25em">Median: {{.Median}}</td>
348
{{range $b := .Buckets}}
351
<td style="padding:0 0 0 0.25em">[</td>
352
<td style="text-align:right;padding:0 0.25em">{{.Lower}},</td>
353
<td style="text-align:right;padding:0 0.25em">{{.Upper}})</td>
354
<td style="text-align:right;padding:0 0.25em">{{.N}}</td>
355
<td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .Pct}}%</td>
356
<td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .CumulativePct}}%</td>
357
<td><div style="background-color: blue; height: 1em; width: {{.GraphWidth}};"></div></td>