mirror of
https://github.com/weaveworks/scope.git
synced 2026-05-06 01:08:03 +00:00
``` $ gvt delete github.com/weaveworks/common $ gvt fetch --revision 4d96fd8dcf2c7b417912c6219b310112cb4a4626 github.com/weaveworks/common 2018/07/23 15:31:11 Fetching: github.com/weaveworks/common 2018/07/23 15:31:14 · Skipping (existing): github.com/golang/protobuf/ptypes/any 2018/07/23 15:31:14 · Fetching recursive dependency: github.com/pkg/errors 2018/07/23 15:31:16 · Skipping (existing): github.com/aws/aws-sdk-go/aws 2018/07/23 15:31:16 · Fetching recursive dependency: github.com/sirupsen/logrus 2018/07/23 15:31:18 ·· Skipping (existing): golang.org/x/sys/unix 2018/07/23 15:31:18 ·· Skipping (existing): golang.org/x/crypto/ssh/terminal 2018/07/23 15:31:18 · Skipping (existing): google.golang.org/grpc/status 2018/07/23 15:31:18 · Skipping (existing): github.com/gorilla/mux 2018/07/23 15:31:18 · Fetching recursive dependency: github.com/opentracing-contrib/go-stdlib/nethttp 2018/07/23 15:31:20 ·· Skipping (existing): github.com/opentracing/opentracing-go/ext 2018/07/23 15:31:20 ·· Skipping (existing): github.com/opentracing/opentracing-go/log 2018/07/23 15:31:20 ·· Skipping (existing): github.com/opentracing/opentracing-go 2018/07/23 15:31:20 · Skipping (existing): github.com/prometheus/client_golang/prometheus 2018/07/23 15:31:20 · Skipping (existing): google.golang.org/grpc 2018/07/23 15:31:20 · Skipping (existing): github.com/pmezard/go-difflib/difflib 2018/07/23 15:31:20 · Fetching recursive dependency: github.com/go-kit/kit/log 2018/07/23 15:31:23 ·· Fetching recursive dependency: github.com/go-logfmt/logfmt 2018/07/23 15:31:25 ··· Fetching recursive dependency: github.com/kr/logfmt 2018/07/23 15:31:27 ·· Fetching recursive dependency: github.com/go-stack/stack 2018/07/23 15:31:29 · Fetching recursive dependency: google.golang.org/genproto/googleapis/rpc/status 2018/07/23 15:31:37 ·· Skipping (existing): github.com/golang/protobuf/proto 2018/07/23 15:31:37 ·· Skipping (existing): github.com/golang/protobuf/ptypes/any 2018/07/23 15:31:37 · Skipping (existing): github.com/opentracing/opentracing-go/log 2018/07/23 15:31:37 · Fetching recursive dependency: github.com/sercand/kuberesolver 2018/07/23 15:31:39 ·· Skipping (existing): google.golang.org/grpc/grpclog 2018/07/23 15:31:39 ·· Skipping (existing): google.golang.org/grpc/resolver 2018/07/23 15:31:39 ·· Skipping (existing): golang.org/x/net/context 2018/07/23 15:31:39 · Skipping (existing): google.golang.org/grpc/metadata 2018/07/23 15:31:39 · Skipping (existing): github.com/opentracing/opentracing-go/ext 2018/07/23 15:31:39 · Skipping (existing): github.com/armon/go-socks5 2018/07/23 15:31:39 · Skipping (existing): github.com/opentracing/opentracing-go 2018/07/23 15:31:39 · Skipping (existing): github.com/davecgh/go-spew/spew 2018/07/23 15:31:39 · Skipping (existing): github.com/golang/protobuf/ptypes 2018/07/23 15:31:39 · Skipping (existing): github.com/golang/protobuf/proto 2018/07/23 15:31:39 · Fetching recursive dependency: github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc 2018/07/23 15:31:41 ·· Skipping (existing): github.com/opentracing/opentracing-go/log 2018/07/23 15:31:41 ·· Skipping (existing): golang.org/x/net/context 2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc/codes 2018/07/23 15:31:41 ·· Skipping (existing): github.com/golang/protobuf/proto 2018/07/23 15:31:41 ·· Skipping (existing): github.com/opentracing/opentracing-go 2018/07/23 15:31:41 ·· Skipping (existing): github.com/opentracing/opentracing-go/ext 2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc 2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc/metadata 2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc/status 2018/07/23 15:31:41 · Fetching recursive dependency: github.com/uber/jaeger-client-go/config 2018/07/23 15:31:44 ·· Fetching recursive dependency: github.com/uber/jaeger-client-go/internal/throttler/remote 2018/07/23 15:31:44 ··· Fetching recursive dependency: github.com/uber/jaeger-client-go/utils 2018/07/23 15:31:44 ···· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift 2018/07/23 15:31:44 ···· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift-gen/agent 2018/07/23 15:31:44 ····· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift-gen/jaeger 2018/07/23 15:31:44 ····· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift-gen/zipkincore 2018/07/23 15:31:44 ··· Fetching recursive dependency: github.com/uber/jaeger-client-go 2018/07/23 15:31:44 ···· Fetching recursive dependency: github.com/crossdock/crossdock-go 2018/07/23 15:31:46 ····· Skipping (existing): github.com/davecgh/go-spew/spew 2018/07/23 15:31:46 ····· Skipping (existing): golang.org/x/net/context/ctxhttp 2018/07/23 15:31:46 ····· Skipping (existing): golang.org/x/net/context 2018/07/23 15:31:46 ····· Skipping (existing): github.com/pmezard/go-difflib/difflib 2018/07/23 15:31:46 ···· Skipping (existing): github.com/opentracing/opentracing-go/log 2018/07/23 15:31:46 ···· Fetching recursive dependency: go.uber.org/zap/zapcore 2018/07/23 15:31:49 ····· Fetching recursive dependency: go.uber.org/atomic 2018/07/23 15:31:51 ····· Fetching recursive dependency: go.uber.org/zap/internal/bufferpool 2018/07/23 15:31:51 ······ Fetching recursive dependency: go.uber.org/zap/buffer 2018/07/23 15:31:51 ····· Fetching recursive dependency: go.uber.org/multierr 2018/07/23 15:31:54 ····· Fetching recursive dependency: go.uber.org/zap/internal/exit 2018/07/23 15:31:54 ····· Fetching recursive dependency: go.uber.org/zap/internal/color 2018/07/23 15:31:54 ···· Fetching recursive dependency: go.uber.org/zap 2018/07/23 15:31:54 ···· Skipping (existing): github.com/opentracing/opentracing-go 2018/07/23 15:31:54 ···· Skipping (existing): github.com/opentracing/opentracing-go/ext 2018/07/23 15:31:54 ···· Fetching recursive dependency: github.com/uber/jaeger-lib/metrics 2018/07/23 15:31:56 ····· Fetching recursive dependency: github.com/uber-go/tally 2018/07/23 15:31:58 ······ Fetching recursive dependency: github.com/m3db/prometheus_client_golang/prometheus/promhttp 2018/07/23 15:32:00 ······· Skipping (existing): github.com/prometheus/client_golang/prometheus 2018/07/23 15:32:00 ······· Skipping (existing): github.com/prometheus/common/expfmt 2018/07/23 15:32:00 ······· Skipping (existing): github.com/prometheus/client_model/go 2018/07/23 15:32:00 ······ Fetching recursive dependency: gopkg.in/validator.v2 2018/07/23 15:32:06 ······ Fetching recursive dependency: github.com/cactus/go-statsd-client/statsd 2018/07/23 15:32:08 ······ Skipping (existing): gopkg.in/yaml.v2 2018/07/23 15:32:08 ······ Fetching recursive dependency: github.com/m3db/prometheus_client_golang/prometheus 2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/procfs 2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/client_model/go 2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/common/expfmt 2018/07/23 15:32:08 ······· Skipping (existing): golang.org/x/net/context 2018/07/23 15:32:08 ······· Skipping (existing): github.com/beorn7/perks/quantile 2018/07/23 15:32:08 ······· Skipping (existing): github.com/golang/protobuf/proto 2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/common/model 2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/client_golang/prometheus 2018/07/23 15:32:08 ······ Fetching recursive dependency: github.com/apache/thrift/lib/go/thrift 2018/07/23 15:32:13 ····· Skipping (existing): github.com/stretchr/testify/assert 2018/07/23 15:32:13 ····· Fetching recursive dependency: github.com/go-kit/kit/metrics/influx 2018/07/23 15:32:13 ······ Fetching recursive dependency: github.com/influxdata/influxdb/client/v2 2018/07/23 15:32:17 ······· Fetching recursive dependency: github.com/influxdata/influxdb/models 2018/07/23 15:32:17 ········ Fetching recursive dependency: github.com/influxdata/influxdb/pkg/escape 2018/07/23 15:32:17 ······ Fetching recursive dependency: github.com/go-kit/kit/metrics 2018/07/23 15:32:17 ······· Fetching recursive dependency: github.com/performancecopilot/speed 2018/07/23 15:32:19 ······· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/aws 2018/07/23 15:32:28 ········ Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/internal/sdk 2018/07/23 15:32:28 ········ Skipping (existing): github.com/go-ini/ini 2018/07/23 15:32:28 ········ Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/service/sts 2018/07/23 15:32:28 ········· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/private/protocol/query 2018/07/23 15:32:28 ·········· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/private/protocol 2018/07/23 15:32:28 ········· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/internal/awsutil 2018/07/23 15:32:28 ·········· Skipping (existing): github.com/jmespath/go-jmespath 2018/07/23 15:32:28 ······· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/service/cloudwatch 2018/07/23 15:32:29 ······· Skipping (existing): github.com/aws/aws-sdk-go/aws 2018/07/23 15:32:29 ······· Skipping (existing): github.com/prometheus/client_golang/prometheus 2018/07/23 15:32:29 ······· Skipping (existing): github.com/aws/aws-sdk-go/service/cloudwatch 2018/07/23 15:32:29 ······· Skipping (existing): github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface 2018/07/23 15:32:29 ······· Fetching recursive dependency: golang.org/x/sync/errgroup 2018/07/23 15:32:31 ········ Skipping (existing): golang.org/x/net/context 2018/07/23 15:32:31 ······· Fetching recursive dependency: github.com/go-kit/kit/util/conn 2018/07/23 15:32:31 ······· Fetching recursive dependency: github.com/VividCortex/gohistogram 2018/07/23 15:32:33 ····· Skipping (existing): github.com/prometheus/client_golang/prometheus 2018/07/23 15:32:33 ····· Fetching recursive dependency: github.com/codahale/hdrhistogram 2018/07/23 15:32:35 ·· Skipping (existing): github.com/opentracing/opentracing-go 2018/07/23 15:32:35 · Fetching recursive dependency: github.com/mwitkow/go-grpc-middleware 2018/07/23 15:32:37 ·· Fetching recursive dependency: github.com/grpc-ecosystem/go-grpc-middleware/logging 2018/07/23 15:32:39 ··· Fetching recursive dependency: github.com/grpc-ecosystem/go-grpc-middleware 2018/07/23 15:32:39 ···· Fetching recursive dependency: github.com/golang/protobuf/jsonpb 2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/timestamp 2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/proto 2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/duration 2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/any 2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/struct 2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/wrappers 2018/07/23 15:32:42 ···· Skipping (existing): google.golang.org/grpc/metadata 2018/07/23 15:32:42 ···· Fetching recursive dependency: github.com/stretchr/testify/suite 2018/07/23 15:32:45 ····· Skipping (existing): github.com/stretchr/testify/assert 2018/07/23 15:32:45 ····· Fetching recursive dependency: github.com/stretchr/testify/require 2018/07/23 15:32:45 ······ Skipping (existing): github.com/stretchr/testify/assert 2018/07/23 15:32:45 ···· Skipping (existing): google.golang.org/grpc/peer 2018/07/23 15:32:45 ···· Skipping (existing): golang.org/x/net/context 2018/07/23 15:32:45 ···· Skipping (existing): golang.org/x/net/trace 2018/07/23 15:32:45 ···· Fetching recursive dependency: github.com/gogo/protobuf/gogoproto 2018/07/23 15:32:48 ····· Fetching recursive dependency: github.com/gogo/protobuf/protoc-gen-gogo/descriptor 2018/07/23 15:32:48 ······ Skipping (existing): github.com/gogo/protobuf/proto 2018/07/23 15:32:48 ····· Skipping (existing): github.com/gogo/protobuf/proto 2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc/credentials 2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc 2018/07/23 15:32:48 ···· Skipping (existing): github.com/opentracing/opentracing-go 2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc/codes 2018/07/23 15:32:48 ···· Skipping (existing): github.com/golang/protobuf/proto 2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc/grpclog 2018/07/23 15:32:48 ···· Skipping (existing): github.com/opentracing/opentracing-go/ext 2018/07/23 15:32:48 ···· Skipping (existing): github.com/opentracing/opentracing-go/log 2018/07/23 15:32:48 ··· Skipping (existing): golang.org/x/net/context 2018/07/23 15:32:48 ··· Skipping (existing): google.golang.org/grpc 2018/07/23 15:32:48 ··· Skipping (existing): google.golang.org/grpc/grpclog 2018/07/23 15:32:48 ··· Skipping (existing): google.golang.org/grpc/codes 2018/07/23 15:32:48 ··· Skipping (existing): github.com/golang/protobuf/proto 2018/07/23 15:32:48 ·· Skipping (existing): github.com/opentracing/opentracing-go 2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc 2018/07/23 15:32:48 ·· Skipping (existing): golang.org/x/net/context 2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/codes 2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/grpclog 2018/07/23 15:32:48 ·· Skipping (existing): github.com/opentracing/opentracing-go/log 2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/metadata 2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/peer 2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/credentials 2018/07/23 15:32:48 ·· Skipping (existing): github.com/golang/protobuf/proto 2018/07/23 15:32:48 ·· Skipping (existing): golang.org/x/net/trace 2018/07/23 15:32:48 ·· Skipping (existing): github.com/opentracing/opentracing-go/ext 2018/07/23 15:32:48 · Fetching recursive dependency: github.com/weaveworks/promrus 2018/07/23 15:32:53 ·· Skipping (existing): gopkg.in/yaml.v2 2018/07/23 15:32:53 ·· Skipping (existing): golang.org/x/net/context/ctxhttp 2018/07/23 15:32:53 ·· Fetching recursive dependency: github.com/stretchr/objx 2018/07/23 15:32:55 ·· Fetching recursive dependency: gopkg.in/alecthomas/kingpin.v2 2018/07/23 15:32:58 ··· Fetching recursive dependency: github.com/alecthomas/units 2018/07/23 15:33:00 ··· Fetching recursive dependency: github.com/alecthomas/template 2018/07/23 15:33:02 ·· Fetching recursive dependency: github.com/julienschmidt/httprouter 2018/07/23 15:33:05 ·· Skipping (existing): golang.org/x/net/context 2018/07/23 15:33:05 · Skipping (existing): github.com/aws/aws-sdk-go/aws/credentials 2018/07/23 15:33:05 · Skipping (existing): github.com/golang/protobuf/ptypes/empty 2018/07/23 15:33:05 · Skipping (existing): golang.org/x/net/context 2018/07/23 15:33:05 · Skipping (existing): golang.org/x/tools/cover 2018/07/23 15:33:05 · Skipping (existing): github.com/mgutz/ansi ```
565 lines
14 KiB
Go
565 lines
14 KiB
Go
// Package hdrhistogram provides an implementation of Gil Tene's HDR Histogram
|
|
// data structure. The HDR Histogram allows for fast and accurate analysis of
|
|
// the extreme ranges of data with non-normal distributions, like latency.
|
|
package hdrhistogram
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
// A Bracket is a part of a cumulative distribution.
|
|
type Bracket struct {
|
|
Quantile float64
|
|
Count, ValueAt int64
|
|
}
|
|
|
|
// A Snapshot is an exported view of a Histogram, useful for serializing them.
|
|
// A Histogram can be constructed from it by passing it to Import.
|
|
type Snapshot struct {
|
|
LowestTrackableValue int64
|
|
HighestTrackableValue int64
|
|
SignificantFigures int64
|
|
Counts []int64
|
|
}
|
|
|
|
// A Histogram is a lossy data structure used to record the distribution of
|
|
// non-normally distributed data (like latency) with a high degree of accuracy
|
|
// and a bounded degree of precision.
|
|
type Histogram struct {
|
|
lowestTrackableValue int64
|
|
highestTrackableValue int64
|
|
unitMagnitude int64
|
|
significantFigures int64
|
|
subBucketHalfCountMagnitude int32
|
|
subBucketHalfCount int32
|
|
subBucketMask int64
|
|
subBucketCount int32
|
|
bucketCount int32
|
|
countsLen int32
|
|
totalCount int64
|
|
counts []int64
|
|
}
|
|
|
|
// New returns a new Histogram instance capable of tracking values in the given
|
|
// range and with the given amount of precision.
|
|
func New(minValue, maxValue int64, sigfigs int) *Histogram {
|
|
if sigfigs < 1 || 5 < sigfigs {
|
|
panic(fmt.Errorf("sigfigs must be [1,5] (was %d)", sigfigs))
|
|
}
|
|
|
|
largestValueWithSingleUnitResolution := 2 * math.Pow10(sigfigs)
|
|
subBucketCountMagnitude := int32(math.Ceil(math.Log2(float64(largestValueWithSingleUnitResolution))))
|
|
|
|
subBucketHalfCountMagnitude := subBucketCountMagnitude
|
|
if subBucketHalfCountMagnitude < 1 {
|
|
subBucketHalfCountMagnitude = 1
|
|
}
|
|
subBucketHalfCountMagnitude--
|
|
|
|
unitMagnitude := int32(math.Floor(math.Log2(float64(minValue))))
|
|
if unitMagnitude < 0 {
|
|
unitMagnitude = 0
|
|
}
|
|
|
|
subBucketCount := int32(math.Pow(2, float64(subBucketHalfCountMagnitude)+1))
|
|
|
|
subBucketHalfCount := subBucketCount / 2
|
|
subBucketMask := int64(subBucketCount-1) << uint(unitMagnitude)
|
|
|
|
// determine exponent range needed to support the trackable value with no
|
|
// overflow:
|
|
smallestUntrackableValue := int64(subBucketCount) << uint(unitMagnitude)
|
|
bucketsNeeded := int32(1)
|
|
for smallestUntrackableValue < maxValue {
|
|
smallestUntrackableValue <<= 1
|
|
bucketsNeeded++
|
|
}
|
|
|
|
bucketCount := bucketsNeeded
|
|
countsLen := (bucketCount + 1) * (subBucketCount / 2)
|
|
|
|
return &Histogram{
|
|
lowestTrackableValue: minValue,
|
|
highestTrackableValue: maxValue,
|
|
unitMagnitude: int64(unitMagnitude),
|
|
significantFigures: int64(sigfigs),
|
|
subBucketHalfCountMagnitude: subBucketHalfCountMagnitude,
|
|
subBucketHalfCount: subBucketHalfCount,
|
|
subBucketMask: subBucketMask,
|
|
subBucketCount: subBucketCount,
|
|
bucketCount: bucketCount,
|
|
countsLen: countsLen,
|
|
totalCount: 0,
|
|
counts: make([]int64, countsLen),
|
|
}
|
|
}
|
|
|
|
// ByteSize returns an estimate of the amount of memory allocated to the
|
|
// histogram in bytes.
|
|
//
|
|
// N.B.: This does not take into account the overhead for slices, which are
|
|
// small, constant, and specific to the compiler version.
|
|
func (h *Histogram) ByteSize() int {
|
|
return 6*8 + 5*4 + len(h.counts)*8
|
|
}
|
|
|
|
// Merge merges the data stored in the given histogram with the receiver,
|
|
// returning the number of recorded values which had to be dropped.
|
|
func (h *Histogram) Merge(from *Histogram) (dropped int64) {
|
|
i := from.rIterator()
|
|
for i.next() {
|
|
v := i.valueFromIdx
|
|
c := i.countAtIdx
|
|
|
|
if h.RecordValues(v, c) != nil {
|
|
dropped += c
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// TotalCount returns total number of values recorded.
|
|
func (h *Histogram) TotalCount() int64 {
|
|
return h.totalCount
|
|
}
|
|
|
|
// Max returns the approximate maximum recorded value.
|
|
func (h *Histogram) Max() int64 {
|
|
var max int64
|
|
i := h.iterator()
|
|
for i.next() {
|
|
if i.countAtIdx != 0 {
|
|
max = i.highestEquivalentValue
|
|
}
|
|
}
|
|
return h.highestEquivalentValue(max)
|
|
}
|
|
|
|
// Min returns the approximate minimum recorded value.
|
|
func (h *Histogram) Min() int64 {
|
|
var min int64
|
|
i := h.iterator()
|
|
for i.next() {
|
|
if i.countAtIdx != 0 && min == 0 {
|
|
min = i.highestEquivalentValue
|
|
break
|
|
}
|
|
}
|
|
return h.lowestEquivalentValue(min)
|
|
}
|
|
|
|
// Mean returns the approximate arithmetic mean of the recorded values.
|
|
func (h *Histogram) Mean() float64 {
|
|
if h.totalCount == 0 {
|
|
return 0
|
|
}
|
|
var total int64
|
|
i := h.iterator()
|
|
for i.next() {
|
|
if i.countAtIdx != 0 {
|
|
total += i.countAtIdx * h.medianEquivalentValue(i.valueFromIdx)
|
|
}
|
|
}
|
|
return float64(total) / float64(h.totalCount)
|
|
}
|
|
|
|
// StdDev returns the approximate standard deviation of the recorded values.
|
|
func (h *Histogram) StdDev() float64 {
|
|
if h.totalCount == 0 {
|
|
return 0
|
|
}
|
|
|
|
mean := h.Mean()
|
|
geometricDevTotal := 0.0
|
|
|
|
i := h.iterator()
|
|
for i.next() {
|
|
if i.countAtIdx != 0 {
|
|
dev := float64(h.medianEquivalentValue(i.valueFromIdx)) - mean
|
|
geometricDevTotal += (dev * dev) * float64(i.countAtIdx)
|
|
}
|
|
}
|
|
|
|
return math.Sqrt(geometricDevTotal / float64(h.totalCount))
|
|
}
|
|
|
|
// Reset deletes all recorded values and restores the histogram to its original
|
|
// state.
|
|
func (h *Histogram) Reset() {
|
|
h.totalCount = 0
|
|
for i := range h.counts {
|
|
h.counts[i] = 0
|
|
}
|
|
}
|
|
|
|
// RecordValue records the given value, returning an error if the value is out
|
|
// of range.
|
|
func (h *Histogram) RecordValue(v int64) error {
|
|
return h.RecordValues(v, 1)
|
|
}
|
|
|
|
// RecordCorrectedValue records the given value, correcting for stalls in the
|
|
// recording process. This only works for processes which are recording values
|
|
// at an expected interval (e.g., doing jitter analysis). Processes which are
|
|
// recording ad-hoc values (e.g., latency for incoming requests) can't take
|
|
// advantage of this.
|
|
func (h *Histogram) RecordCorrectedValue(v, expectedInterval int64) error {
|
|
if err := h.RecordValue(v); err != nil {
|
|
return err
|
|
}
|
|
|
|
if expectedInterval <= 0 || v <= expectedInterval {
|
|
return nil
|
|
}
|
|
|
|
missingValue := v - expectedInterval
|
|
for missingValue >= expectedInterval {
|
|
if err := h.RecordValue(missingValue); err != nil {
|
|
return err
|
|
}
|
|
missingValue -= expectedInterval
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RecordValues records n occurrences of the given value, returning an error if
|
|
// the value is out of range.
|
|
func (h *Histogram) RecordValues(v, n int64) error {
|
|
idx := h.countsIndexFor(v)
|
|
if idx < 0 || int(h.countsLen) <= idx {
|
|
return fmt.Errorf("value %d is too large to be recorded", v)
|
|
}
|
|
h.counts[idx] += n
|
|
h.totalCount += n
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValueAtQuantile returns the recorded value at the given quantile (0..100).
|
|
func (h *Histogram) ValueAtQuantile(q float64) int64 {
|
|
if q > 100 {
|
|
q = 100
|
|
}
|
|
|
|
total := int64(0)
|
|
countAtPercentile := int64(((q / 100) * float64(h.totalCount)) + 0.5)
|
|
|
|
i := h.iterator()
|
|
for i.next() {
|
|
total += i.countAtIdx
|
|
if total >= countAtPercentile {
|
|
return h.highestEquivalentValue(i.valueFromIdx)
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// CumulativeDistribution returns an ordered list of brackets of the
|
|
// distribution of recorded values.
|
|
func (h *Histogram) CumulativeDistribution() []Bracket {
|
|
var result []Bracket
|
|
|
|
i := h.pIterator(1)
|
|
for i.next() {
|
|
result = append(result, Bracket{
|
|
Quantile: i.percentile,
|
|
Count: i.countToIdx,
|
|
ValueAt: i.highestEquivalentValue,
|
|
})
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// SignificantFigures returns the significant figures used to create the
|
|
// histogram
|
|
func (h *Histogram) SignificantFigures() int64 {
|
|
return h.significantFigures
|
|
}
|
|
|
|
// LowestTrackableValue returns the lower bound on values that will be added
|
|
// to the histogram
|
|
func (h *Histogram) LowestTrackableValue() int64 {
|
|
return h.lowestTrackableValue
|
|
}
|
|
|
|
// HighestTrackableValue returns the upper bound on values that will be added
|
|
// to the histogram
|
|
func (h *Histogram) HighestTrackableValue() int64 {
|
|
return h.highestTrackableValue
|
|
}
|
|
|
|
// Histogram bar for plotting
|
|
type Bar struct {
|
|
From, To, Count int64
|
|
}
|
|
|
|
// Pretty print as csv for easy plotting
|
|
func (b Bar) String() string {
|
|
return fmt.Sprintf("%v, %v, %v\n", b.From, b.To, b.Count)
|
|
}
|
|
|
|
// Distribution returns an ordered list of bars of the
|
|
// distribution of recorded values, counts can be normalized to a probability
|
|
func (h *Histogram) Distribution() (result []Bar) {
|
|
i := h.iterator()
|
|
for i.next() {
|
|
result = append(result, Bar{
|
|
Count: i.countAtIdx,
|
|
From: h.lowestEquivalentValue(i.valueFromIdx),
|
|
To: i.highestEquivalentValue,
|
|
})
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// Equals returns true if the two Histograms are equivalent, false if not.
|
|
func (h *Histogram) Equals(other *Histogram) bool {
|
|
switch {
|
|
case
|
|
h.lowestTrackableValue != other.lowestTrackableValue,
|
|
h.highestTrackableValue != other.highestTrackableValue,
|
|
h.unitMagnitude != other.unitMagnitude,
|
|
h.significantFigures != other.significantFigures,
|
|
h.subBucketHalfCountMagnitude != other.subBucketHalfCountMagnitude,
|
|
h.subBucketHalfCount != other.subBucketHalfCount,
|
|
h.subBucketMask != other.subBucketMask,
|
|
h.subBucketCount != other.subBucketCount,
|
|
h.bucketCount != other.bucketCount,
|
|
h.countsLen != other.countsLen,
|
|
h.totalCount != other.totalCount:
|
|
return false
|
|
default:
|
|
for i, c := range h.counts {
|
|
if c != other.counts[i] {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Export returns a snapshot view of the Histogram. This can be later passed to
|
|
// Import to construct a new Histogram with the same state.
|
|
func (h *Histogram) Export() *Snapshot {
|
|
return &Snapshot{
|
|
LowestTrackableValue: h.lowestTrackableValue,
|
|
HighestTrackableValue: h.highestTrackableValue,
|
|
SignificantFigures: h.significantFigures,
|
|
Counts: append([]int64(nil), h.counts...), // copy
|
|
}
|
|
}
|
|
|
|
// Import returns a new Histogram populated from the Snapshot data (which the
|
|
// caller must stop accessing).
|
|
func Import(s *Snapshot) *Histogram {
|
|
h := New(s.LowestTrackableValue, s.HighestTrackableValue, int(s.SignificantFigures))
|
|
h.counts = s.Counts
|
|
totalCount := int64(0)
|
|
for i := int32(0); i < h.countsLen; i++ {
|
|
countAtIndex := h.counts[i]
|
|
if countAtIndex > 0 {
|
|
totalCount += countAtIndex
|
|
}
|
|
}
|
|
h.totalCount = totalCount
|
|
return h
|
|
}
|
|
|
|
func (h *Histogram) iterator() *iterator {
|
|
return &iterator{
|
|
h: h,
|
|
subBucketIdx: -1,
|
|
}
|
|
}
|
|
|
|
func (h *Histogram) rIterator() *rIterator {
|
|
return &rIterator{
|
|
iterator: iterator{
|
|
h: h,
|
|
subBucketIdx: -1,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (h *Histogram) pIterator(ticksPerHalfDistance int32) *pIterator {
|
|
return &pIterator{
|
|
iterator: iterator{
|
|
h: h,
|
|
subBucketIdx: -1,
|
|
},
|
|
ticksPerHalfDistance: ticksPerHalfDistance,
|
|
}
|
|
}
|
|
|
|
func (h *Histogram) sizeOfEquivalentValueRange(v int64) int64 {
|
|
bucketIdx := h.getBucketIndex(v)
|
|
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
|
|
adjustedBucket := bucketIdx
|
|
if subBucketIdx >= h.subBucketCount {
|
|
adjustedBucket++
|
|
}
|
|
return int64(1) << uint(h.unitMagnitude+int64(adjustedBucket))
|
|
}
|
|
|
|
func (h *Histogram) valueFromIndex(bucketIdx, subBucketIdx int32) int64 {
|
|
return int64(subBucketIdx) << uint(int64(bucketIdx)+h.unitMagnitude)
|
|
}
|
|
|
|
func (h *Histogram) lowestEquivalentValue(v int64) int64 {
|
|
bucketIdx := h.getBucketIndex(v)
|
|
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
|
|
return h.valueFromIndex(bucketIdx, subBucketIdx)
|
|
}
|
|
|
|
func (h *Histogram) nextNonEquivalentValue(v int64) int64 {
|
|
return h.lowestEquivalentValue(v) + h.sizeOfEquivalentValueRange(v)
|
|
}
|
|
|
|
func (h *Histogram) highestEquivalentValue(v int64) int64 {
|
|
return h.nextNonEquivalentValue(v) - 1
|
|
}
|
|
|
|
func (h *Histogram) medianEquivalentValue(v int64) int64 {
|
|
return h.lowestEquivalentValue(v) + (h.sizeOfEquivalentValueRange(v) >> 1)
|
|
}
|
|
|
|
func (h *Histogram) getCountAtIndex(bucketIdx, subBucketIdx int32) int64 {
|
|
return h.counts[h.countsIndex(bucketIdx, subBucketIdx)]
|
|
}
|
|
|
|
func (h *Histogram) countsIndex(bucketIdx, subBucketIdx int32) int32 {
|
|
bucketBaseIdx := (bucketIdx + 1) << uint(h.subBucketHalfCountMagnitude)
|
|
offsetInBucket := subBucketIdx - h.subBucketHalfCount
|
|
return bucketBaseIdx + offsetInBucket
|
|
}
|
|
|
|
func (h *Histogram) getBucketIndex(v int64) int32 {
|
|
pow2Ceiling := bitLen(v | h.subBucketMask)
|
|
return int32(pow2Ceiling - int64(h.unitMagnitude) -
|
|
int64(h.subBucketHalfCountMagnitude+1))
|
|
}
|
|
|
|
func (h *Histogram) getSubBucketIdx(v int64, idx int32) int32 {
|
|
return int32(v >> uint(int64(idx)+int64(h.unitMagnitude)))
|
|
}
|
|
|
|
func (h *Histogram) countsIndexFor(v int64) int {
|
|
bucketIdx := h.getBucketIndex(v)
|
|
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
|
|
return int(h.countsIndex(bucketIdx, subBucketIdx))
|
|
}
|
|
|
|
type iterator struct {
|
|
h *Histogram
|
|
bucketIdx, subBucketIdx int32
|
|
countAtIdx, countToIdx, valueFromIdx int64
|
|
highestEquivalentValue int64
|
|
}
|
|
|
|
func (i *iterator) next() bool {
|
|
if i.countToIdx >= i.h.totalCount {
|
|
return false
|
|
}
|
|
|
|
// increment bucket
|
|
i.subBucketIdx++
|
|
if i.subBucketIdx >= i.h.subBucketCount {
|
|
i.subBucketIdx = i.h.subBucketHalfCount
|
|
i.bucketIdx++
|
|
}
|
|
|
|
if i.bucketIdx >= i.h.bucketCount {
|
|
return false
|
|
}
|
|
|
|
i.countAtIdx = i.h.getCountAtIndex(i.bucketIdx, i.subBucketIdx)
|
|
i.countToIdx += i.countAtIdx
|
|
i.valueFromIdx = i.h.valueFromIndex(i.bucketIdx, i.subBucketIdx)
|
|
i.highestEquivalentValue = i.h.highestEquivalentValue(i.valueFromIdx)
|
|
|
|
return true
|
|
}
|
|
|
|
type rIterator struct {
|
|
iterator
|
|
countAddedThisStep int64
|
|
}
|
|
|
|
func (r *rIterator) next() bool {
|
|
for r.iterator.next() {
|
|
if r.countAtIdx != 0 {
|
|
r.countAddedThisStep = r.countAtIdx
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type pIterator struct {
|
|
iterator
|
|
seenLastValue bool
|
|
ticksPerHalfDistance int32
|
|
percentileToIteratorTo float64
|
|
percentile float64
|
|
}
|
|
|
|
func (p *pIterator) next() bool {
|
|
if !(p.countToIdx < p.h.totalCount) {
|
|
if p.seenLastValue {
|
|
return false
|
|
}
|
|
|
|
p.seenLastValue = true
|
|
p.percentile = 100
|
|
|
|
return true
|
|
}
|
|
|
|
if p.subBucketIdx == -1 && !p.iterator.next() {
|
|
return false
|
|
}
|
|
|
|
var done = false
|
|
for !done {
|
|
currentPercentile := (100.0 * float64(p.countToIdx)) / float64(p.h.totalCount)
|
|
if p.countAtIdx != 0 && p.percentileToIteratorTo <= currentPercentile {
|
|
p.percentile = p.percentileToIteratorTo
|
|
halfDistance := math.Trunc(math.Pow(2, math.Trunc(math.Log2(100.0/(100.0-p.percentileToIteratorTo)))+1))
|
|
percentileReportingTicks := float64(p.ticksPerHalfDistance) * halfDistance
|
|
p.percentileToIteratorTo += 100.0 / percentileReportingTicks
|
|
return true
|
|
}
|
|
done = !p.iterator.next()
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func bitLen(x int64) (n int64) {
|
|
for ; x >= 0x8000; x >>= 16 {
|
|
n += 16
|
|
}
|
|
if x >= 0x80 {
|
|
x >>= 8
|
|
n += 8
|
|
}
|
|
if x >= 0x8 {
|
|
x >>= 4
|
|
n += 4
|
|
}
|
|
if x >= 0x2 {
|
|
x >>= 2
|
|
n += 2
|
|
}
|
|
if x >= 0x1 {
|
|
n++
|
|
}
|
|
return
|
|
}
|