@@ -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,62 @@ 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
+ // determine the N number of free cores (physical cpus) within the UncoreCache, then
565
+ // determine the M number of free cpus (virtual cpus) that correspond with the free cores
566
+ freeCores := a .details .CoresNeededInUncoreCache (numCoresNeeded , uncoreID )
567
+ freeCPUs := a .details .CPUsInCores (freeCores .UnsortedList ()... )
568
+
569
+ // claim the cpus if the free cpus within the UncoreCache can satisfy the needed cpus
570
+ claimed := (a .numCPUsNeeded == freeCPUs .Size ())
571
+ klog .V (4 ).InfoS ("takePartialUncore: trying to claim partial uncore" ,
572
+ "uncore" , uncoreID ,
573
+ "claimed" , claimed ,
574
+ "needed" , a .numCPUsNeeded ,
575
+ "cores" , freeCores .String (),
576
+ "cpus" , freeCPUs .String ())
577
+ if ! claimed {
578
+ return
579
+
580
+ }
581
+ a .take (freeCPUs )
582
+ }
583
+
584
+ // First try to take full UncoreCache, if available and need is at least the size of the UncoreCache group.
585
+ // Second try to take the partial UncoreCache if available and the request size can fit w/in the UncoreCache.
586
+ func (a * cpuAccumulator ) takeUncoreCache () {
587
+ numCPUsInUncore := a .topo .CPUsPerUncore ()
588
+ for _ , uncore := range a .sortAvailableUncoreCaches () {
589
+ // take full UncoreCache if the CPUs needed is greater than free UncoreCache size
590
+ if a .needsAtLeast (numCPUsInUncore ) {
591
+ a .takeFullUncore ()
592
+ }
593
+
594
+ if a .isSatisfied () {
595
+ return
596
+ }
597
+
598
+ // take partial UncoreCache if the CPUs needed is less than free UncoreCache size
599
+ a .takePartialUncore (uncore )
600
+ if a .isSatisfied () {
601
+ return
602
+ }
603
+ }
604
+ }
605
+
522
606
func (a * cpuAccumulator ) takeFullCores () {
523
607
for _ , core := range a .freeCores () {
524
608
cpusInCore := a .topo .CPUDetails .CPUsInCores (core )
@@ -637,6 +721,14 @@ func (a *cpuAccumulator) iterateCombinations(n []int, k int, f func([]int) LoopC
637
721
// or the remaining number of CPUs to take after having taken full sockets and NUMA nodes is less
638
722
// than a whole NUMA node, the function tries to take whole physical cores (cores).
639
723
//
724
+ // If `PreferAlignByUncoreCache` is enabled, the function will try to optimally assign Uncorecaches.
725
+ // If `numCPUs` is larger than or equal to the total number of CPUs in a Uncorecache, and there are
726
+ // free (i.e. all CPUs within the Uncorecache are free) Uncorecaches, the function takes as many entire
727
+ // cores from free Uncorecaches as possible. If/Once `numCPUs` is smaller than the total number of
728
+ // CPUs in a free Uncorecache, the function scans each Uncorecache index in numerical order to assign
729
+ // cores that will fit within the Uncorecache. If `numCPUs` cannot fit within any Uncorecache, the
730
+ // function tries to take whole physical cores.
731
+ //
640
732
// If `numCPUs` is bigger than the total number of CPUs in a core, and there are
641
733
// free (i.e. all CPUs in them are free) cores, the function takes as many entire free cores as possible.
642
734
// The cores are taken from one socket at a time, and the sockets are considered by
@@ -658,7 +750,7 @@ func (a *cpuAccumulator) iterateCombinations(n []int, k int, f func([]int) LoopC
658
750
// the least amount of free CPUs to the one with the highest amount of free CPUs (i.e. in ascending
659
751
// order of free CPUs). For any NUMA node, the cores are selected from the ones in the socket with
660
752
// 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 ) {
753
+ func takeByTopologyNUMAPacked (topo * topology.CPUTopology , availableCPUs cpuset.CPUSet , numCPUs int , cpuSortingStrategy CPUSortingStrategy , preferAlignByUncoreCache bool ) (cpuset.CPUSet , error ) {
662
754
acc := newCPUAccumulator (topo , availableCPUs , numCPUs , cpuSortingStrategy )
663
755
if acc .isSatisfied () {
664
756
return acc .result , nil
@@ -681,7 +773,17 @@ func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.C
681
773
return acc .result , nil
682
774
}
683
775
684
- // 2. Acquire whole cores, if available and the container requires at least
776
+ // 2. If PreferAlignByUncoreCache is enabled, acquire whole UncoreCaches
777
+ // if available and the container requires at least a UncoreCache's-worth
778
+ // of CPUs. Otherwise, acquire CPUs from the least amount of UncoreCaches.
779
+ if preferAlignByUncoreCache {
780
+ acc .takeUncoreCache ()
781
+ if acc .isSatisfied () {
782
+ return acc .result , nil
783
+ }
784
+ }
785
+
786
+ // 3. Acquire whole cores, if available and the container requires at least
685
787
// a core's-worth of CPUs.
686
788
// If `CPUSortingStrategySpread` is specified, skip taking the whole core.
687
789
if cpuSortingStrategy != CPUSortingStrategySpread {
@@ -691,7 +793,7 @@ func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.C
691
793
}
692
794
}
693
795
694
- // 3 . Acquire single threads, preferring to fill partially-allocated cores
796
+ // 4 . Acquire single threads, preferring to fill partially-allocated cores
695
797
// on the same sockets as the whole cores we have already taken in this
696
798
// allocation.
697
799
acc .takeRemainingCPUs ()
@@ -769,8 +871,10 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
769
871
// If the number of CPUs requested cannot be handed out in chunks of
770
872
// 'cpuGroupSize', then we just call out the packing algorithm since we
771
873
// can't distribute CPUs in this chunk size.
874
+ // PreferAlignByUncoreCache feature not implemented here yet and set to false.
875
+ // Support for PreferAlignByUncoreCache to be done at beta release.
772
876
if (numCPUs % cpuGroupSize ) != 0 {
773
- return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy )
877
+ return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy , false )
774
878
}
775
879
776
880
// Otherwise build an accumulator to start allocating CPUs from.
@@ -953,7 +1057,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
953
1057
// size 'cpuGroupSize' from 'bestCombo'.
954
1058
distribution := (numCPUs / len (bestCombo ) / cpuGroupSize ) * cpuGroupSize
955
1059
for _ , numa := range bestCombo {
956
- cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), distribution , cpuSortingStrategy )
1060
+ cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), distribution , cpuSortingStrategy , false )
957
1061
acc .take (cpus )
958
1062
}
959
1063
@@ -968,7 +1072,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
968
1072
if acc .details .CPUsInNUMANodes (numa ).Size () < cpuGroupSize {
969
1073
continue
970
1074
}
971
- cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), cpuGroupSize , cpuSortingStrategy )
1075
+ cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), cpuGroupSize , cpuSortingStrategy , false )
972
1076
acc .take (cpus )
973
1077
remainder -= cpuGroupSize
974
1078
}
@@ -992,5 +1096,5 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
992
1096
993
1097
// If we never found a combination of NUMA nodes that we could properly
994
1098
// distribute CPUs across, fall back to the packing algorithm.
995
- return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy )
1099
+ return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy , false )
996
1100
}
0 commit comments