@@ -102,10 +102,8 @@ func min(x, y int) int {
102
102
type numaOrSocketsFirstFuncs interface {
103
103
takeFullFirstLevel ()
104
104
takeFullSecondLevel ()
105
- takeThirdLevel ()
106
105
sortAvailableNUMANodes () []int
107
106
sortAvailableSockets () []int
108
- sortAvailableUncoreCaches () []int
109
107
sortAvailableCores () []int
110
108
}
111
109
@@ -127,18 +125,12 @@ func (n *numaFirst) takeFullSecondLevel() {
127
125
n .acc .takeFullSockets ()
128
126
}
129
127
130
- // In Split UncoreCache Topology, we take from the sets of UncoreCache as the third level
131
- func (n * numaFirst ) takeThirdLevel () {
132
- n .acc .takeUncoreCache ()
133
- }
134
-
135
- // If NUMA nodes are higher in the memory hierarchy than sockets, then just
136
- // sort the NUMA nodes directly, and return them.
137
- func (n * numaFirst ) sortAvailableUncoreCaches () []int {
128
+ // Sort the UncoreCaches within the NUMA nodes.
129
+ func (a * cpuAccumulator ) sortAvailableUncoreCaches () []int {
138
130
var result []int
139
- for _ , socket := range n . acc .sortAvailableNUMANodes () {
140
- uncore := n . acc . details .UncoreInNUMANodes (socket ).UnsortedList ()
141
- n . acc . sort (uncore , n . acc .details .CPUsInUncoreCaches )
131
+ for _ , numa := range a .sortAvailableNUMANodes () {
132
+ uncore := a . details .UncoreInNUMANodes (numa ).UnsortedList ()
133
+ a . sort (uncore , a .details .CPUsInUncoreCaches )
142
134
result = append (result , uncore ... )
143
135
}
144
136
return result
@@ -189,10 +181,6 @@ func (s *socketsFirst) takeFullSecondLevel() {
189
181
s .acc .takeFullNUMANodes ()
190
182
}
191
183
192
- func (s * socketsFirst ) takeThirdLevel () {
193
- s .acc .takeUncoreCache ()
194
- }
195
-
196
184
// If sockets are higher in the memory hierarchy than NUMA nodes, then we need
197
185
// to pull the set of NUMA nodes out of each sorted Socket, and accumulate the
198
186
// partial order across them.
@@ -214,18 +202,6 @@ func (s *socketsFirst) sortAvailableSockets() []int {
214
202
return sockets
215
203
}
216
204
217
- // If sockets higher in the memory hierarchy than NUMA nodes, then UncoreCache
218
- // sit directly below NUMA Nodes in the memory hierchy
219
- func (s * socketsFirst ) sortAvailableUncoreCaches () []int {
220
- var result []int
221
- for _ , uncore := range s .acc .sortAvailableNUMANodes () {
222
- uncore := s .acc .details .UncoreInNUMANodes (uncore ).UnsortedList ()
223
- s .acc .sort (uncore , s .acc .details .CPUsInUncoreCaches )
224
- result = append (result , uncore ... )
225
- }
226
- return result
227
- }
228
-
229
205
// If sockets are higher in the memory hierarchy than NUMA nodes, then cores
230
206
// sit directly below NUMA Nodes in the memory hierarchy.
231
207
func (s * socketsFirst ) sortAvailableCores () []int {
@@ -273,8 +249,8 @@ func (a *cpuAccumulator) isSocketFree(socketID int) bool {
273
249
return a .details .CPUsInSockets (socketID ).Size () == a .topo .CPUsPerSocket ()
274
250
}
275
251
276
- // Returns true if the supplied UnCoreCache is fully available in `a.details`.
277
- // "fully available" means that all the CPUs in it are free.
252
+ // Returns true if the supplied UnCoreCache is fully available,
253
+ // meaning all its CPUs are free in `a.details` .
278
254
func (a * cpuAccumulator ) isUncoreCacheFree (uncoreID int ) bool {
279
255
return a .details .CPUsInUncoreCaches (uncoreID ).Size () == a .topo .CPUDetails .CPUsInUncoreCaches (uncoreID ).Size ()
280
256
}
@@ -309,19 +285,14 @@ func (a *cpuAccumulator) freeSockets() []int {
309
285
// Returns free UncoreCache IDs as a slice sorted by sortAvailableUnCoreCache().
310
286
func (a * cpuAccumulator ) freeUncoreCache () []int {
311
287
free := []int {}
312
- for _ , uncore := range a .numaOrSocketsFirst . sortAvailableUncoreCaches () {
288
+ for _ , uncore := range a .sortAvailableUncoreCaches () {
313
289
if a .isUncoreCacheFree (uncore ) {
314
290
free = append (free , uncore )
315
291
}
316
292
}
317
293
return free
318
294
}
319
295
320
- // Returns all UncoreCache IDs as a slice sorted by sortAvailableUncoreCache().
321
- func (a * cpuAccumulator ) allUncoreCache () []int {
322
- return a .numaOrSocketsFirst .sortAvailableUncoreCaches ()
323
- }
324
-
325
296
// Returns free core IDs as a slice sorted by sortAvailableCores().
326
297
func (a * cpuAccumulator ) freeCores () []int {
327
298
free := []int {}
@@ -414,7 +385,7 @@ func (a *cpuAccumulator) takeFullSockets() {
414
385
a .take (cpusInSocket )
415
386
}
416
387
}
417
- func (a * cpuAccumulator ) takeFullUnCore () {
388
+ func (a * cpuAccumulator ) takeFullUncore () {
418
389
for _ , uncore := range a .freeUncoreCache () {
419
390
cpusInUncore := a .topo .CPUDetails .CPUsInUncoreCaches (uncore )
420
391
if ! a .needsAtLeast (cpusInUncore .Size ()) {
@@ -424,30 +395,34 @@ func (a *cpuAccumulator) takeFullUnCore() {
424
395
}
425
396
}
426
397
398
+ func (a * cpuAccumulator ) takePartialUncore (uncoreID int ) {
399
+ numCoresNeeded := a .numCPUsNeeded / a .topo .CPUsPerCore ()
400
+ var freeCPUsInUncoreCache cpuset.CPUSet
401
+ freeCoresInUncoreCache := a .details .CoresNeededInUncoreCache (numCoresNeeded , uncoreID )
402
+ for _ , coreID := range freeCoresInUncoreCache .List () {
403
+ freeCPUsInUncoreCache = freeCPUsInUncoreCache .Union (a .topo .CPUDetails .CPUsInCores (coreID ))
404
+ }
405
+ klog .V (4 ).InfoS ("freeCPUsInUncorecache : " , "freeCPUsInUncorecache" , freeCPUsInUncoreCache .String (), "freeCPUsInUnCoreCache" , freeCPUsInUncoreCache .String ())
406
+ if a .numCPUsNeeded == freeCPUsInUncoreCache .Size () {
407
+ a .take (freeCPUsInUncoreCache )
408
+ }
409
+ }
410
+
427
411
// First try to take full UncoreCache, if available and need is at least the size of the UncoreCache group.
428
412
// Second try to take the partial UncoreCache if available and the request size can fit w/in the UncoreCache.
429
413
func (a * cpuAccumulator ) takeUncoreCache () {
430
- for _ , uncore := range a .allUncoreCache () {
431
- numCoresNeeded := a .numCPUsNeeded / a .topo .CPUsPerCore ()
432
-
433
- // take full UncoreCache if the CPUs needed is greater a UncoreCache size
434
- if a .numCPUsNeeded >= a .topo .NumCPUs / a .topo .NumUncoreCache {
435
- a .takeFullUnCore ()
414
+ cpusPerUncoreCache := a .topo .NumCPUs / a .topo .NumUncoreCache
415
+ for _ , uncore := range a .sortAvailableUncoreCaches () {
416
+ // take full UncoreCache if the CPUs needed is greater than free UncoreCache size
417
+ if a .needsAtLeast (cpusPerUncoreCache ) {
418
+ a .takeFullUncore ()
436
419
}
437
420
438
- var freeCPUsInUncoreCache cpuset.CPUSet
439
- // need to get needed cores in UncoreCache
440
- freeCoresInUncoreCache := a .details .CoresNeededInUncoreCache (numCoresNeeded , uncore )
441
- klog .V (2 ).InfoS ("free cores from a.details list: " , "freeCoresInUncorecache" , freeCoresInUncoreCache )
442
- for _ , coreID := range freeCoresInUncoreCache .List () {
443
- freeCPUsInUncoreCache = freeCPUsInUncoreCache .Union (a .topo .CPUDetails .CPUsInCores (coreID ))
444
- }
445
- klog .V (2 ).InfoS ("freeCPUsInUncorecache : " , "freeCPUsInUncorecache" , freeCPUsInUncoreCache )
446
- if a .numCPUsNeeded == freeCPUsInUncoreCache .Size () {
447
- klog .V (4 ).InfoS ("takePartialUncore: claiming cores from Uncorecache ID" , "uncore" , uncore )
448
- a .take (freeCPUsInUncoreCache )
421
+ if a .isSatisfied () {
422
+ return
449
423
}
450
424
425
+ a .takePartialUncore (uncore )
451
426
if a .isSatisfied () {
452
427
return
453
428
}
@@ -543,7 +518,7 @@ func (a *cpuAccumulator) iterateCombinations(n []int, k int, f func([]int) LoopC
543
518
helper (n , k , 0 , []int {}, f )
544
519
}
545
520
546
- func takeByTopologyNUMAPacked (topo * topology.CPUTopology , availableCPUs cpuset.CPUSet , numCPUs int ) (cpuset.CPUSet , error ) {
521
+ func takeByTopologyNUMAPacked (topo * topology.CPUTopology , availableCPUs cpuset.CPUSet , numCPUs int , preferAlignByUncoreCache bool ) (cpuset.CPUSet , error ) {
547
522
acc := newCPUAccumulator (topo , availableCPUs , numCPUs )
548
523
if acc .isSatisfied () {
549
524
return acc .result , nil
@@ -566,66 +541,23 @@ func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.C
566
541
return acc .result , nil
567
542
}
568
543
569
- // 2. Acquire whole cores, if available and the container requires at least
570
- // a core's-worth of CPUs.
571
- acc .takeFullCores ()
572
- if acc .isSatisfied () {
573
- return acc .result , nil
574
- }
575
-
576
- // 3. Acquire single threads, preferring to fill partially-allocated cores
577
- // on the same sockets as the whole cores we have already taken in this
578
- // allocation.
579
- acc .takeRemainingCPUs ()
580
- if acc .isSatisfied () {
581
- return acc .result , nil
582
- }
583
-
584
- return cpuset .New (), fmt .Errorf ("failed to allocate cpus" )
585
- }
586
-
587
- // takeByTopologyUnCoreCachePacked uses the "packed" sorting strategy similar to takeByTopologyNUMAPacked.
588
- // It includes an additional level of sorting by uncorecache
589
- func takeByTopologyUncoreCachePacked (topo * topology.CPUTopology , availableCPUs cpuset.CPUSet , numCPUs int , cpuSortingStrategy CPUSortingStrategy ) (cpuset.CPUSet , error ) {
590
- acc := newCPUAccumulator (topo , availableCPUs , numCPUs , cpuSortingStrategy )
591
- if acc .isSatisfied () {
592
- return acc .result , nil
593
- }
594
- if acc .isFailed () {
595
- return cpuset .New (), fmt .Errorf ("not enough cpus available to satisfy request: requested=%d, available=%d" , numCPUs , availableCPUs .Size ())
596
- }
597
-
598
- // Algorithm: topology-aware best-fit
599
- // 1. Acquire whole NUMA nodes and sockets, if available and the container
600
- // requires at least a NUMA node or socket's-worth of CPUs. If NUMA
601
- // Nodes map to 1 or more sockets, pull from NUMA nodes first.
602
- // Otherwise pull from sockets first.
603
- acc .numaOrSocketsFirst .takeFullFirstLevel ()
604
- if acc .isSatisfied () {
605
- return acc .result , nil
606
- }
607
- acc .numaOrSocketsFirst .takeFullSecondLevel ()
608
- if acc .isSatisfied () {
609
- return acc .result , nil
610
- }
611
-
612
- // 2. Acquire partial uncorecache, if there are enough CPUs available to satisfy the container requirement
613
- // Acquire the full uncorecache, if available and the container requires at least all the CPUs in the uncorecache grouping
614
- acc .numaOrSocketsFirst .takeThirdLevel ()
615
- if acc .isSatisfied () {
616
- return acc .result , nil
544
+ // 2. If PreferAlignByUncoreCache is enabled, acquire whole UncoreCaches
545
+ // if available and the container requires at least a UncoreCache's-worth
546
+ // of CPUs. Otherwise, acquire CPUs from the least amount of UncoreCaches.
547
+ if preferAlignByUncoreCache {
548
+ acc .takeUncoreCache ()
549
+ if acc .isSatisfied () {
550
+ return acc .result , nil
551
+ }
617
552
}
618
553
619
554
// 3. Acquire whole cores, if available and the container requires at least
620
555
// a core's-worth of CPUs.
621
556
// If `CPUSortingStrategySpread` is specified, skip taking the whole core.
622
- if cpuSortingStrategy != CPUSortingStrategySpread {
623
- acc .takeFullCores ()
624
- if acc .isSatisfied () {
625
- return acc .result , nil
626
- }
557
+ acc .takeFullCores ()
558
+ if acc .isSatisfied () {
559
+ return acc .result , nil
627
560
}
628
-
629
561
// 4. Acquire single threads, preferring to fill partially-allocated cores
630
562
// on the same sockets as the whole cores we have already taken in this
631
563
// allocation.
@@ -704,8 +636,10 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
704
636
// If the number of CPUs requested cannot be handed out in chunks of
705
637
// 'cpuGroupSize', then we just call out the packing algorithm since we
706
638
// can't distribute CPUs in this chunk size.
639
+ // PreferAlignByUncoreCache feature not implemented here yet and set to false.
640
+ // Support for PreferAlignByUncoreCache to be done at beta release.
707
641
if (numCPUs % cpuGroupSize ) != 0 {
708
- return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs )
642
+ return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , false )
709
643
}
710
644
711
645
// Otherwise build an accumulator to start allocating CPUs from.
@@ -888,7 +822,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
888
822
// size 'cpuGroupSize' from 'bestCombo'.
889
823
distribution := (numCPUs / len (bestCombo ) / cpuGroupSize ) * cpuGroupSize
890
824
for _ , numa := range bestCombo {
891
- cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), distribution )
825
+ cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), distribution , false )
892
826
acc .take (cpus )
893
827
}
894
828
@@ -903,7 +837,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
903
837
if acc .details .CPUsInNUMANodes (numa ).Size () < cpuGroupSize {
904
838
continue
905
839
}
906
- cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), cpuGroupSize )
840
+ cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), cpuGroupSize , false )
907
841
acc .take (cpus )
908
842
remainder -= cpuGroupSize
909
843
}
@@ -927,5 +861,5 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
927
861
928
862
// If we never found a combination of NUMA nodes that we could properly
929
863
// distribute CPUs across, fall back to the packing algorithm.
930
- return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs )
864
+ return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , false )
931
865
}
0 commit comments