k8s.io/perf-tests/clusterloader2@v0.0.0-20240304094227-64bdb12da87e/pkg/measurement/util/resource.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  	"sort"
    22  	"time"
    23  )
    24  
    25  // ContainerResourceUsage represents resource usage by a single container.
    26  type ContainerResourceUsage struct {
    27  	Name                    string
    28  	Timestamp               time.Time
    29  	CPUUsageInCores         float64
    30  	MemoryUsageInBytes      uint64
    31  	MemoryWorkingSetInBytes uint64
    32  	MemoryRSSInBytes        uint64
    33  	// The interval used to calculate CPUUsageInCores.
    34  	CPUInterval time.Duration
    35  }
    36  
    37  // ResourceUsagePerContainer is a map of ContainerResourceUsage for containers.
    38  type ResourceUsagePerContainer map[string]*ContainerResourceUsage
    39  
    40  // UsageDataPerContainer contains resource usage data series.
    41  type UsageDataPerContainer struct {
    42  	CPUData        []float64
    43  	MemUseData     []uint64
    44  	MemWorkSetData []uint64
    45  }
    46  
    47  // ResourceConstraint specifies constraint on resources.
    48  type ResourceConstraint struct {
    49  	CPUConstraint    float64 `json:"cpuConstraint"`
    50  	MemoryConstraint uint64  `json:"memoryConstraint"`
    51  }
    52  
    53  // SingleContainerSummary is a resource usage summary for a single container.
    54  type SingleContainerSummary struct {
    55  	Name string
    56  	CPU  float64
    57  	Mem  uint64
    58  }
    59  
    60  type uint64arr []uint64
    61  
    62  func (a uint64arr) Len() int           { return len(a) }
    63  func (a uint64arr) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
    64  func (a uint64arr) Less(i, j int) bool { return a[i] < a[j] }
    65  
    66  // ComputePercentiles calculates percentiles for given data series.
    67  func ComputePercentiles(timeSeries []ResourceUsagePerContainer, percentilesToCompute []int) map[int]ResourceUsagePerContainer {
    68  	if len(timeSeries) == 0 {
    69  		return make(map[int]ResourceUsagePerContainer)
    70  	}
    71  	dataMap := make(map[string]*UsageDataPerContainer)
    72  	for i := range timeSeries {
    73  		for name, data := range timeSeries[i] {
    74  			if dataMap[name] == nil {
    75  				dataMap[name] = &UsageDataPerContainer{
    76  					CPUData:        make([]float64, 0, len(timeSeries)),
    77  					MemUseData:     make([]uint64, 0, len(timeSeries)),
    78  					MemWorkSetData: make([]uint64, 0, len(timeSeries)),
    79  				}
    80  			}
    81  			dataMap[name].CPUData = append(dataMap[name].CPUData, data.CPUUsageInCores)
    82  			dataMap[name].MemUseData = append(dataMap[name].MemUseData, data.MemoryUsageInBytes)
    83  			dataMap[name].MemWorkSetData = append(dataMap[name].MemWorkSetData, data.MemoryWorkingSetInBytes)
    84  		}
    85  	}
    86  	for _, v := range dataMap {
    87  		sort.Float64s(v.CPUData)
    88  		sort.Sort(uint64arr(v.MemUseData))
    89  		sort.Sort(uint64arr(v.MemWorkSetData))
    90  	}
    91  
    92  	result := make(map[int]ResourceUsagePerContainer)
    93  	for _, perc := range percentilesToCompute {
    94  		data := make(ResourceUsagePerContainer)
    95  		for k, v := range dataMap {
    96  			percentileIndex := int(math.Ceil(float64(len(v.CPUData)*perc)/100)) - 1
    97  			data[k] = &ContainerResourceUsage{
    98  				Name:                    k,
    99  				CPUUsageInCores:         v.CPUData[percentileIndex],
   100  				MemoryUsageInBytes:      v.MemUseData[percentileIndex],
   101  				MemoryWorkingSetInBytes: v.MemWorkSetData[percentileIndex],
   102  			}
   103  		}
   104  		result[perc] = data
   105  	}
   106  	return result
   107  }
   108  
   109  // LeftMergeData merges two data structures.
   110  func LeftMergeData(left, right map[int]ResourceUsagePerContainer) map[int]ResourceUsagePerContainer {
   111  	result := make(map[int]ResourceUsagePerContainer)
   112  	for percentile, data := range left {
   113  		result[percentile] = data
   114  		if _, ok := right[percentile]; !ok {
   115  			continue
   116  		}
   117  		for k, v := range right[percentile] {
   118  			result[percentile][k] = v
   119  		}
   120  	}
   121  	return result
   122  }