github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/qrm-plugins/cpu/dynamicpolicy/cpuadvisor/helper.go (about) 1 /* 2 Copyright 2022 The Katalyst 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 cpuadvisor 18 19 import ( 20 "fmt" 21 "sort" 22 "strings" 23 24 "github.com/kubewharf/katalyst-core/pkg/util/general" 25 "github.com/kubewharf/katalyst-core/pkg/util/machine" 26 ) 27 28 // FakedContainerName represents a placeholder since pool entry has no container-level 29 // FakedNUMAID represents a placeholder since pools like shared/reclaimed will not contain a specific numa 30 const ( 31 EmptyOwnerPoolName = "" 32 FakedContainerName = "" 33 FakedNUMAID = -1 34 NameSeparator = "#" 35 ) 36 37 type BlockCPUSet map[string]machine.CPUSet 38 39 func NewBlockCPUSet() BlockCPUSet { 40 return make(BlockCPUSet) 41 } 42 43 // GetBlocks parses ListAndWatchResponse and returns map[int][]*Block, 44 // the map is keyed as numa id -> blocks slice (which has been sorted and deduped) 45 func (lwr *ListAndWatchResponse) GetBlocks() (map[int][]*Block, error) { 46 if lwr == nil { 47 return nil, fmt.Errorf("got nil ListAndWatchResponse") 48 } 49 50 numaToBlocks := make(map[int]map[string]*Block) 51 numaToSortedBlocks := make(map[int][]*Block) 52 blocksEntryNames := make(map[string][][]string) 53 visBlocksToNUMA := make(map[string]int) 54 55 for entryName, entry := range lwr.Entries { 56 for subEntryName, calculationInfo := range entry.Entries { 57 if calculationInfo == nil { 58 general.Warningf("[ListAndWatchResponse.GetBlocks] got nil calculationInfo entry: %s, subEntry: %s", 59 entryName, subEntryName) 60 continue 61 } 62 63 for numaIdInt64, calculationResult := range calculationInfo.CalculationResultsByNumas { 64 if calculationResult == nil { 65 general.Warningf("[ListAndWatchResponse.GetBlocks] got nil NUMA result entry: %s, subEntry: %s", 66 entryName, subEntryName) 67 continue 68 } 69 70 numaIdInt, err := general.CovertInt64ToInt(numaIdInt64) 71 if err != nil { 72 return nil, fmt.Errorf("parse nuam id: %d failed with error: %v entry: %s, subEntry: %s", 73 numaIdInt64, err, entryName, subEntryName) 74 } 75 76 if numaToBlocks[numaIdInt] == nil { 77 numaToBlocks[numaIdInt] = make(map[string]*Block) 78 } 79 80 for _, block := range calculationResult.Blocks { 81 if block == nil { 82 general.Warningf("[ListAndWatchResponse.GetBlocks] got nil block result entry: %s, subEntry: %s", 83 entryName, subEntryName) 84 continue 85 } 86 87 blockId := block.BlockId 88 if foundNUMAId, found := visBlocksToNUMA[blockId]; found && numaToBlocks[foundNUMAId][blockId] != nil { 89 if foundNUMAId != numaIdInt { 90 return nil, fmt.Errorf("found block: %s both in NUMA: %d and NUMA: %d, entry: %s, subEntry: %s", 91 blockId, foundNUMAId, numaIdInt, entryName, subEntryName) 92 } else if numaToBlocks[foundNUMAId][blockId].Result != block.Result { 93 return nil, fmt.Errorf("found block: %s result is different with current block: %s result, entry: %s, subEntry: %s", 94 numaToBlocks[foundNUMAId][blockId].String(), block.String(), entryName, subEntryName) 95 } 96 } 97 98 numaToBlocks[numaIdInt][blockId] = block 99 visBlocksToNUMA[blockId] = numaIdInt 100 blocksEntryNames[blockId] = append(blocksEntryNames[blockId], []string{entryName, subEntryName}) 101 } 102 } 103 } 104 } 105 106 for numaId, blocksMap := range numaToBlocks { 107 blocks := make([]*Block, 0, len(blocksMap)) 108 109 for _, block := range blocksMap { 110 blocks = append(blocks, block) 111 112 if len(blocksEntryNames[block.BlockId]) == 0 { 113 return nil, fmt.Errorf("there is no entryName for block: %s", block.BlockId) 114 } 115 } 116 117 sort.Slice(blocks, getBlocksLessFunc(blocksEntryNames, blocks)) 118 119 numaToSortedBlocks[numaId] = blocks 120 } 121 122 logNUMAToBlocksSeq(numaToSortedBlocks, blocksEntryNames) 123 124 return numaToSortedBlocks, nil 125 } 126 127 // getBlocksLessFunc judges if a block is less than another by entryNames of them 128 func getBlocksLessFunc(blocksEntryNames map[string][][]string, blocks []*Block) func(i, j int) bool { 129 return func(i, j int) bool { 130 blockId1 := blocks[i].BlockId 131 blockId2 := blocks[j].BlockId 132 133 entryNames1 := blocksEntryNames[blockId1] 134 entryNames2 := blocksEntryNames[blockId2] 135 136 getNames := func(entryNames [][]string, isPod bool) []string { 137 names := make([]string, 0, len(entryNames)) 138 for _, namesTuple := range entryNames { 139 entryName, subEntryName := namesTuple[0], namesTuple[1] 140 141 if isPod { 142 if subEntryName != FakedContainerName { 143 names = append(names, entryName) 144 } 145 } else { 146 if subEntryName == FakedContainerName { 147 names = append(names, entryName) 148 } 149 } 150 } 151 sort.Strings(names) 152 return names 153 } 154 155 podNames1, poolNames1, podNames2, poolNames2 := getNames(entryNames1, true), 156 getNames(entryNames1, false), 157 getNames(entryNames2, true), 158 getNames(entryNames2, false) 159 160 if len(podNames1) > len(podNames2) { 161 return true 162 } else if len(podNames1) < len(podNames2) { 163 return false 164 } else { 165 if len(podNames1) > 0 { 166 return strings.Join(podNames1, NameSeparator) < strings.Join(podNames2, NameSeparator) 167 } 168 169 if len(poolNames1) > len(poolNames2) { 170 return true 171 } else if len(poolNames1) < len(poolNames2) { 172 return false 173 } else { 174 return strings.Join(poolNames1, NameSeparator) < strings.Join(poolNames2, NameSeparator) 175 } 176 } 177 } 178 } 179 180 func logNUMAToBlocksSeq(numaToSortedBlocks map[int][]*Block, blocksEntryNames map[string][][]string) { 181 for numaId, blocks := range numaToSortedBlocks { 182 for i, block := range blocks { 183 general.InfoS("logNUMAToBlocksSeq", "numaId", numaId, "seq", i, "blockId", block.BlockId, "entryNames", blocksEntryNames[block.BlockId]) 184 } 185 } 186 } 187 188 // GeEntryNUMABlocks returns Block lists according to the given [entry, subEntry] pair 189 func (lwr *ListAndWatchResponse) GeEntryNUMABlocks(entry, subEntry string, numa int64) ([]*Block, bool) { 190 if entry, ok := lwr.Entries[entry]; !ok { 191 return []*Block{}, false 192 } else if info, ok := entry.Entries[subEntry]; !ok { 193 return []*Block{}, false 194 } else if results, ok := info.CalculationResultsByNumas[numa]; !ok { 195 return []*Block{}, false 196 } else { 197 return results.Blocks, true 198 } 199 } 200 201 // GetCalculationInfo returns CalculationInfo according to the given [entry, subEntry] 202 func (lwr *ListAndWatchResponse) GetCalculationInfo(entry, subEntry string) (*CalculationInfo, bool) { 203 if entry, ok := lwr.Entries[entry]; !ok { 204 return nil, false 205 } else if info, ok := entry.Entries[subEntry]; !ok { 206 return nil, false 207 } else { 208 return info, true 209 } 210 } 211 212 // FilterCalculationInfo filter out CalculationInfo only for dedicated pod, 213 // and the returned map and formatted as pod -> container -> CalculationInfo 214 func (lwr *ListAndWatchResponse) FilterCalculationInfo(pool string) map[string]map[string]*CalculationInfo { 215 dedicatedCalculationInfos := make(map[string]map[string]*CalculationInfo) 216 for entryName, entry := range lwr.Entries { 217 for subEntryName, calculationInfo := range entry.Entries { 218 if calculationInfo != nil && calculationInfo.OwnerPoolName == pool { 219 if dedicatedCalculationInfos[entryName] == nil { 220 dedicatedCalculationInfos[entryName] = make(map[string]*CalculationInfo) 221 } 222 223 dedicatedCalculationInfos[entryName][subEntryName] = calculationInfo 224 } 225 } 226 } 227 return dedicatedCalculationInfos 228 } 229 230 // GetNUMACalculationResult returns numa-level calculation results 231 func (ce *CalculationEntries) GetNUMACalculationResult(container string, numa int64) (*NumaCalculationResult, bool) { 232 info, ok := ce.Entries[container] 233 if !ok || info == nil { 234 return nil, false 235 } 236 237 if numaInfo, ok := info.CalculationResultsByNumas[numa]; ok { 238 return numaInfo, true 239 } 240 return nil, false 241 } 242 243 // GetNUMAQuantities returns quantity in each numa for in this CalculationInfo 244 func (ci *CalculationInfo) GetNUMAQuantities() (map[int]int, error) { 245 if ci == nil { 246 return nil, fmt.Errorf("got nil ci") 247 } 248 249 numaQuantities := make(map[int]int) 250 for numaId, numaResult := range ci.CalculationResultsByNumas { 251 if numaResult == nil { 252 general.Warningf("[GetTotalQuantity] got nil NUMA result") 253 continue 254 } 255 256 var quantityUInt64 uint64 = 0 257 for _, block := range numaResult.Blocks { 258 if block == nil { 259 general.Warningf("[GetTotalQuantity] got nil block") 260 continue 261 } 262 quantityUInt64 += block.Result 263 } 264 265 quantityInt, err := general.CovertUInt64ToInt(quantityUInt64) 266 if err != nil { 267 return nil, fmt.Errorf("converting quantity: %d to int failed with error: %v", 268 quantityUInt64, err) 269 } 270 271 numaIdInt, err := general.CovertInt64ToInt(numaId) 272 if err != nil { 273 return nil, fmt.Errorf("converting quantity: %d to int failed with error: %v", 274 numaId, err) 275 } 276 numaQuantities[numaIdInt] = quantityInt 277 } 278 return numaQuantities, nil 279 } 280 281 // GetTotalQuantity returns total quantity for in this CalculationInfo 282 func (ci *CalculationInfo) GetTotalQuantity() (int, error) { 283 numaQuantities, err := ci.GetNUMAQuantities() 284 if err != nil { 285 return 0, err 286 } 287 288 totalQuantity := 0 289 for _, quantity := range numaQuantities { 290 totalQuantity += quantity 291 } 292 return totalQuantity, nil 293 } 294 295 // GetCPUSet returns cpuset for this container by union all blocks for it 296 func (ci *CalculationInfo) GetCPUSet(entry, subEntry string, b BlockCPUSet) (machine.CPUSet, error) { 297 cpusets := machine.NewCPUSet() 298 for _, results := range ci.CalculationResultsByNumas { 299 if results == nil { 300 general.Warningf("got nil numa result entry: %s, subEntry: %s", entry, subEntry) 301 continue 302 } 303 304 for _, block := range results.Blocks { 305 if block == nil { 306 general.Warningf("got nil block result entry: %s, subEntry: %s", entry, subEntry) 307 continue 308 } 309 310 if cset, found := b[block.BlockId]; !found { 311 return machine.CPUSet{}, fmt.Errorf("block %s not found, entry: %s, subEntry: %s", block.BlockId, entry, subEntry) 312 } else { 313 cpusets = cpusets.Union(cset) 314 } 315 } 316 } 317 return cpusets, nil 318 }