k8s.io/perf-tests/clusterloader2@v0.0.0-20240304094227-64bdb12da87e/pkg/measurement/util/histogram.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "math" 21 "reflect" 22 "sort" 23 "strconv" 24 25 dto "github.com/prometheus/client_model/go" 26 "github.com/prometheus/common/model" 27 28 "k8s.io/component-base/metrics/testutil" 29 ) 30 31 // Histogram is a structure that represents distribution of data. 32 type Histogram struct { 33 Labels map[string]string `json:"labels"` 34 // Buckets maps value to cumulative sample count: 35 // * key: float64 converted to string 36 // * value: cumulative count of all samples less or equal to the key. 37 Buckets map[string]int `json:"buckets"` 38 } 39 40 // Quantile calculates the quantile 'q' based on the given buckets of Histogram. 41 func (h *Histogram) Quantile(q float64) (float64, error) { 42 hist := testutil.Histogram{ 43 &dto.Histogram{ 44 Bucket: []*dto.Bucket{}, 45 }, 46 } 47 48 for k, v := range h.Buckets { 49 upper, err := strconv.ParseFloat(k, 64) 50 if err != nil { 51 return math.MaxFloat64, err 52 } 53 54 cumulativeCount := uint64(v) 55 hist.Bucket = append(hist.Bucket, &dto.Bucket{ 56 CumulativeCount: &cumulativeCount, 57 UpperBound: &upper, 58 }) 59 } 60 61 // hist.Quantile expected a slice of Buckets ordered by UpperBound 62 sort.Slice(hist.Bucket, func(i, j int) bool { 63 return *hist.Bucket[i].UpperBound < *hist.Bucket[j].UpperBound 64 }) 65 66 // hist.Quantile is using hist.SampleCount, let's populate it. 67 if len(hist.Bucket) != 0 { 68 sampleCount := *hist.Bucket[len(hist.Bucket)-1].CumulativeCount 69 hist.SampleCount = &sampleCount 70 } 71 72 return hist.Quantile(q), nil 73 } 74 75 // HistogramVec is an array of Histograms. 76 type HistogramVec []Histogram 77 78 // NewHistogram creates new Histogram instance. 79 func NewHistogram(labels map[string]string) *Histogram { 80 return &Histogram{ 81 Labels: labels, 82 Buckets: make(map[string]int), 83 } 84 } 85 86 // ConvertSampleToBucket converts prometheus sample into HistogramVec bucket. 87 func ConvertSampleToBucket(sample *model.Sample, h *HistogramVec) { 88 labels := make(map[string]string) 89 for k, v := range sample.Metric { 90 if k != model.BucketLabel { 91 labels[string(k)] = string(v) 92 } 93 } 94 var hist *Histogram 95 for i := range *h { 96 if reflect.DeepEqual(labels, (*h)[i].Labels) { 97 hist = &((*h)[i]) 98 break 99 } 100 } 101 if hist == nil { 102 hist = NewHistogram(labels) 103 *h = append(*h, *hist) 104 } 105 hist.Buckets[string(sample.Metric[model.BucketLabel])] += int(sample.Value) 106 } 107 108 // ConvertSampleToHistogram converts prometheus sample into Histogram. 109 func ConvertSampleToHistogram(sample *model.Sample, h *Histogram) { 110 labels := make(map[string]string) 111 for k, v := range sample.Metric { 112 if k != model.BucketLabel { 113 labels[string(k)] = string(v) 114 } 115 } 116 117 h.Labels = labels 118 h.Buckets[string(sample.Metric[model.BucketLabel])] += int(sample.Value) 119 }