@@ -118,6 +118,17 @@ func (n *numaFirst) takeFullSecondLevel() {
118
118
n .acc .takeFullSockets ()
119
119
}
120
120
121
+ // Sort the UncoreCaches within the NUMA nodes.
122
+ func (a * cpuAccumulator ) sortAvailableUncoreCaches () []int {
123
+ var result []int
124
+ for _ , numa := range a .sortAvailableNUMANodes () {
125
+ uncore := a .details .UncoreInNUMANodes (numa ).UnsortedList ()
126
+ a .sort (uncore , a .details .CPUsInUncoreCaches )
127
+ result = append (result , uncore ... )
128
+ }
129
+ return result
130
+ }
131
+
121
132
// If NUMA nodes are higher in the memory hierarchy than sockets, then just
122
133
// sort the NUMA nodes directly, and return them.
123
134
func (n * numaFirst ) sortAvailableNUMANodes () []int {
@@ -318,6 +329,12 @@ func (a *cpuAccumulator) isSocketFree(socketID int) bool {
318
329
return a .details .CPUsInSockets (socketID ).Size () == a .topo .CPUsPerSocket ()
319
330
}
320
331
332
+ // Returns true if the supplied UnCoreCache is fully available,
333
+ // "fully available" means that all the CPUs in it are free.
334
+ func (a * cpuAccumulator ) isUncoreCacheFree (uncoreID int ) bool {
335
+ return a .details .CPUsInUncoreCaches (uncoreID ).Size () == a .topo .CPUDetails .CPUsInUncoreCaches (uncoreID ).Size ()
336
+ }
337
+
321
338
// Returns true if the supplied core is fully available in `a.details`.
322
339
// "fully available" means that all the CPUs in it are free.
323
340
func (a * cpuAccumulator ) isCoreFree (coreID int ) bool {
@@ -346,6 +363,17 @@ func (a *cpuAccumulator) freeSockets() []int {
346
363
return free
347
364
}
348
365
366
+ // Returns free UncoreCache IDs as a slice sorted by sortAvailableUnCoreCache().
367
+ func (a * cpuAccumulator ) freeUncoreCache () []int {
368
+ free := []int {}
369
+ for _ , uncore := range a .sortAvailableUncoreCaches () {
370
+ if a .isUncoreCacheFree (uncore ) {
371
+ free = append (free , uncore )
372
+ }
373
+ }
374
+ return free
375
+ }
376
+
349
377
// Returns free core IDs as a slice sorted by sortAvailableCores().
350
378
func (a * cpuAccumulator ) freeCores () []int {
351
379
free := []int {}
@@ -519,6 +547,60 @@ func (a *cpuAccumulator) takeFullSockets() {
519
547
}
520
548
}
521
549
550
+ func (a * cpuAccumulator ) takeFullUncore () {
551
+ for _ , uncore := range a .freeUncoreCache () {
552
+ cpusInUncore := a .topo .CPUDetails .CPUsInUncoreCaches (uncore )
553
+ if ! a .needsAtLeast (cpusInUncore .Size ()) {
554
+ continue
555
+ }
556
+ klog .V (4 ).InfoS ("takeFullUncore: claiming uncore" , "uncore" , uncore )
557
+ a .take (cpusInUncore )
558
+ }
559
+ }
560
+
561
+ func (a * cpuAccumulator ) takePartialUncore (uncoreID int ) {
562
+ numCoresNeeded := a .numCPUsNeeded / a .topo .CPUsPerCore ()
563
+
564
+ // note: we need to keep the first N free cores (physical cpus) and only we we got these expand to their
565
+ // cpus (virtual cpus). Taking directly the first M cpus (virtual cpus) leads to suboptimal allocation
566
+ freeCores := a .details .CoresNeededInUncoreCache (numCoresNeeded , uncoreID )
567
+ freeCPUs := a .details .CPUsInCores (freeCores .UnsortedList ()... )
568
+
569
+ claimed := (a .numCPUsNeeded == freeCPUs .Size ())
570
+ klog .V (4 ).InfoS ("takePartialUncore: trying to claim partial uncore" ,
571
+ "uncore" , uncoreID ,
572
+ "claimed" , claimed ,
573
+ "needed" , a .numCPUsNeeded ,
574
+ "cores" , freeCores .String (),
575
+ "cpus" , freeCPUs .String ())
576
+ if ! claimed {
577
+ return
578
+
579
+ }
580
+ a .take (freeCPUs )
581
+ }
582
+
583
+ // First try to take full UncoreCache, if available and need is at least the size of the UncoreCache group.
584
+ // Second try to take the partial UncoreCache if available and the request size can fit w/in the UncoreCache.
585
+ func (a * cpuAccumulator ) takeUncoreCache () {
586
+ numCPUsInUncore := a .topo .CPUsPerUncore ()
587
+ for _ , uncore := range a .sortAvailableUncoreCaches () {
588
+ // take full UncoreCache if the CPUs needed is greater than free UncoreCache size
589
+ if a .needsAtLeast (numCPUsInUncore ) {
590
+ a .takeFullUncore ()
591
+ }
592
+
593
+ if a .isSatisfied () {
594
+ return
595
+ }
596
+
597
+ a .takePartialUncore (uncore )
598
+ if a .isSatisfied () {
599
+ return
600
+ }
601
+ }
602
+ }
603
+
522
604
func (a * cpuAccumulator ) takeFullCores () {
523
605
for _ , core := range a .freeCores () {
524
606
cpusInCore := a .topo .CPUDetails .CPUsInCores (core )
@@ -637,6 +719,14 @@ func (a *cpuAccumulator) iterateCombinations(n []int, k int, f func([]int) LoopC
637
719
// or the remaining number of CPUs to take after having taken full sockets and NUMA nodes is less
638
720
// than a whole NUMA node, the function tries to take whole physical cores (cores).
639
721
//
722
+ // If `PreferAlignByUncoreCache` is enabled, the function will try to optimally assign Uncorecaches.
723
+ // If `numCPUs` is larger than or equal to the total number of CPUs in a Uncorecache, and there are
724
+ // free (i.e. all CPUs within the Uncorecache are free) Uncorecaches, the function takes as many entire
725
+ // cores from free Uncorecaches as possible. If/Once `numCPUs` is smaller than the total number of
726
+ // CPUs in a free Uncorecache, the function scans each Uncorecache index in numerical order to assign
727
+ // cores that will fit within the Uncorecache. If `numCPUs` cannot fit within any Uncorecache, the
728
+ // function tries to take whole physical cores.
729
+ //
640
730
// If `numCPUs` is bigger than the total number of CPUs in a core, and there are
641
731
// free (i.e. all CPUs in them are free) cores, the function takes as many entire free cores as possible.
642
732
// The cores are taken from one socket at a time, and the sockets are considered by
@@ -658,7 +748,7 @@ func (a *cpuAccumulator) iterateCombinations(n []int, k int, f func([]int) LoopC
658
748
// the least amount of free CPUs to the one with the highest amount of free CPUs (i.e. in ascending
659
749
// order of free CPUs). For any NUMA node, the cores are selected from the ones in the socket with
660
750
// the least amount of free CPUs to the one with the highest amount of free CPUs.
661
- func takeByTopologyNUMAPacked (topo * topology.CPUTopology , availableCPUs cpuset.CPUSet , numCPUs int , cpuSortingStrategy CPUSortingStrategy ) (cpuset.CPUSet , error ) {
751
+ func takeByTopologyNUMAPacked (topo * topology.CPUTopology , availableCPUs cpuset.CPUSet , numCPUs int , cpuSortingStrategy CPUSortingStrategy , preferAlignByUncoreCache bool ) (cpuset.CPUSet , error ) {
662
752
acc := newCPUAccumulator (topo , availableCPUs , numCPUs , cpuSortingStrategy )
663
753
if acc .isSatisfied () {
664
754
return acc .result , nil
@@ -681,7 +771,17 @@ func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.C
681
771
return acc .result , nil
682
772
}
683
773
684
- // 2. Acquire whole cores, if available and the container requires at least
774
+ // 2. If PreferAlignByUncoreCache is enabled, acquire whole UncoreCaches
775
+ // if available and the container requires at least a UncoreCache's-worth
776
+ // of CPUs. Otherwise, acquire CPUs from the least amount of UncoreCaches.
777
+ if preferAlignByUncoreCache {
778
+ acc .takeUncoreCache ()
779
+ if acc .isSatisfied () {
780
+ return acc .result , nil
781
+ }
782
+ }
783
+
784
+ // 3. Acquire whole cores, if available and the container requires at least
685
785
// a core's-worth of CPUs.
686
786
// If `CPUSortingStrategySpread` is specified, skip taking the whole core.
687
787
if cpuSortingStrategy != CPUSortingStrategySpread {
@@ -691,7 +791,7 @@ func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.C
691
791
}
692
792
}
693
793
694
- // 3 . Acquire single threads, preferring to fill partially-allocated cores
794
+ // 4 . Acquire single threads, preferring to fill partially-allocated cores
695
795
// on the same sockets as the whole cores we have already taken in this
696
796
// allocation.
697
797
acc .takeRemainingCPUs ()
@@ -769,8 +869,10 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
769
869
// If the number of CPUs requested cannot be handed out in chunks of
770
870
// 'cpuGroupSize', then we just call out the packing algorithm since we
771
871
// can't distribute CPUs in this chunk size.
872
+ // PreferAlignByUncoreCache feature not implemented here yet and set to false.
873
+ // Support for PreferAlignByUncoreCache to be done at beta release.
772
874
if (numCPUs % cpuGroupSize ) != 0 {
773
- return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy )
875
+ return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy , false )
774
876
}
775
877
776
878
// Otherwise build an accumulator to start allocating CPUs from.
@@ -953,7 +1055,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
953
1055
// size 'cpuGroupSize' from 'bestCombo'.
954
1056
distribution := (numCPUs / len (bestCombo ) / cpuGroupSize ) * cpuGroupSize
955
1057
for _ , numa := range bestCombo {
956
- cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), distribution , cpuSortingStrategy )
1058
+ cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), distribution , cpuSortingStrategy , false )
957
1059
acc .take (cpus )
958
1060
}
959
1061
@@ -968,7 +1070,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
968
1070
if acc .details .CPUsInNUMANodes (numa ).Size () < cpuGroupSize {
969
1071
continue
970
1072
}
971
- cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), cpuGroupSize , cpuSortingStrategy )
1073
+ cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), cpuGroupSize , cpuSortingStrategy , false )
972
1074
acc .take (cpus )
973
1075
remainder -= cpuGroupSize
974
1076
}
@@ -992,5 +1094,5 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
992
1094
993
1095
// If we never found a combination of NUMA nodes that we could properly
994
1096
// distribute CPUs across, fall back to the packing algorithm.
995
- return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy )
1097
+ return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy , false )
996
1098
}
0 commit comments