Skip to content

Commit 35c7773

Browse files
Merge pull request #45 from HdrHistogram/optimizations.for.percentile
Optimized nextNonEquivalentValue(), and consequently ValueAtPercentile, ValueAtPercentiles...
2 parents c968ba7 + 6577d04 commit 35c7773

File tree

9 files changed

+347
-100
lines changed

9 files changed

+347
-100
lines changed

.github/workflows/unit-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ jobs:
44
test:
55
strategy:
66
matrix:
7-
go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x]
7+
go-version: [1.14.x, 1.15.x]
88
os: [ubuntu-latest, macos-latest, windows-latest]
99
runs-on: ${{ matrix.os }}
1010
steps:

Makefile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ GOFMT=$(GOCMD) fmt
1010
GODOC=godoc
1111

1212
.PHONY: all test coverage
13-
all: test coverage
13+
all: test
1414

1515
checkfmt:
1616
@echo 'Checking gofmt';\
@@ -26,7 +26,7 @@ lint:
2626
golangci-lint run
2727

2828
get:
29-
$(GOGET) -t -v ./...
29+
$(GOGET) -v ./...
3030

3131
fmt:
3232
$(GOFMT) ./...
@@ -35,7 +35,10 @@ test: get fmt
3535
$(GOTEST) -count=1 ./...
3636

3737
coverage: get test
38-
$(GOTEST) -race -coverprofile=coverage.txt -covermode=atomic .
38+
$(GOTEST) -count=1 -race -coverprofile=coverage.txt -covermode=atomic .
39+
40+
benchmark: get
41+
$(GOTEST) -bench=. -benchmem
3942

4043
godoc:
4144
$(GODOC)

example_hdr_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,30 @@ func ExampleHistogram_PercentilesPrint() {
8585
// #[Max = 12722175.000, Total count = 10]
8686
// #[Buckets = 15, SubBuckets = 2048]
8787
}
88+
89+
// When doing an percentile analysis we normally require more than one percentile to be calculated for the given histogram.
90+
//
91+
// When that is the case ValueAtPercentiles() will deeply optimize the total time to retrieve the percentiles vs the other option
92+
// which is multiple calls to ValueAtQuantile().
93+
//
94+
// nolint
95+
func ExampleHistogram_ValueAtPercentiles() {
96+
histogram := hdrhistogram.New(1, 30000000, 3)
97+
98+
for i := 0; i < 1000000; i++ {
99+
histogram.RecordValue(int64(i))
100+
}
101+
102+
percentileValuesMap := histogram.ValueAtPercentiles([]float64{50.0, 95.0, 99.0, 99.9})
103+
fmt.Printf("Percentile 50: %d\n", percentileValuesMap[50.0])
104+
fmt.Printf("Percentile 95: %d\n", percentileValuesMap[95.0])
105+
fmt.Printf("Percentile 99: %d\n", percentileValuesMap[99.0])
106+
fmt.Printf("Percentile 99.9: %d\n", percentileValuesMap[99.9])
107+
108+
// Output:
109+
// Percentile 50: 500223
110+
// Percentile 95: 950271
111+
// Percentile 99: 990207
112+
// Percentile 99.9: 999423
113+
114+
}

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ go 1.14
44

55
require (
66
github.com/davecgh/go-spew v1.1.1 // indirect
7-
github.com/google/go-cmp v0.5.2
7+
github.com/google/go-cmp v0.5.4
88
github.com/kr/text v0.2.0 // indirect
99
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
10-
github.com/stretchr/testify v1.6.1
10+
github.com/stretchr/testify v1.7.0
11+
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect
1112
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
13+
gonum.org/v1/gonum v0.8.2
1214
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
1315
)

go.sum

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
2+
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
3+
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
14
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
2-
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
35
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
46
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
57
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6-
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
7-
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
8+
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
9+
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
10+
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
11+
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
12+
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
13+
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
814
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
915
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1016
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -14,15 +20,44 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
1420
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1521
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1622
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
17-
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
18-
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
19-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
23+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
24+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
25+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
26+
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
27+
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
28+
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
29+
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
30+
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
31+
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw=
32+
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
33+
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
34+
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
35+
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
36+
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
37+
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
38+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
39+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
40+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
41+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
42+
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
43+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
44+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
45+
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
46+
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
47+
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
48+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
2049
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
2150
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
2251
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
23-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
52+
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
53+
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
54+
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
55+
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
56+
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
57+
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
2458
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2559
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
2660
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2761
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
2862
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
63+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

hdr.go

Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"fmt"
88
"io"
99
"math"
10+
"math/bits"
11+
"sort"
1012
)
1113

1214
// A Bracket is a part of a cumulative distribution.
@@ -309,22 +311,34 @@ func (h *Histogram) setCountAtIndex(idx int, n int64) {
309311
// ValueAtQuantile returns the largest value that (100% - percentile) of the overall recorded value entries
310312
// in the histogram are either larger than or equivalent to.
311313
//
314+
// The passed quantile must be a float64 value in [0.0 .. 100.0]
312315
// Note that two values are "equivalent" if `ValuesAreEquivalent(value1,value2)` would return true.
313316
//
314317
// Returns 0 if no recorded values exist.
315318
func (h *Histogram) ValueAtQuantile(q float64) int64 {
316-
if q > 100 {
317-
q = 100
319+
return h.ValueAtPercentile(q)
320+
}
321+
322+
// ValueAtPercentile returns the largest value that (100% - percentile) of the overall recorded value entries
323+
// in the histogram are either larger than or equivalent to.
324+
//
325+
// The passed percentile must be a float64 value in [0.0 .. 100.0]
326+
// Note that two values are "equivalent" if `ValuesAreEquivalent(value1,value2)` would return true.
327+
//
328+
// Returns 0 if no recorded values exist.
329+
func (h *Histogram) ValueAtPercentile(percentile float64) int64 {
330+
if percentile > 100 {
331+
percentile = 100
318332
}
319333

320334
total := int64(0)
321-
countAtPercentile := int64(((q / 100) * float64(h.totalCount)) + 0.5)
335+
countAtPercentile := int64(((percentile / 100) * float64(h.totalCount)) + 0.5)
322336

323337
i := h.iterator()
324338
for i.next() {
325339
total += i.countAtIdx
326340
if total >= countAtPercentile {
327-
if q == 0.0 {
341+
if percentile == 0.0 {
328342
return h.lowestEquivalentValue(i.valueFromIdx)
329343
}
330344
return h.highestEquivalentValue(i.valueFromIdx)
@@ -334,6 +348,45 @@ func (h *Histogram) ValueAtQuantile(q float64) int64 {
334348
return 0
335349
}
336350

351+
// ValueAtPercentiles, given an slice of percentiles returns a map containing for each passed percentile,
352+
// the largest value that (100% - percentile) of the overall recorded value entries
353+
// in the histogram are either larger than or equivalent to.
354+
//
355+
// Each element in the given an slice of percentiles must be a float64 value in [0.0 .. 100.0]
356+
// Note that two values are "equivalent" if `ValuesAreEquivalent(value1,value2)` would return true.
357+
//
358+
// Returns a map of 0's if no recorded values exist.
359+
func (h *Histogram) ValueAtPercentiles(percentiles []float64) (values map[float64]int64) {
360+
sort.Float64s(percentiles)
361+
totalQuantilesToCalculate := len(percentiles)
362+
values = make(map[float64]int64, totalQuantilesToCalculate)
363+
countAtPercentiles := make([]int64, totalQuantilesToCalculate)
364+
for i, percentile := range percentiles {
365+
if percentile > 100 {
366+
percentile = 100
367+
}
368+
values[percentile] = 0
369+
countAtPercentiles[i] = int64(((percentile / 100) * float64(h.totalCount)) + 0.5)
370+
}
371+
372+
total := int64(0)
373+
currentQuantileSlicePos := 0
374+
i := h.iterator()
375+
for currentQuantileSlicePos < totalQuantilesToCalculate && i.next() {
376+
total += i.countAtIdx
377+
for currentQuantileSlicePos < totalQuantilesToCalculate && total >= countAtPercentiles[currentQuantileSlicePos] {
378+
currentPercentile := percentiles[currentQuantileSlicePos]
379+
if currentPercentile == 0.0 {
380+
values[currentPercentile] = h.lowestEquivalentValue(i.valueFromIdx)
381+
} else {
382+
values[currentPercentile] = h.highestEquivalentValue(i.valueFromIdx)
383+
}
384+
currentQuantileSlicePos++
385+
}
386+
}
387+
return
388+
}
389+
337390
// Determine if two values are equivalent with the histogram's resolution.
338391
// Where "equivalent" means that value samples recorded for any two
339392
// equivalent values are counted in a common total count.
@@ -483,6 +536,10 @@ func (h *Histogram) pIterator(ticksPerHalfDistance int32) *pIterator {
483536

484537
func (h *Histogram) sizeOfEquivalentValueRange(v int64) int64 {
485538
bucketIdx := h.getBucketIndex(v)
539+
return h.sizeOfEquivalentValueRangeGivenBucketIdx(v, bucketIdx)
540+
}
541+
542+
func (h *Histogram) sizeOfEquivalentValueRangeGivenBucketIdx(v int64, bucketIdx int32) int64 {
486543
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
487544
adjustedBucket := bucketIdx
488545
if subBucketIdx >= h.subBucketCount {
@@ -497,12 +554,17 @@ func (h *Histogram) valueFromIndex(bucketIdx, subBucketIdx int32) int64 {
497554

498555
func (h *Histogram) lowestEquivalentValue(v int64) int64 {
499556
bucketIdx := h.getBucketIndex(v)
557+
return h.lowestEquivalentValueGivenBucketIdx(v, bucketIdx)
558+
}
559+
560+
func (h *Histogram) lowestEquivalentValueGivenBucketIdx(v int64, bucketIdx int32) int64 {
500561
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
501562
return h.valueFromIndex(bucketIdx, subBucketIdx)
502563
}
503564

504565
func (h *Histogram) nextNonEquivalentValue(v int64) int64 {
505-
return h.lowestEquivalentValue(v) + h.sizeOfEquivalentValueRange(v)
566+
bucketIdx := h.getBucketIndex(v)
567+
return h.lowestEquivalentValueGivenBucketIdx(v, bucketIdx) + h.sizeOfEquivalentValueRangeGivenBucketIdx(v, bucketIdx)
506568
}
507569

508570
func (h *Histogram) highestEquivalentValue(v int64) int64 {
@@ -527,7 +589,7 @@ func (h *Histogram) countsIndex(bucketIdx, subBucketIdx int32) int32 {
527589
// Calculates the number of powers of two by which the value is greater than the biggest value that fits in
528590
// bucket 0. This is the bucket index since each successive bucket can hold a value 2x greater.
529591
func (h *Histogram) getBucketIndex(v int64) int32 {
530-
pow2Ceiling := bitLen(v | h.subBucketMask)
592+
var pow2Ceiling = int64(64 - bits.LeadingZeros64(uint64(v|h.subBucketMask)))
531593
return int32(pow2Ceiling - int64(h.unitMagnitude) -
532594
int64(h.subBucketHalfCountMagnitude+1))
533595
}
@@ -638,28 +700,6 @@ func (p *pIterator) next() bool {
638700
return true
639701
}
640702

641-
func bitLen(x int64) (n int64) {
642-
for ; x >= 0x8000; x >>= 16 {
643-
n += 16
644-
}
645-
if x >= 0x80 {
646-
x >>= 8
647-
n += 8
648-
}
649-
if x >= 0x8 {
650-
x >>= 4
651-
n += 4
652-
}
653-
if x >= 0x2 {
654-
x >>= 2
655-
n += 2
656-
}
657-
if x >= 0x1 {
658-
n++
659-
}
660-
return
661-
}
662-
663703
// CumulativeDistribution returns an ordered list of brackets of the
664704
// distribution of recorded values.
665705
func (h *Histogram) CumulativeDistributionWithTicks(ticksPerHalfDistance int32) []Bracket {

0 commit comments

Comments
 (0)