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  }