package trace
import (
)
const (
bucketCount = 38
)
type histogram struct {
sum int64
sumOfSquares float64
buckets []int64
value int
valueCount int64
}
func ( *histogram) ( int64) {
.sum +=
.sumOfSquares += float64() * float64()
:= getBucket()
if .valueCount == 0 || (.valueCount > 0 && .value == ) {
.value =
.valueCount++
} else {
.allocateBuckets()
.buckets[]++
}
}
func ( *histogram) () {
if .buckets == nil {
.buckets = make([]int64, bucketCount)
.buckets[.value] = .valueCount
.value = 0
.valueCount = -1
}
}
func ( int64) int {
:= 0
for ; >= 0x100; >>= 8 {
+= 8
}
for ; > 0; >>= 1 {
+= 1
}
return
}
func ( int64) ( int) {
= log2() - 1
if < 0 {
= 0
}
if >= bucketCount {
= bucketCount - 1
}
return
}
func ( *histogram) () ( int64) {
if .valueCount >= 0 {
= .valueCount
}
for , := range .buckets {
+= int64()
}
return
}
func ( *histogram) () float64 {
:= .total()
if == 0 {
return 0
}
return float64(.sum) / float64()
}
func ( *histogram) () float64 {
:= float64(.total())
if == 0 {
return 0
}
:= float64(.sum) /
return .sumOfSquares/ - *
}
func ( *histogram) () float64 {
return math.Sqrt(.variance())
}
func ( *histogram) ( float64) int64 {
:= .total()
if == 0 {
return 0
} else if == 1 {
return int64(.average())
}
:= round(float64() * )
var int64
for := range .buckets {
:= .buckets[]
+=
if == {
:= uint8( + 1)
:= bucketBoundary()
if < {
for .buckets[] == 0 {
++
}
}
:= bucketBoundary()
return + round(float64(-)/2)
} else if > {
:= -
:= float64(-) / float64()
:= bucketBoundary(uint8())
:= bucketBoundary(uint8( + 1))
:= -
return + round(*float64())
}
}
return bucketBoundary(bucketCount - 1)
}
func ( *histogram) () int64 {
return .percentileBoundary(0.5)
}
func ( *histogram) ( timeseries.Observable) {
:= .(*histogram)
if .valueCount == 0 {
} else if .valueCount >= 0 && .valueCount > 0 && .value == .value {
.valueCount += .valueCount
} else {
.allocateBuckets()
if .valueCount >= 0 {
.buckets[.value] += .valueCount
} else {
for := range .buckets {
.buckets[] += .buckets[]
}
}
}
.sumOfSquares += .sumOfSquares
.sum += .sum
}
func ( *histogram) () {
.buckets = nil
.value = 0
.valueCount = 0
.sum = 0
.sumOfSquares = 0
}
func ( *histogram) ( timeseries.Observable) {
:= .(*histogram)
if .valueCount == -1 {
.allocateBuckets()
copy(.buckets, .buckets)
}
.sum = .sum
.sumOfSquares = .sumOfSquares
.value = .value
.valueCount = .valueCount
}
func ( *histogram) ( float64) {
if .valueCount == -1 {
for := range .buckets {
.buckets[] = int64(float64(.buckets[]) * )
}
} else {
.valueCount = int64(float64(.valueCount) * )
}
.sum = int64(float64(.sum) * )
.sumOfSquares = .sumOfSquares *
}
func ( *histogram) () timeseries.Observable {
:= new(histogram)
.Clear()
return
}
func ( *histogram) () string {
return fmt.Sprintf("%d, %f, %d, %d, %v",
.sum, .sumOfSquares, .value, .valueCount, .buckets)
}
func ( float64) int64 {
return int64(math.Floor( + 0.5))
}
func ( uint8) int64 {
if == 0 {
return 0
}
return 1 <<
}
type bucketData struct {
Lower, Upper int64
N int64
Pct, CumulativePct float64
GraphWidth int
}
type data struct {
Buckets []*bucketData
Count, Median int64
Mean, StandardDeviation float64
}
const maxHTMLBarWidth = 350.0
func ( *histogram) () *data {
.allocateBuckets()
:= int64(0)
for , := range .buckets {
if > {
=
}
}
:= .total()
:= maxHTMLBarWidth / float64()
var float64
if == 0 {
= 1.0
} else {
= 100.0 / float64()
}
:= make([]*bucketData, len(.buckets))
:= int64(0)
for , := range .buckets {
if == 0 {
continue
}
+=
var int64
if < bucketCount-1 {
= bucketBoundary(uint8( + 1))
} else {
= math.MaxInt64
}
[] = &bucketData{
Lower: bucketBoundary(uint8()),
Upper: ,
N: ,
Pct: float64() * ,
CumulativePct: float64() * ,
GraphWidth: int(float64() * ),
}
}
return &data{
Buckets: ,
Count: ,
Median: .median(),
Mean: .average(),
StandardDeviation: .standardDeviation(),
}
}
func ( *histogram) () template.HTML {
:= new(bytes.Buffer)
if := distTmpl().Execute(, .newData()); != nil {
.Reset()
log.Printf("net/trace: couldn't execute template: %v", )
}
return template.HTML(.String())
}
var distTmplCache *template.Template
var distTmplOnce sync.Once
func () *template.Template {
distTmplOnce.Do(func() {
distTmplCache = template.Must(template.New("distTmpl").Parse(`
<table>
<tr>
<td style="padding:0.25em">Count: {{.Count}}</td>
<td style="padding:0.25em">Mean: {{printf "%.0f" .Mean}}</td>
<td style="padding:0.25em">StdDev: {{printf "%.0f" .StandardDeviation}}</td>
<td style="padding:0.25em">Median: {{.Median}}</td>
</tr>
</table>
<hr>
<table>
{{range $b := .Buckets}}
{{if $b}}
<tr>
<td style="padding:0 0 0 0.25em">[</td>
<td style="text-align:right;padding:0 0.25em">{{.Lower}},</td>
<td style="text-align:right;padding:0 0.25em">{{.Upper}})</td>
<td style="text-align:right;padding:0 0.25em">{{.N}}</td>
<td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .Pct}}%</td>
<td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .CumulativePct}}%</td>
<td><div style="background-color: blue; height: 1em; width: {{.GraphWidth}};"></div></td>
</tr>
{{end}}
{{end}}
</table>
`))
})
return distTmplCache
}