@@ -95,10 +95,8 @@ func standardDeviation(xs []int) float64 {
95
95
type numaOrSocketsFirstFuncs interface {
96
96
takeFullFirstLevel ()
97
97
takeFullSecondLevel ()
98
- takeThirdLevel ()
99
98
sortAvailableNUMANodes () []int
100
99
sortAvailableSockets () []int
101
- sortAvailableUncoreCaches () []int
102
100
sortAvailableCores () []int
103
101
}
104
102
@@ -120,18 +118,12 @@ func (n *numaFirst) takeFullSecondLevel() {
120
118
n .acc .takeFullSockets ()
121
119
}
122
120
123
- // In Split UncoreCache Topology, we take from the sets of UncoreCache as the third level
124
- func (n * numaFirst ) takeThirdLevel () {
125
- n .acc .takeUncoreCache ()
126
- }
127
-
128
- // If NUMA nodes are higher in the memory hierarchy than sockets, then just
129
- // sort the NUMA nodes directly, and return them.
130
- func (n * numaFirst ) sortAvailableUncoreCaches () []int {
121
+ // Sort the UncoreCaches within the NUMA nodes.
122
+ func (a * cpuAccumulator ) sortAvailableUncoreCaches () []int {
131
123
var result []int
132
- for _ , socket := range n . acc .sortAvailableNUMANodes () {
133
- uncore := n . acc . details .UncoreInNUMANodes (socket ).UnsortedList ()
134
- n . acc . sort (uncore , n . acc .details .CPUsInUncoreCaches )
124
+ for _ , numa := range a .sortAvailableNUMANodes () {
125
+ uncore := a . details .UncoreInNUMANodes (numa ).UnsortedList ()
126
+ a . sort (uncore , a .details .CPUsInUncoreCaches )
135
127
result = append (result , uncore ... )
136
128
}
137
129
return result
@@ -182,10 +174,6 @@ func (s *socketsFirst) takeFullSecondLevel() {
182
174
s .acc .takeFullNUMANodes ()
183
175
}
184
176
185
- func (s * socketsFirst ) takeThirdLevel () {
186
- s .acc .takeUncoreCache ()
187
- }
188
-
189
177
// If sockets are higher in the memory hierarchy than NUMA nodes, then we need
190
178
// to pull the set of NUMA nodes out of each sorted Socket, and accumulate the
191
179
// partial order across them.
@@ -207,18 +195,6 @@ func (s *socketsFirst) sortAvailableSockets() []int {
207
195
return sockets
208
196
}
209
197
210
- // If sockets higher in the memory hierarchy than NUMA nodes, then UncoreCache
211
- // sit directly below NUMA Nodes in the memory hierchy
212
- func (s * socketsFirst ) sortAvailableUncoreCaches () []int {
213
- var result []int
214
- for _ , uncore := range s .acc .sortAvailableNUMANodes () {
215
- uncore := s .acc .details .UncoreInNUMANodes (uncore ).UnsortedList ()
216
- s .acc .sort (uncore , s .acc .details .CPUsInUncoreCaches )
217
- result = append (result , uncore ... )
218
- }
219
- return result
220
- }
221
-
222
198
// If sockets are higher in the memory hierarchy than NUMA nodes, then cores
223
199
// sit directly below NUMA Nodes in the memory hierarchy.
224
200
func (s * socketsFirst ) sortAvailableCores () []int {
@@ -353,8 +329,8 @@ func (a *cpuAccumulator) isSocketFree(socketID int) bool {
353
329
return a .details .CPUsInSockets (socketID ).Size () == a .topo .CPUsPerSocket ()
354
330
}
355
331
356
- // Returns true if the supplied UnCoreCache is fully available in `a.details`.
357
- // "fully available" means that all the CPUs in it are free.
332
+ // Returns true if the supplied UnCoreCache is fully available,
333
+ // meaning all its CPUs are free in `a.details` .
358
334
func (a * cpuAccumulator ) isUncoreCacheFree (uncoreID int ) bool {
359
335
return a .details .CPUsInUncoreCaches (uncoreID ).Size () == a .topo .CPUDetails .CPUsInUncoreCaches (uncoreID ).Size ()
360
336
}
@@ -390,19 +366,14 @@ func (a *cpuAccumulator) freeSockets() []int {
390
366
// Returns free UncoreCache IDs as a slice sorted by sortAvailableUnCoreCache().
391
367
func (a * cpuAccumulator ) freeUncoreCache () []int {
392
368
free := []int {}
393
- for _ , uncore := range a .numaOrSocketsFirst . sortAvailableUncoreCaches () {
369
+ for _ , uncore := range a .sortAvailableUncoreCaches () {
394
370
if a .isUncoreCacheFree (uncore ) {
395
371
free = append (free , uncore )
396
372
}
397
373
}
398
374
return free
399
375
}
400
376
401
- // Returns all UncoreCache IDs as a slice sorted by sortAvailableUncoreCache().
402
- func (a * cpuAccumulator ) allUncoreCache () []int {
403
- return a .numaOrSocketsFirst .sortAvailableUncoreCaches ()
404
- }
405
-
406
377
// Returns free core IDs as a slice sorted by sortAvailableCores().
407
378
func (a * cpuAccumulator ) freeCores () []int {
408
379
free := []int {}
@@ -575,7 +546,7 @@ func (a *cpuAccumulator) takeFullSockets() {
575
546
a .take (cpusInSocket )
576
547
}
577
548
}
578
- func (a * cpuAccumulator ) takeFullUnCore () {
549
+ func (a * cpuAccumulator ) takeFullUncore () {
579
550
for _ , uncore := range a .freeUncoreCache () {
580
551
cpusInUncore := a .topo .CPUDetails .CPUsInUncoreCaches (uncore )
581
552
if ! a .needsAtLeast (cpusInUncore .Size ()) {
@@ -585,30 +556,34 @@ func (a *cpuAccumulator) takeFullUnCore() {
585
556
}
586
557
}
587
558
559
+ func (a * cpuAccumulator ) takePartialUncore (uncoreID int ) {
560
+ numCoresNeeded := a .numCPUsNeeded / a .topo .CPUsPerCore ()
561
+ var freeCPUsInUncoreCache cpuset.CPUSet
562
+ freeCoresInUncoreCache := a .details .CoresNeededInUncoreCache (numCoresNeeded , uncoreID )
563
+ for _ , coreID := range freeCoresInUncoreCache .List () {
564
+ freeCPUsInUncoreCache = freeCPUsInUncoreCache .Union (a .topo .CPUDetails .CPUsInCores (coreID ))
565
+ }
566
+ klog .V (4 ).InfoS ("freeCPUsInUncorecache : " , "freeCPUsInUncorecache" , freeCPUsInUncoreCache .String (), "freeCPUsInUnCoreCache" , freeCPUsInUncoreCache .String ())
567
+ if a .numCPUsNeeded == freeCPUsInUncoreCache .Size () {
568
+ a .take (freeCPUsInUncoreCache )
569
+ }
570
+ }
571
+
588
572
// First try to take full UncoreCache, if available and need is at least the size of the UncoreCache group.
589
573
// Second try to take the partial UncoreCache if available and the request size can fit w/in the UncoreCache.
590
574
func (a * cpuAccumulator ) takeUncoreCache () {
591
- for _ , uncore := range a .allUncoreCache () {
592
- numCoresNeeded := a .numCPUsNeeded / a .topo .CPUsPerCore ()
593
-
594
- // take full UncoreCache if the CPUs needed is greater a UncoreCache size
595
- if a .numCPUsNeeded >= a .topo .NumCPUs / a .topo .NumUncoreCache {
596
- a .takeFullUnCore ()
575
+ cpusPerUncoreCache := a .topo .NumCPUs / a .topo .NumUncoreCache
576
+ for _ , uncore := range a .sortAvailableUncoreCaches () {
577
+ // take full UncoreCache if the CPUs needed is greater than free UncoreCache size
578
+ if a .needsAtLeast (cpusPerUncoreCache ) {
579
+ a .takeFullUncore ()
597
580
}
598
581
599
- var freeCPUsInUncoreCache cpuset.CPUSet
600
- // need to get needed cores in UncoreCache
601
- freeCoresInUncoreCache := a .details .CoresNeededInUncoreCache (numCoresNeeded , uncore )
602
- klog .V (2 ).InfoS ("free cores from a.details list: " , "freeCoresInUncorecache" , freeCoresInUncoreCache )
603
- for _ , coreID := range freeCoresInUncoreCache .List () {
604
- freeCPUsInUncoreCache = freeCPUsInUncoreCache .Union (a .topo .CPUDetails .CPUsInCores (coreID ))
605
- }
606
- klog .V (2 ).InfoS ("freeCPUsInUncorecache : " , "freeCPUsInUncorecache" , freeCPUsInUncoreCache )
607
- if a .numCPUsNeeded == freeCPUsInUncoreCache .Size () {
608
- klog .V (4 ).InfoS ("takePartialUncore: claiming cores from Uncorecache ID" , "uncore" , uncore )
609
- a .take (freeCPUsInUncoreCache )
582
+ if a .isSatisfied () {
583
+ return
610
584
}
611
585
586
+ a .takePartialUncore (uncore )
612
587
if a .isSatisfied () {
613
588
return
614
589
}
@@ -733,6 +708,14 @@ func (a *cpuAccumulator) iterateCombinations(n []int, k int, f func([]int) LoopC
733
708
// or the remaining number of CPUs to take after having taken full sockets and NUMA nodes is less
734
709
// than a whole NUMA node, the function tries to take whole physical cores (cores).
735
710
//
711
+ // If `PreferAlignByUncoreCache` is enabled, the function will try to optimally assign Uncorecaches.
712
+ // If `numCPUs` is larger than or equal to the total number of CPUs in a Uncorecache, and there are
713
+ // free (i.e. all CPUs within the Uncorecache are free) Uncorecaches, the function takes as many entire
714
+ // cores from free Uncorecaches as possible. If/Once `numCPUs` is smaller than the total number of
715
+ // CPUs in a free Uncorecache, the function scans each Uncorecache index in numerical order to assign
716
+ // cores that will fit within the Uncorecache. If `numCPUs` cannot fit within any Uncorecache, the
717
+ // function tries to take whole physical cores.
718
+ //
736
719
// If `numCPUs` is bigger than the total number of CPUs in a core, and there are
737
720
// free (i.e. all CPUs in them are free) cores, the function takes as many entire free cores as possible.
738
721
// The cores are taken from one socket at a time, and the sockets are considered by
@@ -754,7 +737,7 @@ func (a *cpuAccumulator) iterateCombinations(n []int, k int, f func([]int) LoopC
754
737
// the least amount of free CPUs to the one with the highest amount of free CPUs (i.e. in ascending
755
738
// order of free CPUs). For any NUMA node, the cores are selected from the ones in the socket with
756
739
// the least amount of free CPUs to the one with the highest amount of free CPUs.
757
- func takeByTopologyNUMAPacked (topo * topology.CPUTopology , availableCPUs cpuset.CPUSet , numCPUs int , cpuSortingStrategy CPUSortingStrategy ) (cpuset.CPUSet , error ) {
740
+ func takeByTopologyNUMAPacked (topo * topology.CPUTopology , availableCPUs cpuset.CPUSet , numCPUs int , cpuSortingStrategy CPUSortingStrategy , preferAlignByUncoreCache bool ) (cpuset.CPUSet , error ) {
758
741
acc := newCPUAccumulator (topo , availableCPUs , numCPUs , cpuSortingStrategy )
759
742
if acc .isSatisfied () {
760
743
return acc .result , nil
@@ -777,59 +760,16 @@ func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.C
777
760
return acc .result , nil
778
761
}
779
762
780
- // 2. Acquire whole cores, if available and the container requires at least
781
- // a core 's-worth of CPUs.
782
- // If `CPUSortingStrategySpread` is specified, skip taking the whole core .
783
- if cpuSortingStrategy != CPUSortingStrategySpread {
784
- acc .takeFullCores ()
763
+ // 2. If PreferAlignByUncoreCache is enabled, acquire whole UncoreCaches
764
+ // if available and the container requires at least a UncoreCache 's-worth
765
+ // of CPUs. Otherwise, acquire CPUs from the least amount of UncoreCaches .
766
+ if preferAlignByUncoreCache {
767
+ acc .takeUncoreCache ()
785
768
if acc .isSatisfied () {
786
769
return acc .result , nil
787
770
}
788
771
}
789
772
790
- // 3. Acquire single threads, preferring to fill partially-allocated cores
791
- // on the same sockets as the whole cores we have already taken in this
792
- // allocation.
793
- acc .takeRemainingCPUs ()
794
- if acc .isSatisfied () {
795
- return acc .result , nil
796
- }
797
-
798
- return cpuset .New (), fmt .Errorf ("failed to allocate cpus" )
799
- }
800
-
801
- // takeByTopologyUnCoreCachePacked uses the "packed" sorting strategy similar to takeByTopologyNUMAPacked.
802
- // It includes an additional level of sorting by uncorecache
803
- func takeByTopologyUncoreCachePacked (topo * topology.CPUTopology , availableCPUs cpuset.CPUSet , numCPUs int , cpuSortingStrategy CPUSortingStrategy ) (cpuset.CPUSet , error ) {
804
- acc := newCPUAccumulator (topo , availableCPUs , numCPUs , cpuSortingStrategy )
805
- if acc .isSatisfied () {
806
- return acc .result , nil
807
- }
808
- if acc .isFailed () {
809
- return cpuset .New (), fmt .Errorf ("not enough cpus available to satisfy request: requested=%d, available=%d" , numCPUs , availableCPUs .Size ())
810
- }
811
-
812
- // Algorithm: topology-aware best-fit
813
- // 1. Acquire whole NUMA nodes and sockets, if available and the container
814
- // requires at least a NUMA node or socket's-worth of CPUs. If NUMA
815
- // Nodes map to 1 or more sockets, pull from NUMA nodes first.
816
- // Otherwise pull from sockets first.
817
- acc .numaOrSocketsFirst .takeFullFirstLevel ()
818
- if acc .isSatisfied () {
819
- return acc .result , nil
820
- }
821
- acc .numaOrSocketsFirst .takeFullSecondLevel ()
822
- if acc .isSatisfied () {
823
- return acc .result , nil
824
- }
825
-
826
- // 2. Acquire partial uncorecache, if there are enough CPUs available to satisfy the container requirement
827
- // Acquire the full uncorecache, if available and the container requires at least all the CPUs in the uncorecache grouping
828
- acc .numaOrSocketsFirst .takeThirdLevel ()
829
- if acc .isSatisfied () {
830
- return acc .result , nil
831
- }
832
-
833
773
// 3. Acquire whole cores, if available and the container requires at least
834
774
// a core's-worth of CPUs.
835
775
// If `CPUSortingStrategySpread` is specified, skip taking the whole core.
@@ -918,8 +858,10 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
918
858
// If the number of CPUs requested cannot be handed out in chunks of
919
859
// 'cpuGroupSize', then we just call out the packing algorithm since we
920
860
// can't distribute CPUs in this chunk size.
861
+ // PreferAlignByUncoreCache feature not implemented here yet and set to false.
862
+ // Support for PreferAlignByUncoreCache to be done at beta release.
921
863
if (numCPUs % cpuGroupSize ) != 0 {
922
- return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy )
864
+ return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy , false )
923
865
}
924
866
925
867
// Otherwise build an accumulator to start allocating CPUs from.
@@ -1102,7 +1044,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
1102
1044
// size 'cpuGroupSize' from 'bestCombo'.
1103
1045
distribution := (numCPUs / len (bestCombo ) / cpuGroupSize ) * cpuGroupSize
1104
1046
for _ , numa := range bestCombo {
1105
- cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), distribution , cpuSortingStrategy )
1047
+ cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), distribution , cpuSortingStrategy , false )
1106
1048
acc .take (cpus )
1107
1049
}
1108
1050
@@ -1117,7 +1059,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
1117
1059
if acc .details .CPUsInNUMANodes (numa ).Size () < cpuGroupSize {
1118
1060
continue
1119
1061
}
1120
- cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), cpuGroupSize , cpuSortingStrategy )
1062
+ cpus , _ := takeByTopologyNUMAPacked (acc .topo , acc .details .CPUsInNUMANodes (numa ), cpuGroupSize , cpuSortingStrategy , false )
1121
1063
acc .take (cpus )
1122
1064
remainder -= cpuGroupSize
1123
1065
}
@@ -1141,5 +1083,5 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu
1141
1083
1142
1084
// If we never found a combination of NUMA nodes that we could properly
1143
1085
// distribute CPUs across, fall back to the packing algorithm.
1144
- return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy )
1086
+ return takeByTopologyNUMAPacked (topo , availableCPUs , numCPUs , cpuSortingStrategy , false )
1145
1087
}
0 commit comments