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.
315318func (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
484537func (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
498555func (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
504565func (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
508570func (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.
529591func (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.
665705func (h * Histogram ) CumulativeDistributionWithTicks (ticksPerHalfDistance int32 ) []Bracket {
0 commit comments