From 691924b19f072261e84c5f0587a640e20feed21c Mon Sep 17 00:00:00 2001 From: sphrasavath Date: Mon, 19 Aug 2024 20:04:07 +0000 Subject: [PATCH 1/3] UPSTREAM: 126750: Add takeByTopologyUnCoreCachePacked if policy option align-cpus-by-uncorecache is enabled. Adding new function to evaluate uncore cache id. Reverse allocation logic. Implement preferAlignByUncorecache within TakeByTopologyNUMAPacked, along with new test cases. (cherry picked from commit 3f459f20d6cccfe97a20eb2dafd9d608410e7610) --- pkg/kubelet/cm/cpumanager/cpu_assignment.go | 116 +- .../cm/cpumanager/cpu_assignment_test.go | 77 +- pkg/kubelet/cm/cpumanager/cpu_manager_test.go | 20 +- pkg/kubelet/cm/cpumanager/policy_options.go | 20 +- pkg/kubelet/cm/cpumanager/policy_static.go | 3 +- pkg/kubelet/cm/cpumanager/policy_test.go | 107 +- .../cm/cpumanager/topology/topology.go | 119 +- .../cm/cpumanager/topology/topology_test.go | 1140 ++++++++++++----- .../cm/cpumanager/topology_hints_test.go | 12 +- 9 files changed, 1263 insertions(+), 351 deletions(-) diff --git a/pkg/kubelet/cm/cpumanager/cpu_assignment.go b/pkg/kubelet/cm/cpumanager/cpu_assignment.go index 08622568ab33f..180250955023c 100644 --- a/pkg/kubelet/cm/cpumanager/cpu_assignment.go +++ b/pkg/kubelet/cm/cpumanager/cpu_assignment.go @@ -118,6 +118,17 @@ func (n *numaFirst) takeFullSecondLevel() { n.acc.takeFullSockets() } +// Sort the UncoreCaches within the NUMA nodes. +func (a *cpuAccumulator) sortAvailableUncoreCaches() []int { + var result []int + for _, numa := range a.sortAvailableNUMANodes() { + uncore := a.details.UncoreInNUMANodes(numa).UnsortedList() + a.sort(uncore, a.details.CPUsInUncoreCaches) + result = append(result, uncore...) + } + return result +} + // If NUMA nodes are higher in the memory hierarchy than sockets, then just // sort the NUMA nodes directly, and return them. func (n *numaFirst) sortAvailableNUMANodes() []int { @@ -318,6 +329,12 @@ func (a *cpuAccumulator) isSocketFree(socketID int) bool { return a.details.CPUsInSockets(socketID).Size() == a.topo.CPUsPerSocket() } +// Returns true if the supplied UnCoreCache is fully available, +// "fully available" means that all the CPUs in it are free. +func (a *cpuAccumulator) isUncoreCacheFree(uncoreID int) bool { + return a.details.CPUsInUncoreCaches(uncoreID).Size() == a.topo.CPUDetails.CPUsInUncoreCaches(uncoreID).Size() +} + // Returns true if the supplied core is fully available in `a.details`. // "fully available" means that all the CPUs in it are free. func (a *cpuAccumulator) isCoreFree(coreID int) bool { @@ -346,6 +363,17 @@ func (a *cpuAccumulator) freeSockets() []int { return free } +// Returns free UncoreCache IDs as a slice sorted by sortAvailableUnCoreCache(). +func (a *cpuAccumulator) freeUncoreCache() []int { + free := []int{} + for _, uncore := range a.sortAvailableUncoreCaches() { + if a.isUncoreCacheFree(uncore) { + free = append(free, uncore) + } + } + return free +} + // Returns free core IDs as a slice sorted by sortAvailableCores(). func (a *cpuAccumulator) freeCores() []int { free := []int{} @@ -519,6 +547,60 @@ func (a *cpuAccumulator) takeFullSockets() { } } +func (a *cpuAccumulator) takeFullUncore() { + for _, uncore := range a.freeUncoreCache() { + cpusInUncore := a.topo.CPUDetails.CPUsInUncoreCaches(uncore) + if !a.needsAtLeast(cpusInUncore.Size()) { + continue + } + klog.V(4).InfoS("takeFullUncore: claiming uncore", "uncore", uncore) + a.take(cpusInUncore) + } +} + +func (a *cpuAccumulator) takePartialUncore(uncoreID int) { + numCoresNeeded := a.numCPUsNeeded / a.topo.CPUsPerCore() + + // note: we need to keep the first N free cores (physical cpus) and only we we got these expand to their + // cpus (virtual cpus). Taking directly the first M cpus (virtual cpus) leads to suboptimal allocation + freeCores := a.details.CoresNeededInUncoreCache(numCoresNeeded, uncoreID) + freeCPUs := a.details.CPUsInCores(freeCores.UnsortedList()...) + + claimed := (a.numCPUsNeeded == freeCPUs.Size()) + klog.V(4).InfoS("takePartialUncore: trying to claim partial uncore", + "uncore", uncoreID, + "claimed", claimed, + "needed", a.numCPUsNeeded, + "cores", freeCores.String(), + "cpus", freeCPUs.String()) + if !claimed { + return + + } + a.take(freeCPUs) +} + +// First try to take full UncoreCache, if available and need is at least the size of the UncoreCache group. +// Second try to take the partial UncoreCache if available and the request size can fit w/in the UncoreCache. +func (a *cpuAccumulator) takeUncoreCache() { + numCPUsInUncore := a.topo.CPUsPerUncore() + for _, uncore := range a.sortAvailableUncoreCaches() { + // take full UncoreCache if the CPUs needed is greater than free UncoreCache size + if a.needsAtLeast(numCPUsInUncore) { + a.takeFullUncore() + } + + if a.isSatisfied() { + return + } + + a.takePartialUncore(uncore) + if a.isSatisfied() { + return + } + } +} + func (a *cpuAccumulator) takeFullCores() { for _, core := range a.freeCores() { cpusInCore := a.topo.CPUDetails.CPUsInCores(core) @@ -637,6 +719,14 @@ func (a *cpuAccumulator) iterateCombinations(n []int, k int, f func([]int) LoopC // or the remaining number of CPUs to take after having taken full sockets and NUMA nodes is less // than a whole NUMA node, the function tries to take whole physical cores (cores). // +// If `PreferAlignByUncoreCache` is enabled, the function will try to optimally assign Uncorecaches. +// If `numCPUs` is larger than or equal to the total number of CPUs in a Uncorecache, and there are +// free (i.e. all CPUs within the Uncorecache are free) Uncorecaches, the function takes as many entire +// cores from free Uncorecaches as possible. If/Once `numCPUs` is smaller than the total number of +// CPUs in a free Uncorecache, the function scans each Uncorecache index in numerical order to assign +// cores that will fit within the Uncorecache. If `numCPUs` cannot fit within any Uncorecache, the +// function tries to take whole physical cores. +// // If `numCPUs` is bigger than the total number of CPUs in a core, and there are // free (i.e. all CPUs in them are free) cores, the function takes as many entire free cores as possible. // 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 // the least amount of free CPUs to the one with the highest amount of free CPUs (i.e. in ascending // order of free CPUs). For any NUMA node, the cores are selected from the ones in the socket with // the least amount of free CPUs to the one with the highest amount of free CPUs. -func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.CPUSet, numCPUs int, cpuSortingStrategy CPUSortingStrategy) (cpuset.CPUSet, error) { +func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.CPUSet, numCPUs int, cpuSortingStrategy CPUSortingStrategy, preferAlignByUncoreCache bool) (cpuset.CPUSet, error) { acc := newCPUAccumulator(topo, availableCPUs, numCPUs, cpuSortingStrategy) if acc.isSatisfied() { return acc.result, nil @@ -681,7 +771,17 @@ func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.C return acc.result, nil } - // 2. Acquire whole cores, if available and the container requires at least + // 2. If PreferAlignByUncoreCache is enabled, acquire whole UncoreCaches + // if available and the container requires at least a UncoreCache's-worth + // of CPUs. Otherwise, acquire CPUs from the least amount of UncoreCaches. + if preferAlignByUncoreCache { + acc.takeUncoreCache() + if acc.isSatisfied() { + return acc.result, nil + } + } + + // 3. Acquire whole cores, if available and the container requires at least // a core's-worth of CPUs. // If `CPUSortingStrategySpread` is specified, skip taking the whole core. if cpuSortingStrategy != CPUSortingStrategySpread { @@ -691,7 +791,7 @@ func takeByTopologyNUMAPacked(topo *topology.CPUTopology, availableCPUs cpuset.C } } - // 3. Acquire single threads, preferring to fill partially-allocated cores + // 4. Acquire single threads, preferring to fill partially-allocated cores // on the same sockets as the whole cores we have already taken in this // allocation. acc.takeRemainingCPUs() @@ -769,8 +869,10 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu // If the number of CPUs requested cannot be handed out in chunks of // 'cpuGroupSize', then we just call out the packing algorithm since we // can't distribute CPUs in this chunk size. + // PreferAlignByUncoreCache feature not implemented here yet and set to false. + // Support for PreferAlignByUncoreCache to be done at beta release. if (numCPUs % cpuGroupSize) != 0 { - return takeByTopologyNUMAPacked(topo, availableCPUs, numCPUs, cpuSortingStrategy) + return takeByTopologyNUMAPacked(topo, availableCPUs, numCPUs, cpuSortingStrategy, false) } // Otherwise build an accumulator to start allocating CPUs from. @@ -953,7 +1055,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu // size 'cpuGroupSize' from 'bestCombo'. distribution := (numCPUs / len(bestCombo) / cpuGroupSize) * cpuGroupSize for _, numa := range bestCombo { - cpus, _ := takeByTopologyNUMAPacked(acc.topo, acc.details.CPUsInNUMANodes(numa), distribution, cpuSortingStrategy) + cpus, _ := takeByTopologyNUMAPacked(acc.topo, acc.details.CPUsInNUMANodes(numa), distribution, cpuSortingStrategy, false) acc.take(cpus) } @@ -968,7 +1070,7 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu if acc.details.CPUsInNUMANodes(numa).Size() < cpuGroupSize { continue } - cpus, _ := takeByTopologyNUMAPacked(acc.topo, acc.details.CPUsInNUMANodes(numa), cpuGroupSize, cpuSortingStrategy) + cpus, _ := takeByTopologyNUMAPacked(acc.topo, acc.details.CPUsInNUMANodes(numa), cpuGroupSize, cpuSortingStrategy, false) acc.take(cpus) remainder -= cpuGroupSize } @@ -992,5 +1094,5 @@ func takeByTopologyNUMADistributed(topo *topology.CPUTopology, availableCPUs cpu // If we never found a combination of NUMA nodes that we could properly // distribute CPUs across, fall back to the packing algorithm. - return takeByTopologyNUMAPacked(topo, availableCPUs, numCPUs, cpuSortingStrategy) + return takeByTopologyNUMAPacked(topo, availableCPUs, numCPUs, cpuSortingStrategy, false) } diff --git a/pkg/kubelet/cm/cpumanager/cpu_assignment_test.go b/pkg/kubelet/cm/cpumanager/cpu_assignment_test.go index 961f55e465b7f..241bfe611b359 100644 --- a/pkg/kubelet/cm/cpumanager/cpu_assignment_test.go +++ b/pkg/kubelet/cm/cpumanager/cpu_assignment_test.go @@ -668,6 +668,79 @@ func TestTakeByTopologyNUMAPacked(t *testing.T) { "", mustParseCPUSet(t, "0-29,40-69,30,31,70,71"), }, + // Test cases for PreferAlignByUncoreCache + { + "take cpus from two full UncoreCaches and partial from a single UncoreCache", + topoUncoreSingleSocketNoSMT, + StaticPolicyOptions{PreferAlignByUncoreCacheOption: true}, + mustParseCPUSet(t, "1-15"), + 10, + "", + cpuset.New(1, 2, 4, 5, 6, 7, 8, 9, 10, 11), + }, + { + "take one cpu from dual socket with HT - core from Socket 0", + topoDualSocketHT, + StaticPolicyOptions{PreferAlignByUncoreCacheOption: true}, + cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), + 1, + "", + cpuset.New(2), + }, + { + "take first available UncoreCache from first socket", + topoUncoreDualSocketNoSMT, + StaticPolicyOptions{PreferAlignByUncoreCacheOption: true}, + mustParseCPUSet(t, "0-15"), + 4, + "", + cpuset.New(0, 1, 2, 3), + }, + { + "take all available UncoreCache from first socket", + topoUncoreDualSocketNoSMT, + StaticPolicyOptions{PreferAlignByUncoreCacheOption: true}, + mustParseCPUSet(t, "2-15"), + 6, + "", + cpuset.New(2, 3, 4, 5, 6, 7), + }, + { + "take first available UncoreCache from second socket", + topoUncoreDualSocketNoSMT, + StaticPolicyOptions{PreferAlignByUncoreCacheOption: true}, + mustParseCPUSet(t, "8-15"), + 4, + "", + cpuset.New(8, 9, 10, 11), + }, + { + "take first available UncoreCache from available NUMA", + topoUncoreSingleSocketMultiNuma, + StaticPolicyOptions{PreferAlignByUncoreCacheOption: true}, + mustParseCPUSet(t, "3,4-8,12"), + 2, + "", + cpuset.New(4, 5), + }, + { + "take cpus from best available UncoreCache group of multi uncore cache single socket - SMT enabled", + topoUncoreSingleSocketSMT, + StaticPolicyOptions{PreferAlignByUncoreCacheOption: true}, + mustParseCPUSet(t, "2-3,10-11,4-7,12-15"), + 6, + "", + cpuset.New(4, 5, 6, 12, 13, 14), + }, + { + "take cpus from multiple UncoreCache of single socket - SMT enabled", + topoUncoreSingleSocketSMT, + StaticPolicyOptions{PreferAlignByUncoreCacheOption: true}, + mustParseCPUSet(t, "1-7,9-15"), + 10, + "", + mustParseCPUSet(t, "4-7,12-15,1,9"), + }, }...) for _, tc := range testCases { @@ -677,7 +750,7 @@ func TestTakeByTopologyNUMAPacked(t *testing.T) { strategy = CPUSortingStrategySpread } - result, err := takeByTopologyNUMAPacked(tc.topo, tc.availableCPUs, tc.numCPUs, strategy) + result, err := takeByTopologyNUMAPacked(tc.topo, tc.availableCPUs, tc.numCPUs, strategy, tc.opts.PreferAlignByUncoreCacheOption) if tc.expErr != "" && err != nil && err.Error() != tc.expErr { t.Errorf("expected error to be [%v] but it was [%v]", tc.expErr, err) } @@ -778,7 +851,7 @@ func TestTakeByTopologyWithSpreadPhysicalCPUsPreferredOption(t *testing.T) { if tc.opts.DistributeCPUsAcrossCores { strategy = CPUSortingStrategySpread } - result, err := takeByTopologyNUMAPacked(tc.topo, tc.availableCPUs, tc.numCPUs, strategy) + result, err := takeByTopologyNUMAPacked(tc.topo, tc.availableCPUs, tc.numCPUs, strategy, tc.opts.PreferAlignByUncoreCacheOption) if tc.expErr != "" && err.Error() != tc.expErr { t.Errorf("testCase %q failed, expected error to be [%v] but it was [%v]", tc.description, tc.expErr, err) } diff --git a/pkg/kubelet/cm/cpumanager/cpu_manager_test.go b/pkg/kubelet/cm/cpumanager/cpu_manager_test.go index 0630032c51135..8b4fafb56133e 100644 --- a/pkg/kubelet/cm/cpumanager/cpu_manager_test.go +++ b/pkg/kubelet/cm/cpumanager/cpu_manager_test.go @@ -651,20 +651,24 @@ func TestCPUManagerGenerate(t *testing.T) { { Cores: []cadvisorapi.Core{ { - Id: 0, - Threads: []int{0}, + Id: 0, + Threads: []int{0}, + UncoreCaches: []cadvisorapi.Cache{{Id: 1}}, }, { - Id: 1, - Threads: []int{1}, + Id: 1, + Threads: []int{1}, + UncoreCaches: []cadvisorapi.Cache{{Id: 1}}, }, { - Id: 2, - Threads: []int{2}, + Id: 2, + Threads: []int{2}, + UncoreCaches: []cadvisorapi.Cache{{Id: 1}}, }, { - Id: 3, - Threads: []int{3}, + Id: 3, + Threads: []int{3}, + UncoreCaches: []cadvisorapi.Cache{{Id: 1}}, }, }, }, diff --git a/pkg/kubelet/cm/cpumanager/policy_options.go b/pkg/kubelet/cm/cpumanager/policy_options.go index ac6d15ed0d39d..58ad86d9dc680 100644 --- a/pkg/kubelet/cm/cpumanager/policy_options.go +++ b/pkg/kubelet/cm/cpumanager/policy_options.go @@ -33,6 +33,7 @@ const ( DistributeCPUsAcrossNUMAOption string = "distribute-cpus-across-numa" AlignBySocketOption string = "align-by-socket" DistributeCPUsAcrossCoresOption string = "distribute-cpus-across-cores" + PreferAlignByUnCoreCacheOption string = "prefer-align-cpus-by-uncorecache" ) var ( @@ -40,6 +41,7 @@ var ( DistributeCPUsAcrossNUMAOption, AlignBySocketOption, DistributeCPUsAcrossCoresOption, + PreferAlignByUnCoreCacheOption, ) betaOptions = sets.New[string]( FullPCPUsOnlyOption, @@ -86,6 +88,9 @@ type StaticPolicyOptions struct { // cpus (HT) on different physical core. // This is a preferred policy so do not throw error if they have to packed in one physical core. DistributeCPUsAcrossCores bool + // Flag that makes best-effort to align CPUs to a uncorecache boundary + // As long as there are CPUs available, pods will be admitted if the condition is not met. + PreferAlignByUncoreCacheOption bool } // NewStaticPolicyOptions creates a StaticPolicyOptions struct from the user configuration. @@ -121,7 +126,12 @@ func NewStaticPolicyOptions(policyOptions map[string]string) (StaticPolicyOption return opts, fmt.Errorf("bad value for option %q: %w", name, err) } opts.DistributeCPUsAcrossCores = optValue - + case PreferAlignByUnCoreCacheOption: + optValue, err := strconv.ParseBool(value) + if err != nil { + return opts, fmt.Errorf("bad value for option %q: %w", name, err) + } + opts.PreferAlignByUncoreCacheOption = optValue default: // this should never be reached, we already detect unknown options, // but we keep it as further safety. @@ -138,6 +148,14 @@ func NewStaticPolicyOptions(policyOptions map[string]string) (StaticPolicyOption return opts, fmt.Errorf("static policy options %s and %s can not be used at the same time", DistributeCPUsAcrossNUMAOption, DistributeCPUsAcrossCoresOption) } + if opts.PreferAlignByUncoreCacheOption && opts.DistributeCPUsAcrossCores { + return opts, fmt.Errorf("static policy options %s and %s can not be used at the same time", PreferAlignByUnCoreCacheOption, DistributeCPUsAcrossCoresOption) + } + + if opts.PreferAlignByUncoreCacheOption && opts.DistributeCPUsAcrossNUMA { + return opts, fmt.Errorf("static policy options %s and %s can not be used at the same time", PreferAlignByUnCoreCacheOption, DistributeCPUsAcrossNUMAOption) + } + return opts, nil } diff --git a/pkg/kubelet/cm/cpumanager/policy_static.go b/pkg/kubelet/cm/cpumanager/policy_static.go index 2d40c5cc2bf59..899c4dd7de5d8 100644 --- a/pkg/kubelet/cm/cpumanager/policy_static.go +++ b/pkg/kubelet/cm/cpumanager/policy_static.go @@ -510,7 +510,8 @@ func (p *staticPolicy) takeByTopology(availableCPUs cpuset.CPUSet, numCPUs int) } return takeByTopologyNUMADistributed(p.topology, availableCPUs, numCPUs, cpuGroupSize, cpuSortingStrategy) } - return takeByTopologyNUMAPacked(p.topology, availableCPUs, numCPUs, cpuSortingStrategy) + + return takeByTopologyNUMAPacked(p.topology, availableCPUs, numCPUs, cpuSortingStrategy, p.options.PreferAlignByUncoreCacheOption) } func (p *staticPolicy) GetTopologyHints(s state.State, pod *v1.Pod, container *v1.Container) map[string][]topologymanager.TopologyHint { diff --git a/pkg/kubelet/cm/cpumanager/policy_test.go b/pkg/kubelet/cm/cpumanager/policy_test.go index 02f0898063a09..dcdeada2f0e32 100644 --- a/pkg/kubelet/cm/cpumanager/policy_test.go +++ b/pkg/kubelet/cm/cpumanager/policy_test.go @@ -38,9 +38,10 @@ var ( } topoDualSocketHT = &topology.CPUTopology{ - NumCPUs: 12, - NumSockets: 2, - NumCores: 6, + NumCPUs: 12, + NumSockets: 2, + NumCores: 6, + NumUncoreCache: 1, CPUDetails: map[int]topology.CPUInfo{ 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, 1: {CoreID: 1, SocketID: 1, NUMANodeID: 1}, @@ -57,6 +58,106 @@ var ( }, } + topoUncoreDualSocketNoSMT = &topology.CPUTopology{ + NumCPUs: 16, + NumSockets: 2, + NumCores: 16, + NumUncoreCache: 4, + CPUDetails: map[int]topology.CPUInfo{ + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 8: {CoreID: 8, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 2}, + 9: {CoreID: 9, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 2}, + 10: {CoreID: 10, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 2}, + 11: {CoreID: 11, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 2}, + 12: {CoreID: 12, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 3}, + 13: {CoreID: 13, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 3}, + 14: {CoreID: 14, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 3}, + 15: {CoreID: 15, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 3}, + }, + } + + topoUncoreSingleSocketMultiNuma = &topology.CPUTopology{ + NumCPUs: 16, + NumSockets: 1, + NumCores: 16, + NumUncoreCache: 4, + CPUDetails: map[int]topology.CPUInfo{ + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 8: {CoreID: 8, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 2}, + 9: {CoreID: 9, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 2}, + 10: {CoreID: 10, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 2}, + 11: {CoreID: 11, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 2}, + 12: {CoreID: 12, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 13: {CoreID: 13, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 14: {CoreID: 14, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 15: {CoreID: 15, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + }, + } + + topoUncoreSingleSocketSMT = &topology.CPUTopology{ + NumCPUs: 16, + NumSockets: 1, + NumCores: 8, + NumUncoreCache: 2, + CPUDetails: map[int]topology.CPUInfo{ + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 8: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 9: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 10: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 11: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 12: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 13: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 14: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 15: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + }, + } + + topoUncoreSingleSocketNoSMT = &topology.CPUTopology{ + NumCPUs: 16, + NumSockets: 1, + NumCores: 16, + NumUncoreCache: 4, + CPUDetails: map[int]topology.CPUInfo{ + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 8: {CoreID: 8, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 9: {CoreID: 9, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 10: {CoreID: 10, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 11: {CoreID: 11, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 12: {CoreID: 12, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 3}, + 13: {CoreID: 13, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 3}, + 14: {CoreID: 14, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 3}, + 15: {CoreID: 15, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 3}, + }, + } + topoDualSocketNoHT = &topology.CPUTopology{ NumCPUs: 8, NumSockets: 2, diff --git a/pkg/kubelet/cm/cpumanager/topology/topology.go b/pkg/kubelet/cm/cpumanager/topology/topology.go index 62d91a5dee5d0..1de03c8fd9c78 100644 --- a/pkg/kubelet/cm/cpumanager/topology/topology.go +++ b/pkg/kubelet/cm/cpumanager/topology/topology.go @@ -36,12 +36,14 @@ type CPUDetails map[int]CPUInfo // Core - physical CPU, cadvisor - Core // Socket - socket, cadvisor - Socket // NUMA Node - NUMA cell, cadvisor - Node +// UncoreCache - Split L3 Cache Topology, cadvisor type CPUTopology struct { - NumCPUs int - NumCores int - NumSockets int - NumNUMANodes int - CPUDetails CPUDetails + NumCPUs int + NumCores int + NumUncoreCache int + NumSockets int + NumNUMANodes int + CPUDetails CPUDetails } // CPUsPerCore returns the number of logical CPUs are associated with @@ -62,6 +64,15 @@ func (topo *CPUTopology) CPUsPerSocket() int { return topo.NumCPUs / topo.NumSockets } +// CPUsPerUncore returns the number of logicial CPUs that are associated with +// each UncoreCache +func (topo *CPUTopology) CPUsPerUncore() int { + if topo.NumUncoreCache == 0 { + return 0 + } + return topo.NumCPUs / topo.NumUncoreCache +} + // CPUCoreID returns the physical core ID which the given logical CPU // belongs to. func (topo *CPUTopology) CPUCoreID(cpu int) (int, error) { @@ -90,11 +101,12 @@ func (topo *CPUTopology) CPUNUMANodeID(cpu int) (int, error) { return info.NUMANodeID, nil } -// CPUInfo contains the NUMA, socket, and core IDs associated with a CPU. +// CPUInfo contains the NUMA, socket, unCoreCache and core IDs associated with a CPU. type CPUInfo struct { - NUMANodeID int - SocketID int - CoreID int + NUMANodeID int + SocketID int + CoreID int + UncoreCacheID int } // KeepOnly returns a new CPUDetails object with only the supplied cpus. @@ -108,6 +120,67 @@ func (d CPUDetails) KeepOnly(cpus cpuset.CPUSet) CPUDetails { return result } +// UncoreCaches returns all the uncorecache Id (L3 Index) associated with the CPUs in this CPUDetails +func (d CPUDetails) UncoreCaches() cpuset.CPUSet { + var numUnCoreIDs []int + for _, info := range d { + numUnCoreIDs = append(numUnCoreIDs, info.UncoreCacheID) + } + return cpuset.New(numUnCoreIDs...) +} + +// UnCoresInNUMANodes returns all of the uncore IDs associated with the given +// NUMANode IDs in this CPUDetails. +func (d CPUDetails) UncoreInNUMANodes(ids ...int) cpuset.CPUSet { + var unCoreIDs []int + for _, id := range ids { + for _, info := range d { + if info.NUMANodeID == id { + unCoreIDs = append(unCoreIDs, info.UncoreCacheID) + } + } + } + return cpuset.New(unCoreIDs...) +} + +// CoresNeededInUncoreCache returns either the full list of all available unique core IDs associated with the given +// UnCoreCache IDs in this CPUDetails or subset that matches the ask. +func (d CPUDetails) CoresNeededInUncoreCache(numCoresNeeded int, ids ...int) cpuset.CPUSet { + coreIDs := d.coresInUncoreCache(ids...) + if coreIDs.Size() <= numCoresNeeded { + return coreIDs + } + tmpCoreIDs := coreIDs.List() + return cpuset.New(tmpCoreIDs[:numCoresNeeded]...) +} + +// Helper function that just gets the cores +func (d CPUDetails) coresInUncoreCache(ids ...int) cpuset.CPUSet { + var coreIDs []int + for _, id := range ids { + for _, info := range d { + if info.UncoreCacheID == id { + coreIDs = append(coreIDs, info.CoreID) + } + } + } + return cpuset.New(coreIDs...) +} + +// CPUsInUncoreCaches returns all the logical CPU IDs associated with the given +// UnCoreCache IDs in this CPUDetails +func (d CPUDetails) CPUsInUncoreCaches(ids ...int) cpuset.CPUSet { + var cpuIDs []int + for _, id := range ids { + for cpu, info := range d { + if info.UncoreCacheID == id { + cpuIDs = append(cpuIDs, cpu) + } + } + } + return cpuset.New(cpuIDs...) +} + // NUMANodes returns all of the NUMANode IDs associated with the CPUs in this // CPUDetails. func (d CPUDetails) NUMANodes() cpuset.CPUSet { @@ -245,6 +318,16 @@ func (d CPUDetails) CPUsInCores(ids ...int) cpuset.CPUSet { return cpuset.New(cpuIDs...) } +func getUncoreCacheID(core cadvisorapi.Core) int { + if len(core.UncoreCaches) < 1 { + // In case cAdvisor is nil, failback to socket alignment since uncorecache is not shared + return core.SocketID + } + // Even though cadvisor API returns a slice, we only expect either 0 or a 1 uncore caches, + // so everything past the first entry should be discarded or ignored + return core.UncoreCaches[0].Id +} + // Discover returns CPUTopology based on cadvisor node info func Discover(machineInfo *cadvisorapi.MachineInfo) (*CPUTopology, error) { if machineInfo.NumCores == 0 { @@ -260,9 +343,10 @@ func Discover(machineInfo *cadvisorapi.MachineInfo) (*CPUTopology, error) { if coreID, err := getUniqueCoreID(core.Threads); err == nil { for _, cpu := range core.Threads { CPUDetails[cpu] = CPUInfo{ - CoreID: coreID, - SocketID: core.SocketID, - NUMANodeID: node.Id, + CoreID: coreID, + SocketID: core.SocketID, + NUMANodeID: node.Id, + UncoreCacheID: getUncoreCacheID(core), } } } else { @@ -273,11 +357,12 @@ func Discover(machineInfo *cadvisorapi.MachineInfo) (*CPUTopology, error) { } return &CPUTopology{ - NumCPUs: machineInfo.NumCores, - NumSockets: machineInfo.NumSockets, - NumCores: numPhysicalCores, - NumNUMANodes: CPUDetails.NUMANodes().Size(), - CPUDetails: CPUDetails, + NumCPUs: machineInfo.NumCores, + NumSockets: machineInfo.NumSockets, + NumCores: numPhysicalCores, + NumNUMANodes: CPUDetails.NUMANodes().Size(), + NumUncoreCache: CPUDetails.UncoreCaches().Size(), + CPUDetails: CPUDetails, }, nil } diff --git a/pkg/kubelet/cm/cpumanager/topology/topology_test.go b/pkg/kubelet/cm/cpumanager/topology/topology_test.go index 37d8f7f01fc54..b4f14547e5aab 100644 --- a/pkg/kubelet/cm/cpumanager/topology/topology_test.go +++ b/pkg/kubelet/cm/cpumanager/topology/topology_test.go @@ -33,6 +33,41 @@ func Test_Discover(t *testing.T) { want *CPUTopology wantErr bool }{ + { + name: "EmptyUncoreCache", + machineInfo: cadvisorapi.MachineInfo{ + NumCores: 8, + NumSockets: 1, + Topology: []cadvisorapi.Node{ + {Id: 0, + Cores: []cadvisorapi.Core{ + {SocketID: 0, Id: 0, Threads: []int{0, 4}, UncoreCaches: []cadvisorapi.Cache{}}, + {SocketID: 0, Id: 1, Threads: []int{1, 5}, UncoreCaches: []cadvisorapi.Cache{}}, + {SocketID: 0, Id: 2, Threads: []int{2, 6}, UncoreCaches: []cadvisorapi.Cache{}}, + {SocketID: 0, Id: 3, Threads: []int{3, 7}, UncoreCaches: []cadvisorapi.Cache{}}, + }, + }, + }, + }, + want: &CPUTopology{ + NumCPUs: 8, + NumSockets: 1, + NumCores: 4, + NumNUMANodes: 1, + NumUncoreCache: 1, + CPUDetails: map[int]CPUInfo{ + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 4: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 5: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 6: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 7: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + }, + }, + wantErr: false, + }, { name: "FailNumCores", machineInfo: cadvisorapi.MachineInfo{ @@ -49,28 +84,29 @@ func Test_Discover(t *testing.T) { Topology: []cadvisorapi.Node{ {Id: 0, Cores: []cadvisorapi.Core{ - {SocketID: 0, Id: 0, Threads: []int{0, 4}}, - {SocketID: 0, Id: 1, Threads: []int{1, 5}}, - {SocketID: 0, Id: 2, Threads: []int{2, 6}}, - {SocketID: 0, Id: 3, Threads: []int{3, 7}}, + {SocketID: 0, Id: 0, Threads: []int{0, 4}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 1, Threads: []int{1, 5}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 2, Threads: []int{2, 6}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 3, Threads: []int{3, 7}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, }, }, }, }, want: &CPUTopology{ - NumCPUs: 8, - NumSockets: 1, - NumCores: 4, - NumNUMANodes: 1, + NumCPUs: 8, + NumSockets: 1, + NumCores: 4, + NumNUMANodes: 1, + NumUncoreCache: 1, CPUDetails: map[int]CPUInfo{ - 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, - 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0}, - 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0}, - 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0}, - 4: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, - 5: {CoreID: 1, SocketID: 0, NUMANodeID: 0}, - 6: {CoreID: 2, SocketID: 0, NUMANodeID: 0}, - 7: {CoreID: 3, SocketID: 0, NUMANodeID: 0}, + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 4: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 5: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 6: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 7: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, }, }, wantErr: false, @@ -84,148 +120,149 @@ func Test_Discover(t *testing.T) { Topology: []cadvisorapi.Node{ {Id: 0, Cores: []cadvisorapi.Core{ - {SocketID: 0, Id: 0, Threads: []int{0, 40}}, - {SocketID: 0, Id: 1, Threads: []int{1, 41}}, - {SocketID: 0, Id: 2, Threads: []int{2, 42}}, - {SocketID: 0, Id: 8, Threads: []int{3, 43}}, - {SocketID: 0, Id: 9, Threads: []int{4, 44}}, - {SocketID: 0, Id: 16, Threads: []int{5, 45}}, - {SocketID: 0, Id: 17, Threads: []int{6, 46}}, - {SocketID: 0, Id: 18, Threads: []int{7, 47}}, - {SocketID: 0, Id: 24, Threads: []int{8, 48}}, - {SocketID: 0, Id: 25, Threads: []int{9, 49}}, + {SocketID: 0, Id: 0, Threads: []int{0, 40}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 1, Threads: []int{1, 41}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 2, Threads: []int{2, 42}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 8, Threads: []int{3, 43}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 9, Threads: []int{4, 44}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 16, Threads: []int{5, 45}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 17, Threads: []int{6, 46}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 18, Threads: []int{7, 47}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 24, Threads: []int{8, 48}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 25, Threads: []int{9, 49}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, }, }, {Id: 1, Cores: []cadvisorapi.Core{ - {SocketID: 0, Id: 3, Threads: []int{10, 50}}, - {SocketID: 0, Id: 4, Threads: []int{11, 51}}, - {SocketID: 0, Id: 10, Threads: []int{12, 52}}, - {SocketID: 0, Id: 11, Threads: []int{13, 53}}, - {SocketID: 0, Id: 12, Threads: []int{14, 54}}, - {SocketID: 0, Id: 19, Threads: []int{15, 55}}, - {SocketID: 0, Id: 20, Threads: []int{16, 56}}, - {SocketID: 0, Id: 26, Threads: []int{17, 57}}, - {SocketID: 0, Id: 27, Threads: []int{18, 58}}, - {SocketID: 0, Id: 28, Threads: []int{19, 59}}, + {SocketID: 0, Id: 3, Threads: []int{10, 50}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 4, Threads: []int{11, 51}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 10, Threads: []int{12, 52}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 11, Threads: []int{13, 53}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 12, Threads: []int{14, 54}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 19, Threads: []int{15, 55}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 20, Threads: []int{16, 56}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 26, Threads: []int{17, 57}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 27, Threads: []int{18, 58}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 28, Threads: []int{19, 59}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, }, }, {Id: 2, Cores: []cadvisorapi.Core{ - {SocketID: 1, Id: 0, Threads: []int{20, 60}}, - {SocketID: 1, Id: 1, Threads: []int{21, 61}}, - {SocketID: 1, Id: 2, Threads: []int{22, 62}}, - {SocketID: 1, Id: 8, Threads: []int{23, 63}}, - {SocketID: 1, Id: 9, Threads: []int{24, 64}}, - {SocketID: 1, Id: 16, Threads: []int{25, 65}}, - {SocketID: 1, Id: 17, Threads: []int{26, 66}}, - {SocketID: 1, Id: 18, Threads: []int{27, 67}}, - {SocketID: 1, Id: 24, Threads: []int{28, 68}}, - {SocketID: 1, Id: 25, Threads: []int{29, 69}}, + {SocketID: 1, Id: 0, Threads: []int{20, 60}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 1, Threads: []int{21, 61}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 2, Threads: []int{22, 62}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 8, Threads: []int{23, 63}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 9, Threads: []int{24, 64}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 16, Threads: []int{25, 65}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 17, Threads: []int{26, 66}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 18, Threads: []int{27, 67}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 24, Threads: []int{28, 68}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 25, Threads: []int{29, 69}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, }, }, {Id: 3, Cores: []cadvisorapi.Core{ - {SocketID: 1, Id: 3, Threads: []int{30, 70}}, - {SocketID: 1, Id: 4, Threads: []int{31, 71}}, - {SocketID: 1, Id: 10, Threads: []int{32, 72}}, - {SocketID: 1, Id: 11, Threads: []int{33, 73}}, - {SocketID: 1, Id: 12, Threads: []int{34, 74}}, - {SocketID: 1, Id: 19, Threads: []int{35, 75}}, - {SocketID: 1, Id: 20, Threads: []int{36, 76}}, - {SocketID: 1, Id: 26, Threads: []int{37, 77}}, - {SocketID: 1, Id: 27, Threads: []int{38, 78}}, - {SocketID: 1, Id: 28, Threads: []int{39, 79}}, + {SocketID: 1, Id: 3, Threads: []int{30, 70}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 4, Threads: []int{31, 71}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 10, Threads: []int{32, 72}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 11, Threads: []int{33, 73}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 12, Threads: []int{34, 74}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 19, Threads: []int{35, 75}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 20, Threads: []int{36, 76}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 26, Threads: []int{37, 77}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 27, Threads: []int{38, 78}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 28, Threads: []int{39, 79}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, }, }, }, }, want: &CPUTopology{ - NumCPUs: 80, - NumSockets: 2, - NumCores: 40, - NumNUMANodes: 4, + NumCPUs: 80, + NumSockets: 2, + NumCores: 40, + NumNUMANodes: 4, + NumUncoreCache: 1, CPUDetails: map[int]CPUInfo{ - 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, - 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0}, - 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0}, - 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0}, - 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0}, - 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0}, - 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0}, - 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0}, - 8: {CoreID: 8, SocketID: 0, NUMANodeID: 0}, - 9: {CoreID: 9, SocketID: 0, NUMANodeID: 0}, - 10: {CoreID: 10, SocketID: 0, NUMANodeID: 1}, - 11: {CoreID: 11, SocketID: 0, NUMANodeID: 1}, - 12: {CoreID: 12, SocketID: 0, NUMANodeID: 1}, - 13: {CoreID: 13, SocketID: 0, NUMANodeID: 1}, - 14: {CoreID: 14, SocketID: 0, NUMANodeID: 1}, - 15: {CoreID: 15, SocketID: 0, NUMANodeID: 1}, - 16: {CoreID: 16, SocketID: 0, NUMANodeID: 1}, - 17: {CoreID: 17, SocketID: 0, NUMANodeID: 1}, - 18: {CoreID: 18, SocketID: 0, NUMANodeID: 1}, - 19: {CoreID: 19, SocketID: 0, NUMANodeID: 1}, - 20: {CoreID: 20, SocketID: 1, NUMANodeID: 2}, - 21: {CoreID: 21, SocketID: 1, NUMANodeID: 2}, - 22: {CoreID: 22, SocketID: 1, NUMANodeID: 2}, - 23: {CoreID: 23, SocketID: 1, NUMANodeID: 2}, - 24: {CoreID: 24, SocketID: 1, NUMANodeID: 2}, - 25: {CoreID: 25, SocketID: 1, NUMANodeID: 2}, - 26: {CoreID: 26, SocketID: 1, NUMANodeID: 2}, - 27: {CoreID: 27, SocketID: 1, NUMANodeID: 2}, - 28: {CoreID: 28, SocketID: 1, NUMANodeID: 2}, - 29: {CoreID: 29, SocketID: 1, NUMANodeID: 2}, - 30: {CoreID: 30, SocketID: 1, NUMANodeID: 3}, - 31: {CoreID: 31, SocketID: 1, NUMANodeID: 3}, - 32: {CoreID: 32, SocketID: 1, NUMANodeID: 3}, - 33: {CoreID: 33, SocketID: 1, NUMANodeID: 3}, - 34: {CoreID: 34, SocketID: 1, NUMANodeID: 3}, - 35: {CoreID: 35, SocketID: 1, NUMANodeID: 3}, - 36: {CoreID: 36, SocketID: 1, NUMANodeID: 3}, - 37: {CoreID: 37, SocketID: 1, NUMANodeID: 3}, - 38: {CoreID: 38, SocketID: 1, NUMANodeID: 3}, - 39: {CoreID: 39, SocketID: 1, NUMANodeID: 3}, - 40: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, - 41: {CoreID: 1, SocketID: 0, NUMANodeID: 0}, - 42: {CoreID: 2, SocketID: 0, NUMANodeID: 0}, - 43: {CoreID: 3, SocketID: 0, NUMANodeID: 0}, - 44: {CoreID: 4, SocketID: 0, NUMANodeID: 0}, - 45: {CoreID: 5, SocketID: 0, NUMANodeID: 0}, - 46: {CoreID: 6, SocketID: 0, NUMANodeID: 0}, - 47: {CoreID: 7, SocketID: 0, NUMANodeID: 0}, - 48: {CoreID: 8, SocketID: 0, NUMANodeID: 0}, - 49: {CoreID: 9, SocketID: 0, NUMANodeID: 0}, - 50: {CoreID: 10, SocketID: 0, NUMANodeID: 1}, - 51: {CoreID: 11, SocketID: 0, NUMANodeID: 1}, - 52: {CoreID: 12, SocketID: 0, NUMANodeID: 1}, - 53: {CoreID: 13, SocketID: 0, NUMANodeID: 1}, - 54: {CoreID: 14, SocketID: 0, NUMANodeID: 1}, - 55: {CoreID: 15, SocketID: 0, NUMANodeID: 1}, - 56: {CoreID: 16, SocketID: 0, NUMANodeID: 1}, - 57: {CoreID: 17, SocketID: 0, NUMANodeID: 1}, - 58: {CoreID: 18, SocketID: 0, NUMANodeID: 1}, - 59: {CoreID: 19, SocketID: 0, NUMANodeID: 1}, - 60: {CoreID: 20, SocketID: 1, NUMANodeID: 2}, - 61: {CoreID: 21, SocketID: 1, NUMANodeID: 2}, - 62: {CoreID: 22, SocketID: 1, NUMANodeID: 2}, - 63: {CoreID: 23, SocketID: 1, NUMANodeID: 2}, - 64: {CoreID: 24, SocketID: 1, NUMANodeID: 2}, - 65: {CoreID: 25, SocketID: 1, NUMANodeID: 2}, - 66: {CoreID: 26, SocketID: 1, NUMANodeID: 2}, - 67: {CoreID: 27, SocketID: 1, NUMANodeID: 2}, - 68: {CoreID: 28, SocketID: 1, NUMANodeID: 2}, - 69: {CoreID: 29, SocketID: 1, NUMANodeID: 2}, - 70: {CoreID: 30, SocketID: 1, NUMANodeID: 3}, - 71: {CoreID: 31, SocketID: 1, NUMANodeID: 3}, - 72: {CoreID: 32, SocketID: 1, NUMANodeID: 3}, - 73: {CoreID: 33, SocketID: 1, NUMANodeID: 3}, - 74: {CoreID: 34, SocketID: 1, NUMANodeID: 3}, - 75: {CoreID: 35, SocketID: 1, NUMANodeID: 3}, - 76: {CoreID: 36, SocketID: 1, NUMANodeID: 3}, - 77: {CoreID: 37, SocketID: 1, NUMANodeID: 3}, - 78: {CoreID: 38, SocketID: 1, NUMANodeID: 3}, - 79: {CoreID: 39, SocketID: 1, NUMANodeID: 3}, + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 8: {CoreID: 8, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 9: {CoreID: 9, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 10: {CoreID: 10, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 11: {CoreID: 11, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 12: {CoreID: 12, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 13: {CoreID: 13, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 14: {CoreID: 14, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 15: {CoreID: 15, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 16: {CoreID: 16, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 17: {CoreID: 17, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 18: {CoreID: 18, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 19: {CoreID: 19, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 20: {CoreID: 20, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 21: {CoreID: 21, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 22: {CoreID: 22, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 23: {CoreID: 23, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 24: {CoreID: 24, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 25: {CoreID: 25, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 26: {CoreID: 26, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 27: {CoreID: 27, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 28: {CoreID: 28, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 29: {CoreID: 29, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 30: {CoreID: 30, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 31: {CoreID: 31, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 32: {CoreID: 32, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 33: {CoreID: 33, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 34: {CoreID: 34, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 35: {CoreID: 35, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 36: {CoreID: 36, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 37: {CoreID: 37, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 38: {CoreID: 38, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 39: {CoreID: 39, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 40: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 41: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 42: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 43: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 44: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 45: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 46: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 47: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 48: {CoreID: 8, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 49: {CoreID: 9, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 50: {CoreID: 10, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 51: {CoreID: 11, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 52: {CoreID: 12, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 53: {CoreID: 13, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 54: {CoreID: 14, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 55: {CoreID: 15, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 56: {CoreID: 16, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 57: {CoreID: 17, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 58: {CoreID: 18, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 59: {CoreID: 19, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 0}, + 60: {CoreID: 20, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 61: {CoreID: 21, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 62: {CoreID: 22, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 63: {CoreID: 23, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 64: {CoreID: 24, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 65: {CoreID: 25, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 66: {CoreID: 26, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 67: {CoreID: 27, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 68: {CoreID: 28, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 69: {CoreID: 29, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 0}, + 70: {CoreID: 30, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 71: {CoreID: 31, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 72: {CoreID: 32, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 73: {CoreID: 33, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 74: {CoreID: 34, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 75: {CoreID: 35, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 76: {CoreID: 36, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 77: {CoreID: 37, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 78: {CoreID: 38, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, + 79: {CoreID: 39, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 0}, }, }, wantErr: false, @@ -249,140 +286,141 @@ func Test_Discover(t *testing.T) { Topology: []cadvisorapi.Node{ {Id: 0, Cores: []cadvisorapi.Core{ - {SocketID: 0, Id: 0, Threads: []int{0, 40}}, - {SocketID: 0, Id: 1, Threads: []int{1, 41}}, - {SocketID: 0, Id: 2, Threads: []int{2, 42}}, - {SocketID: 0, Id: 8, Threads: []int{3, 43}}, - {SocketID: 0, Id: 9, Threads: []int{4, 44}}, - {SocketID: 0, Id: 16, Threads: []int{5, 45}}, - {SocketID: 0, Id: 17, Threads: []int{6, 46}}, - {SocketID: 0, Id: 18, Threads: []int{7, 47}}, - {SocketID: 0, Id: 24, Threads: []int{8, 48}}, - {SocketID: 0, Id: 25, Threads: []int{9, 49}}, - {SocketID: 1, Id: 3, Threads: []int{10, 50}}, - {SocketID: 1, Id: 4, Threads: []int{11, 51}}, - {SocketID: 1, Id: 10, Threads: []int{12, 52}}, - {SocketID: 1, Id: 11, Threads: []int{13, 53}}, - {SocketID: 1, Id: 12, Threads: []int{14, 54}}, - {SocketID: 1, Id: 19, Threads: []int{15, 55}}, - {SocketID: 1, Id: 20, Threads: []int{16, 56}}, - {SocketID: 1, Id: 26, Threads: []int{17, 57}}, - {SocketID: 1, Id: 27, Threads: []int{18, 58}}, - {SocketID: 1, Id: 28, Threads: []int{19, 59}}, + {SocketID: 0, Id: 0, Threads: []int{0, 40}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 1, Threads: []int{1, 41}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 2, Threads: []int{2, 42}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 8, Threads: []int{3, 43}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 9, Threads: []int{4, 44}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 16, Threads: []int{5, 45}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 17, Threads: []int{6, 46}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 18, Threads: []int{7, 47}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 24, Threads: []int{8, 48}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 25, Threads: []int{9, 49}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 3, Threads: []int{10, 50}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 4, Threads: []int{11, 51}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 10, Threads: []int{12, 52}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 11, Threads: []int{13, 53}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 12, Threads: []int{14, 54}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 19, Threads: []int{15, 55}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 20, Threads: []int{16, 56}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 26, Threads: []int{17, 57}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 27, Threads: []int{18, 58}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 28, Threads: []int{19, 59}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, }, }, {Id: 1, Cores: []cadvisorapi.Core{ - {SocketID: 2, Id: 0, Threads: []int{20, 60}}, - {SocketID: 2, Id: 1, Threads: []int{21, 61}}, - {SocketID: 2, Id: 2, Threads: []int{22, 62}}, - {SocketID: 2, Id: 8, Threads: []int{23, 63}}, - {SocketID: 2, Id: 9, Threads: []int{24, 64}}, - {SocketID: 2, Id: 16, Threads: []int{25, 65}}, - {SocketID: 2, Id: 17, Threads: []int{26, 66}}, - {SocketID: 2, Id: 18, Threads: []int{27, 67}}, - {SocketID: 2, Id: 24, Threads: []int{28, 68}}, - {SocketID: 2, Id: 25, Threads: []int{29, 69}}, - {SocketID: 3, Id: 3, Threads: []int{30, 70}}, - {SocketID: 3, Id: 4, Threads: []int{31, 71}}, - {SocketID: 3, Id: 10, Threads: []int{32, 72}}, - {SocketID: 3, Id: 11, Threads: []int{33, 73}}, - {SocketID: 3, Id: 12, Threads: []int{34, 74}}, - {SocketID: 3, Id: 19, Threads: []int{35, 75}}, - {SocketID: 3, Id: 20, Threads: []int{36, 76}}, - {SocketID: 3, Id: 26, Threads: []int{37, 77}}, - {SocketID: 3, Id: 27, Threads: []int{38, 78}}, - {SocketID: 3, Id: 28, Threads: []int{39, 79}}, + {SocketID: 2, Id: 0, Threads: []int{20, 60}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 2, Id: 1, Threads: []int{21, 61}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 2, Id: 2, Threads: []int{22, 62}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 2, Id: 8, Threads: []int{23, 63}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 2, Id: 9, Threads: []int{24, 64}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 2, Id: 16, Threads: []int{25, 65}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 2, Id: 17, Threads: []int{26, 66}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 2, Id: 18, Threads: []int{27, 67}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 2, Id: 24, Threads: []int{28, 68}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 2, Id: 25, Threads: []int{29, 69}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 3, Threads: []int{30, 70}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 4, Threads: []int{31, 71}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 10, Threads: []int{32, 72}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 11, Threads: []int{33, 73}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 12, Threads: []int{34, 74}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 19, Threads: []int{35, 75}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 20, Threads: []int{36, 76}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 26, Threads: []int{37, 77}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 27, Threads: []int{38, 78}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 3, Id: 28, Threads: []int{39, 79}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, }, }, }, }, want: &CPUTopology{ - NumCPUs: 80, - NumSockets: 4, - NumCores: 40, - NumNUMANodes: 2, + NumCPUs: 80, + NumSockets: 4, + NumCores: 40, + NumNUMANodes: 2, + NumUncoreCache: 1, CPUDetails: map[int]CPUInfo{ - 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, - 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0}, - 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0}, - 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0}, - 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0}, - 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0}, - 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0}, - 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0}, - 8: {CoreID: 8, SocketID: 0, NUMANodeID: 0}, - 9: {CoreID: 9, SocketID: 0, NUMANodeID: 0}, - 10: {CoreID: 10, SocketID: 1, NUMANodeID: 0}, - 11: {CoreID: 11, SocketID: 1, NUMANodeID: 0}, - 12: {CoreID: 12, SocketID: 1, NUMANodeID: 0}, - 13: {CoreID: 13, SocketID: 1, NUMANodeID: 0}, - 14: {CoreID: 14, SocketID: 1, NUMANodeID: 0}, - 15: {CoreID: 15, SocketID: 1, NUMANodeID: 0}, - 16: {CoreID: 16, SocketID: 1, NUMANodeID: 0}, - 17: {CoreID: 17, SocketID: 1, NUMANodeID: 0}, - 18: {CoreID: 18, SocketID: 1, NUMANodeID: 0}, - 19: {CoreID: 19, SocketID: 1, NUMANodeID: 0}, - 20: {CoreID: 20, SocketID: 2, NUMANodeID: 1}, - 21: {CoreID: 21, SocketID: 2, NUMANodeID: 1}, - 22: {CoreID: 22, SocketID: 2, NUMANodeID: 1}, - 23: {CoreID: 23, SocketID: 2, NUMANodeID: 1}, - 24: {CoreID: 24, SocketID: 2, NUMANodeID: 1}, - 25: {CoreID: 25, SocketID: 2, NUMANodeID: 1}, - 26: {CoreID: 26, SocketID: 2, NUMANodeID: 1}, - 27: {CoreID: 27, SocketID: 2, NUMANodeID: 1}, - 28: {CoreID: 28, SocketID: 2, NUMANodeID: 1}, - 29: {CoreID: 29, SocketID: 2, NUMANodeID: 1}, - 30: {CoreID: 30, SocketID: 3, NUMANodeID: 1}, - 31: {CoreID: 31, SocketID: 3, NUMANodeID: 1}, - 32: {CoreID: 32, SocketID: 3, NUMANodeID: 1}, - 33: {CoreID: 33, SocketID: 3, NUMANodeID: 1}, - 34: {CoreID: 34, SocketID: 3, NUMANodeID: 1}, - 35: {CoreID: 35, SocketID: 3, NUMANodeID: 1}, - 36: {CoreID: 36, SocketID: 3, NUMANodeID: 1}, - 37: {CoreID: 37, SocketID: 3, NUMANodeID: 1}, - 38: {CoreID: 38, SocketID: 3, NUMANodeID: 1}, - 39: {CoreID: 39, SocketID: 3, NUMANodeID: 1}, - 40: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, - 41: {CoreID: 1, SocketID: 0, NUMANodeID: 0}, - 42: {CoreID: 2, SocketID: 0, NUMANodeID: 0}, - 43: {CoreID: 3, SocketID: 0, NUMANodeID: 0}, - 44: {CoreID: 4, SocketID: 0, NUMANodeID: 0}, - 45: {CoreID: 5, SocketID: 0, NUMANodeID: 0}, - 46: {CoreID: 6, SocketID: 0, NUMANodeID: 0}, - 47: {CoreID: 7, SocketID: 0, NUMANodeID: 0}, - 48: {CoreID: 8, SocketID: 0, NUMANodeID: 0}, - 49: {CoreID: 9, SocketID: 0, NUMANodeID: 0}, - 50: {CoreID: 10, SocketID: 1, NUMANodeID: 0}, - 51: {CoreID: 11, SocketID: 1, NUMANodeID: 0}, - 52: {CoreID: 12, SocketID: 1, NUMANodeID: 0}, - 53: {CoreID: 13, SocketID: 1, NUMANodeID: 0}, - 54: {CoreID: 14, SocketID: 1, NUMANodeID: 0}, - 55: {CoreID: 15, SocketID: 1, NUMANodeID: 0}, - 56: {CoreID: 16, SocketID: 1, NUMANodeID: 0}, - 57: {CoreID: 17, SocketID: 1, NUMANodeID: 0}, - 58: {CoreID: 18, SocketID: 1, NUMANodeID: 0}, - 59: {CoreID: 19, SocketID: 1, NUMANodeID: 0}, - 60: {CoreID: 20, SocketID: 2, NUMANodeID: 1}, - 61: {CoreID: 21, SocketID: 2, NUMANodeID: 1}, - 62: {CoreID: 22, SocketID: 2, NUMANodeID: 1}, - 63: {CoreID: 23, SocketID: 2, NUMANodeID: 1}, - 64: {CoreID: 24, SocketID: 2, NUMANodeID: 1}, - 65: {CoreID: 25, SocketID: 2, NUMANodeID: 1}, - 66: {CoreID: 26, SocketID: 2, NUMANodeID: 1}, - 67: {CoreID: 27, SocketID: 2, NUMANodeID: 1}, - 68: {CoreID: 28, SocketID: 2, NUMANodeID: 1}, - 69: {CoreID: 29, SocketID: 2, NUMANodeID: 1}, - 70: {CoreID: 30, SocketID: 3, NUMANodeID: 1}, - 71: {CoreID: 31, SocketID: 3, NUMANodeID: 1}, - 72: {CoreID: 32, SocketID: 3, NUMANodeID: 1}, - 73: {CoreID: 33, SocketID: 3, NUMANodeID: 1}, - 74: {CoreID: 34, SocketID: 3, NUMANodeID: 1}, - 75: {CoreID: 35, SocketID: 3, NUMANodeID: 1}, - 76: {CoreID: 36, SocketID: 3, NUMANodeID: 1}, - 77: {CoreID: 37, SocketID: 3, NUMANodeID: 1}, - 78: {CoreID: 38, SocketID: 3, NUMANodeID: 1}, - 79: {CoreID: 39, SocketID: 3, NUMANodeID: 1}, + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 8: {CoreID: 8, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 9: {CoreID: 9, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 10: {CoreID: 10, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 11: {CoreID: 11, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 12: {CoreID: 12, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 13: {CoreID: 13, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 14: {CoreID: 14, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 15: {CoreID: 15, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 16: {CoreID: 16, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 17: {CoreID: 17, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 18: {CoreID: 18, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 19: {CoreID: 19, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 20: {CoreID: 20, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 21: {CoreID: 21, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 22: {CoreID: 22, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 23: {CoreID: 23, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 24: {CoreID: 24, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 25: {CoreID: 25, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 26: {CoreID: 26, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 27: {CoreID: 27, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 28: {CoreID: 28, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 29: {CoreID: 29, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 30: {CoreID: 30, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 31: {CoreID: 31, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 32: {CoreID: 32, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 33: {CoreID: 33, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 34: {CoreID: 34, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 35: {CoreID: 35, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 36: {CoreID: 36, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 37: {CoreID: 37, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 38: {CoreID: 38, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 39: {CoreID: 39, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 40: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 41: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 42: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 43: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 44: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 45: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 46: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 47: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 48: {CoreID: 8, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 49: {CoreID: 9, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 50: {CoreID: 10, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 51: {CoreID: 11, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 52: {CoreID: 12, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 53: {CoreID: 13, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 54: {CoreID: 14, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 55: {CoreID: 15, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 56: {CoreID: 16, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 57: {CoreID: 17, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 58: {CoreID: 18, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 59: {CoreID: 19, SocketID: 1, NUMANodeID: 0, UncoreCacheID: 1}, + 60: {CoreID: 20, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 61: {CoreID: 21, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 62: {CoreID: 22, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 63: {CoreID: 23, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 64: {CoreID: 24, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 65: {CoreID: 25, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 66: {CoreID: 26, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 67: {CoreID: 27, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 68: {CoreID: 28, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 69: {CoreID: 29, SocketID: 2, NUMANodeID: 1, UncoreCacheID: 1}, + 70: {CoreID: 30, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 71: {CoreID: 31, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 72: {CoreID: 32, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 73: {CoreID: 33, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 74: {CoreID: 34, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 75: {CoreID: 35, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 76: {CoreID: 36, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 77: {CoreID: 37, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 78: {CoreID: 38, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, + 79: {CoreID: 39, SocketID: 3, NUMANodeID: 1, UncoreCacheID: 1}, }, }, wantErr: false, @@ -395,28 +433,29 @@ func Test_Discover(t *testing.T) { Topology: []cadvisorapi.Node{ {Id: 0, Cores: []cadvisorapi.Core{ - {SocketID: 0, Id: 0, Threads: []int{0}}, - {SocketID: 0, Id: 2, Threads: []int{2}}, + {SocketID: 0, Id: 0, Threads: []int{0}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 2, Threads: []int{2}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, }, }, {Id: 1, Cores: []cadvisorapi.Core{ - {SocketID: 1, Id: 1, Threads: []int{1}}, - {SocketID: 1, Id: 3, Threads: []int{3}}, + {SocketID: 1, Id: 1, Threads: []int{1}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 3, Threads: []int{3}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, }, }, }, }, want: &CPUTopology{ - NumCPUs: 4, - NumSockets: 2, - NumCores: 4, - NumNUMANodes: 2, + NumCPUs: 4, + NumSockets: 2, + NumCores: 4, + NumNUMANodes: 2, + NumUncoreCache: 1, CPUDetails: map[int]CPUInfo{ - 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, - 1: {CoreID: 1, SocketID: 1, NUMANodeID: 1}, - 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0}, - 3: {CoreID: 3, SocketID: 1, NUMANodeID: 1}, + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 1, NUMANodeID: 1, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 1, NUMANodeID: 1, UncoreCacheID: 0}, }, }, wantErr: false, @@ -429,38 +468,39 @@ func Test_Discover(t *testing.T) { Topology: []cadvisorapi.Node{ {Id: 0, Cores: []cadvisorapi.Core{ - {SocketID: 0, Id: 0, Threads: []int{0, 6}}, - {SocketID: 0, Id: 1, Threads: []int{1, 7}}, - {SocketID: 0, Id: 2, Threads: []int{2, 8}}, + {SocketID: 0, Id: 0, Threads: []int{0, 6}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 1, Threads: []int{1, 7}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 2, Threads: []int{2, 8}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, }, }, {Id: 1, Cores: []cadvisorapi.Core{ - {SocketID: 1, Id: 0, Threads: []int{3, 9}}, - {SocketID: 1, Id: 1, Threads: []int{4, 10}}, - {SocketID: 1, Id: 2, Threads: []int{5, 11}}, + {SocketID: 1, Id: 0, Threads: []int{3, 9}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 1, Threads: []int{4, 10}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 1, Id: 2, Threads: []int{5, 11}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, }, }, }, }, want: &CPUTopology{ - NumCPUs: 12, - NumSockets: 2, - NumCores: 6, - NumNUMANodes: 2, + NumCPUs: 12, + NumSockets: 2, + NumCores: 6, + NumNUMANodes: 2, + NumUncoreCache: 1, CPUDetails: map[int]CPUInfo{ - 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, - 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0}, - 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0}, - 3: {CoreID: 3, SocketID: 1, NUMANodeID: 1}, - 4: {CoreID: 4, SocketID: 1, NUMANodeID: 1}, - 5: {CoreID: 5, SocketID: 1, NUMANodeID: 1}, - 6: {CoreID: 0, SocketID: 0, NUMANodeID: 0}, - 7: {CoreID: 1, SocketID: 0, NUMANodeID: 0}, - 8: {CoreID: 2, SocketID: 0, NUMANodeID: 0}, - 9: {CoreID: 3, SocketID: 1, NUMANodeID: 1}, - 10: {CoreID: 4, SocketID: 1, NUMANodeID: 1}, - 11: {CoreID: 5, SocketID: 1, NUMANodeID: 1}, + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 1, NUMANodeID: 1, UncoreCacheID: 0}, + 4: {CoreID: 4, SocketID: 1, NUMANodeID: 1, UncoreCacheID: 0}, + 5: {CoreID: 5, SocketID: 1, NUMANodeID: 1, UncoreCacheID: 0}, + 6: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 7: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 8: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 9: {CoreID: 3, SocketID: 1, NUMANodeID: 1, UncoreCacheID: 0}, + 10: {CoreID: 4, SocketID: 1, NUMANodeID: 1, UncoreCacheID: 0}, + 11: {CoreID: 5, SocketID: 1, NUMANodeID: 1, UncoreCacheID: 0}, }, }, wantErr: false, @@ -473,10 +513,10 @@ func Test_Discover(t *testing.T) { Topology: []cadvisorapi.Node{ {Id: 0, Cores: []cadvisorapi.Core{ - {SocketID: 0, Id: 0, Threads: []int{0, 4}}, - {SocketID: 0, Id: 1, Threads: []int{1, 5}}, - {SocketID: 0, Id: 2, Threads: []int{2, 2}}, // Wrong case - should fail here - {SocketID: 0, Id: 3, Threads: []int{3, 7}}, + {SocketID: 0, Id: 0, Threads: []int{0, 4}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 1, Threads: []int{1, 5}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 2, Threads: []int{2, 2}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, // Wrong case - should fail here + {SocketID: 0, Id: 3, Threads: []int{3, 7}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, }, }, }, @@ -492,10 +532,10 @@ func Test_Discover(t *testing.T) { Topology: []cadvisorapi.Node{ {Id: 0, Cores: []cadvisorapi.Core{ - {SocketID: 0, Id: 0, Threads: []int{0, 4}}, - {SocketID: 0, Id: 1, Threads: []int{1, 5}}, - {SocketID: 0, Id: 2, Threads: []int{2, 6}}, - {SocketID: 0, Id: 3, Threads: []int{}}, // Wrong case - should fail here + {SocketID: 0, Id: 0, Threads: []int{0, 4}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 1, Threads: []int{1, 5}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 2, Threads: []int{2, 6}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 3, Threads: []int{}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, // Wrong case - should fail here }, }, }, @@ -503,6 +543,280 @@ func Test_Discover(t *testing.T) { want: &CPUTopology{}, wantErr: true, }, + { + // Topology from AMD EPYC 7452 (Rome) 32-Core Processor + // 4 cores per LLC + // Single-socket SMT-disabled + // NPS=1 + name: "UncoreOneSocketNoSMT", + machineInfo: cadvisorapi.MachineInfo{ + NumCores: 32, + NumSockets: 1, + Topology: []cadvisorapi.Node{ + {Id: 0, + Cores: []cadvisorapi.Core{ + {SocketID: 0, Id: 0, Threads: []int{0}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 1, Threads: []int{1}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 2, Threads: []int{2}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 3, Threads: []int{3}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 4, Threads: []int{4}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 5, Threads: []int{5}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 6, Threads: []int{6}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 7, Threads: []int{7}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 8, Threads: []int{8}, UncoreCaches: []cadvisorapi.Cache{{Id: 2}}}, + {SocketID: 0, Id: 9, Threads: []int{9}, UncoreCaches: []cadvisorapi.Cache{{Id: 2}}}, + {SocketID: 0, Id: 10, Threads: []int{10}, UncoreCaches: []cadvisorapi.Cache{{Id: 2}}}, + {SocketID: 0, Id: 11, Threads: []int{11}, UncoreCaches: []cadvisorapi.Cache{{Id: 2}}}, + {SocketID: 0, Id: 12, Threads: []int{12}, UncoreCaches: []cadvisorapi.Cache{{Id: 3}}}, + {SocketID: 0, Id: 13, Threads: []int{13}, UncoreCaches: []cadvisorapi.Cache{{Id: 3}}}, + {SocketID: 0, Id: 14, Threads: []int{14}, UncoreCaches: []cadvisorapi.Cache{{Id: 3}}}, + {SocketID: 0, Id: 15, Threads: []int{15}, UncoreCaches: []cadvisorapi.Cache{{Id: 3}}}, + {SocketID: 0, Id: 16, Threads: []int{16}, UncoreCaches: []cadvisorapi.Cache{{Id: 4}}}, + {SocketID: 0, Id: 17, Threads: []int{17}, UncoreCaches: []cadvisorapi.Cache{{Id: 4}}}, + {SocketID: 0, Id: 18, Threads: []int{18}, UncoreCaches: []cadvisorapi.Cache{{Id: 4}}}, + {SocketID: 0, Id: 19, Threads: []int{19}, UncoreCaches: []cadvisorapi.Cache{{Id: 4}}}, + {SocketID: 0, Id: 20, Threads: []int{20}, UncoreCaches: []cadvisorapi.Cache{{Id: 5}}}, + {SocketID: 0, Id: 21, Threads: []int{21}, UncoreCaches: []cadvisorapi.Cache{{Id: 5}}}, + {SocketID: 0, Id: 22, Threads: []int{22}, UncoreCaches: []cadvisorapi.Cache{{Id: 5}}}, + {SocketID: 0, Id: 23, Threads: []int{23}, UncoreCaches: []cadvisorapi.Cache{{Id: 5}}}, + {SocketID: 0, Id: 24, Threads: []int{24}, UncoreCaches: []cadvisorapi.Cache{{Id: 6}}}, + {SocketID: 0, Id: 25, Threads: []int{25}, UncoreCaches: []cadvisorapi.Cache{{Id: 6}}}, + {SocketID: 0, Id: 26, Threads: []int{26}, UncoreCaches: []cadvisorapi.Cache{{Id: 6}}}, + {SocketID: 0, Id: 27, Threads: []int{27}, UncoreCaches: []cadvisorapi.Cache{{Id: 6}}}, + {SocketID: 0, Id: 28, Threads: []int{28}, UncoreCaches: []cadvisorapi.Cache{{Id: 7}}}, + {SocketID: 0, Id: 29, Threads: []int{29}, UncoreCaches: []cadvisorapi.Cache{{Id: 7}}}, + {SocketID: 0, Id: 30, Threads: []int{30}, UncoreCaches: []cadvisorapi.Cache{{Id: 7}}}, + {SocketID: 0, Id: 31, Threads: []int{31}, UncoreCaches: []cadvisorapi.Cache{{Id: 7}}}, + }, + }, + }, + }, + want: &CPUTopology{ + NumCPUs: 32, + NumSockets: 1, + NumCores: 32, + NumNUMANodes: 1, + NumUncoreCache: 8, + CPUDetails: map[int]CPUInfo{ + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 8: {CoreID: 8, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 9: {CoreID: 9, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 10: {CoreID: 10, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 11: {CoreID: 11, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 12: {CoreID: 12, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 3}, + 13: {CoreID: 13, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 3}, + 14: {CoreID: 14, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 3}, + 15: {CoreID: 15, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 3}, + 16: {CoreID: 16, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 4}, + 17: {CoreID: 17, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 4}, + 18: {CoreID: 18, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 4}, + 19: {CoreID: 19, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 4}, + 20: {CoreID: 20, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 5}, + 21: {CoreID: 21, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 5}, + 22: {CoreID: 22, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 5}, + 23: {CoreID: 23, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 5}, + 24: {CoreID: 24, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 6}, + 25: {CoreID: 25, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 6}, + 26: {CoreID: 26, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 6}, + 27: {CoreID: 27, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 6}, + 28: {CoreID: 28, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 7}, + 29: {CoreID: 29, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 7}, + 30: {CoreID: 30, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 7}, + 31: {CoreID: 31, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 7}, + }, + }, + wantErr: false, + }, + { + // Topology from AMD EPYC 24-Core Processor + // 4 cores per LLC + // Dual-socket SMT-enabled + // NPS=2 + name: "UncoreDualSocketSMT", + machineInfo: cadvisorapi.MachineInfo{ + NumCores: 96, + NumSockets: 2, + Topology: []cadvisorapi.Node{ + {Id: 0, + Cores: []cadvisorapi.Core{ + {SocketID: 0, Id: 0, Threads: []int{0, 48}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 1, Threads: []int{1, 49}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 2, Threads: []int{2, 50}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 3, Threads: []int{3, 51}, UncoreCaches: []cadvisorapi.Cache{{Id: 0}}}, + {SocketID: 0, Id: 4, Threads: []int{4, 52}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 5, Threads: []int{5, 53}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 6, Threads: []int{6, 54}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 7, Threads: []int{7, 55}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 8, Threads: []int{8, 56}, UncoreCaches: []cadvisorapi.Cache{{Id: 2}}}, + {SocketID: 0, Id: 9, Threads: []int{9, 57}, UncoreCaches: []cadvisorapi.Cache{{Id: 2}}}, + {SocketID: 0, Id: 10, Threads: []int{10, 58}, UncoreCaches: []cadvisorapi.Cache{{Id: 2}}}, + {SocketID: 0, Id: 11, Threads: []int{11, 59}, UncoreCaches: []cadvisorapi.Cache{{Id: 2}}}, + }, + }, + {Id: 1, + Cores: []cadvisorapi.Core{ + {SocketID: 0, Id: 12, Threads: []int{12, 60}, UncoreCaches: []cadvisorapi.Cache{{Id: 3}}}, + {SocketID: 0, Id: 13, Threads: []int{13, 61}, UncoreCaches: []cadvisorapi.Cache{{Id: 3}}}, + {SocketID: 0, Id: 14, Threads: []int{14, 62}, UncoreCaches: []cadvisorapi.Cache{{Id: 3}}}, + {SocketID: 0, Id: 15, Threads: []int{15, 63}, UncoreCaches: []cadvisorapi.Cache{{Id: 3}}}, + {SocketID: 0, Id: 16, Threads: []int{16, 64}, UncoreCaches: []cadvisorapi.Cache{{Id: 4}}}, + {SocketID: 0, Id: 17, Threads: []int{17, 65}, UncoreCaches: []cadvisorapi.Cache{{Id: 4}}}, + {SocketID: 0, Id: 18, Threads: []int{18, 66}, UncoreCaches: []cadvisorapi.Cache{{Id: 4}}}, + {SocketID: 0, Id: 19, Threads: []int{19, 67}, UncoreCaches: []cadvisorapi.Cache{{Id: 4}}}, + {SocketID: 0, Id: 20, Threads: []int{20, 68}, UncoreCaches: []cadvisorapi.Cache{{Id: 5}}}, + {SocketID: 0, Id: 21, Threads: []int{21, 69}, UncoreCaches: []cadvisorapi.Cache{{Id: 5}}}, + {SocketID: 0, Id: 22, Threads: []int{22, 70}, UncoreCaches: []cadvisorapi.Cache{{Id: 5}}}, + {SocketID: 0, Id: 23, Threads: []int{23, 71}, UncoreCaches: []cadvisorapi.Cache{{Id: 5}}}, + }, + }, + {Id: 2, + Cores: []cadvisorapi.Core{ + {SocketID: 1, Id: 24, Threads: []int{24, 72}, UncoreCaches: []cadvisorapi.Cache{{Id: 6}}}, + {SocketID: 1, Id: 25, Threads: []int{25, 73}, UncoreCaches: []cadvisorapi.Cache{{Id: 6}}}, + {SocketID: 1, Id: 26, Threads: []int{26, 74}, UncoreCaches: []cadvisorapi.Cache{{Id: 6}}}, + {SocketID: 1, Id: 27, Threads: []int{27, 75}, UncoreCaches: []cadvisorapi.Cache{{Id: 6}}}, + {SocketID: 1, Id: 28, Threads: []int{28, 76}, UncoreCaches: []cadvisorapi.Cache{{Id: 7}}}, + {SocketID: 1, Id: 29, Threads: []int{29, 77}, UncoreCaches: []cadvisorapi.Cache{{Id: 7}}}, + {SocketID: 1, Id: 30, Threads: []int{30, 78}, UncoreCaches: []cadvisorapi.Cache{{Id: 7}}}, + {SocketID: 1, Id: 31, Threads: []int{31, 79}, UncoreCaches: []cadvisorapi.Cache{{Id: 7}}}, + {SocketID: 1, Id: 32, Threads: []int{32, 80}, UncoreCaches: []cadvisorapi.Cache{{Id: 8}}}, + {SocketID: 1, Id: 33, Threads: []int{33, 81}, UncoreCaches: []cadvisorapi.Cache{{Id: 8}}}, + {SocketID: 1, Id: 34, Threads: []int{34, 82}, UncoreCaches: []cadvisorapi.Cache{{Id: 8}}}, + {SocketID: 1, Id: 35, Threads: []int{35, 83}, UncoreCaches: []cadvisorapi.Cache{{Id: 8}}}, + }, + }, + {Id: 3, + Cores: []cadvisorapi.Core{ + {SocketID: 1, Id: 36, Threads: []int{36, 84}, UncoreCaches: []cadvisorapi.Cache{{Id: 9}}}, + {SocketID: 1, Id: 37, Threads: []int{37, 85}, UncoreCaches: []cadvisorapi.Cache{{Id: 9}}}, + {SocketID: 1, Id: 38, Threads: []int{38, 86}, UncoreCaches: []cadvisorapi.Cache{{Id: 9}}}, + {SocketID: 1, Id: 39, Threads: []int{39, 87}, UncoreCaches: []cadvisorapi.Cache{{Id: 9}}}, + {SocketID: 1, Id: 40, Threads: []int{40, 88}, UncoreCaches: []cadvisorapi.Cache{{Id: 10}}}, + {SocketID: 1, Id: 41, Threads: []int{41, 89}, UncoreCaches: []cadvisorapi.Cache{{Id: 10}}}, + {SocketID: 1, Id: 42, Threads: []int{42, 90}, UncoreCaches: []cadvisorapi.Cache{{Id: 10}}}, + {SocketID: 1, Id: 43, Threads: []int{43, 91}, UncoreCaches: []cadvisorapi.Cache{{Id: 10}}}, + {SocketID: 1, Id: 44, Threads: []int{44, 92}, UncoreCaches: []cadvisorapi.Cache{{Id: 11}}}, + {SocketID: 1, Id: 45, Threads: []int{45, 93}, UncoreCaches: []cadvisorapi.Cache{{Id: 11}}}, + {SocketID: 1, Id: 46, Threads: []int{46, 94}, UncoreCaches: []cadvisorapi.Cache{{Id: 11}}}, + {SocketID: 1, Id: 47, Threads: []int{47, 95}, UncoreCaches: []cadvisorapi.Cache{{Id: 11}}}, + }, + }, + }, + }, + want: &CPUTopology{ + NumCPUs: 96, + NumSockets: 2, + NumCores: 48, + NumNUMANodes: 4, + NumUncoreCache: 12, + CPUDetails: map[int]CPUInfo{ + 0: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 1: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 2: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 3: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 4: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 5: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 6: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 7: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 8: {CoreID: 8, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 9: {CoreID: 9, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 10: {CoreID: 10, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 11: {CoreID: 11, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 12: {CoreID: 12, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 13: {CoreID: 13, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 14: {CoreID: 14, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 15: {CoreID: 15, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 16: {CoreID: 16, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 4}, + 17: {CoreID: 17, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 4}, + 18: {CoreID: 18, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 4}, + 19: {CoreID: 19, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 4}, + 20: {CoreID: 20, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 5}, + 21: {CoreID: 21, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 5}, + 22: {CoreID: 22, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 5}, + 23: {CoreID: 23, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 5}, + 24: {CoreID: 24, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 6}, + 25: {CoreID: 25, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 6}, + 26: {CoreID: 26, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 6}, + 27: {CoreID: 27, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 6}, + 28: {CoreID: 28, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 7}, + 29: {CoreID: 29, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 7}, + 30: {CoreID: 30, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 7}, + 31: {CoreID: 31, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 7}, + 32: {CoreID: 32, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 8}, + 33: {CoreID: 33, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 8}, + 34: {CoreID: 34, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 8}, + 35: {CoreID: 35, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 8}, + 36: {CoreID: 36, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 9}, + 37: {CoreID: 37, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 9}, + 38: {CoreID: 38, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 9}, + 39: {CoreID: 39, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 9}, + 40: {CoreID: 40, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 10}, + 41: {CoreID: 41, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 10}, + 42: {CoreID: 42, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 10}, + 43: {CoreID: 43, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 10}, + 44: {CoreID: 44, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 11}, + 45: {CoreID: 45, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 11}, + 46: {CoreID: 46, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 11}, + 47: {CoreID: 47, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 11}, + 48: {CoreID: 0, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 49: {CoreID: 1, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 50: {CoreID: 2, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 51: {CoreID: 3, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 0}, + 52: {CoreID: 4, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 53: {CoreID: 5, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 54: {CoreID: 6, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 55: {CoreID: 7, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 1}, + 56: {CoreID: 8, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 57: {CoreID: 9, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 58: {CoreID: 10, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 59: {CoreID: 11, SocketID: 0, NUMANodeID: 0, UncoreCacheID: 2}, + 60: {CoreID: 12, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 61: {CoreID: 13, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 62: {CoreID: 14, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 63: {CoreID: 15, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 3}, + 64: {CoreID: 16, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 4}, + 65: {CoreID: 17, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 4}, + 66: {CoreID: 18, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 4}, + 67: {CoreID: 19, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 4}, + 68: {CoreID: 20, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 5}, + 69: {CoreID: 21, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 5}, + 70: {CoreID: 22, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 5}, + 71: {CoreID: 23, SocketID: 0, NUMANodeID: 1, UncoreCacheID: 5}, + 72: {CoreID: 24, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 6}, + 73: {CoreID: 25, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 6}, + 74: {CoreID: 26, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 6}, + 75: {CoreID: 27, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 6}, + 76: {CoreID: 28, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 7}, + 77: {CoreID: 29, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 7}, + 78: {CoreID: 30, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 7}, + 79: {CoreID: 31, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 7}, + 80: {CoreID: 32, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 8}, + 81: {CoreID: 33, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 8}, + 82: {CoreID: 34, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 8}, + 83: {CoreID: 35, SocketID: 1, NUMANodeID: 2, UncoreCacheID: 8}, + 84: {CoreID: 36, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 9}, + 85: {CoreID: 37, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 9}, + 86: {CoreID: 38, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 9}, + 87: {CoreID: 39, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 9}, + 88: {CoreID: 40, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 10}, + 89: {CoreID: 41, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 10}, + 90: {CoreID: 42, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 10}, + 91: {CoreID: 43, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 10}, + 92: {CoreID: 44, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 11}, + 93: {CoreID: 45, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 11}, + 94: {CoreID: 46, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 11}, + 95: {CoreID: 47, SocketID: 1, NUMANodeID: 3, UncoreCacheID: 11}, + }, + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1100,3 +1414,217 @@ func TestCPUNUMANodeID(t *testing.T) { }) } } + +func TestCPUsInUncoreCaches(t *testing.T) { + tests := []struct { + name string + details CPUDetails + ids []int + want cpuset.CPUSet + }{ + { + name: "Single Uncore Cache", + details: map[int]CPUInfo{ + 0: {UncoreCacheID: 0}, + 1: {UncoreCacheID: 0}, + }, + ids: []int{0}, + want: cpuset.New(0, 1), + }, + { + name: "Multiple Uncore Caches", + details: map[int]CPUInfo{ + 0: {UncoreCacheID: 0}, + 1: {UncoreCacheID: 0}, + 2: {UncoreCacheID: 1}, + }, + ids: []int{0, 1}, + want: cpuset.New(0, 1, 2), + }, + { + name: "Uncore Cache does not exist", + details: map[int]CPUInfo{ + 0: {UncoreCacheID: 0}, + }, + ids: []int{1}, + want: cpuset.New(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.details.CPUsInUncoreCaches(tt.ids...) + if !reflect.DeepEqual(tt.want, got) { + t.Errorf("CPUsInUncoreCaches() = %v, want %v", got, tt.want) + } + }) + } +} +func TestUncoreInNUMANodes(t *testing.T) { + tests := []struct { + name string + details CPUDetails + ids []int + want cpuset.CPUSet + }{ + { + name: "Single NUMA Node", + details: map[int]CPUInfo{ + 0: {NUMANodeID: 0, UncoreCacheID: 0}, + 1: {NUMANodeID: 0, UncoreCacheID: 1}, + }, + ids: []int{0}, + want: cpuset.New(0, 1), + }, + { + name: "Multiple NUMANode", + details: map[int]CPUInfo{ + 0: {NUMANodeID: 0, UncoreCacheID: 0}, + 1: {NUMANodeID: 0, UncoreCacheID: 0}, + 20: {NUMANodeID: 1, UncoreCacheID: 1}, + 21: {NUMANodeID: 1, UncoreCacheID: 1}, + }, + ids: []int{0, 1}, + want: cpuset.New(0, 1), + }, + { + name: "Non-Existent NUMANode", + details: map[int]CPUInfo{ + 0: {NUMANodeID: 1, UncoreCacheID: 0}, + }, + ids: []int{0}, + want: cpuset.New(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.details.UncoreInNUMANodes(tt.ids...) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("UncoreInNUMANodes()= %v, want %v", got, tt.want) + } + }) + + } +} + +func TestUncoreCaches(t *testing.T) { + tests := []struct { + name string + details CPUDetails + want cpuset.CPUSet + }{ + { + name: "Get CPUSet of UncoreCache IDs", + details: map[int]CPUInfo{ + 0: {UncoreCacheID: 0}, + 1: {UncoreCacheID: 1}, + 2: {UncoreCacheID: 2}, + }, + want: cpuset.New(0, 1, 2), + }, + { + name: "Empty CPUDetails", + details: map[int]CPUInfo{}, + want: cpuset.New(), + }, + { + name: "Shared UncoreCache", + details: map[int]CPUInfo{ + 0: {UncoreCacheID: 0}, + 1: {UncoreCacheID: 0}, + 2: {UncoreCacheID: 0}, + }, + want: cpuset.New(0), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.details.UncoreCaches() + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("UncoreCaches() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCPUsPerUncore(t *testing.T) { + tests := []struct { + name string + topo *CPUTopology + want int + }{ + { + name: "Zero Number of UncoreCache", + topo: &CPUTopology{ + NumCPUs: 8, + NumUncoreCache: 0, + }, + want: 0, + }, + { + name: "Normal case", + topo: &CPUTopology{ + NumCPUs: 16, + NumUncoreCache: 2, + }, + want: 8, + }, + { + name: "Single shared UncoreCache", + topo: &CPUTopology{ + NumCPUs: 8, + NumUncoreCache: 1, + }, + want: 8, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.topo.CPUsPerUncore() + if got != tt.want { + t.Errorf("CPUsPerUncore() = %v, want %v", got, tt.want) + } + }) + } + +} +func Test_getUncoreCacheID(t *testing.T) { + tests := []struct { + name string + args cadvisorapi.Core + want int + }{ + { + name: "Core with uncore cache info", + args: cadvisorapi.Core{ + SocketID: 1, + UncoreCaches: []cadvisorapi.Cache{ + {Id: 5}, + {Id: 6}, + }, + }, + want: 5, + }, + { + name: "Core with empty uncore cache info", + args: cadvisorapi.Core{ + SocketID: 2, + UncoreCaches: []cadvisorapi.Cache{}, + }, + want: 2, + }, + { + name: "Core with nil uncore cache info", + args: cadvisorapi.Core{ + SocketID: 1, + }, + want: 1, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getUncoreCacheID(tt.args); got != tt.want { + t.Errorf("getUncoreCacheID() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/kubelet/cm/cpumanager/topology_hints_test.go b/pkg/kubelet/cm/cpumanager/topology_hints_test.go index dc8f0d498340b..3c491ae84df99 100644 --- a/pkg/kubelet/cm/cpumanager/topology_hints_test.go +++ b/pkg/kubelet/cm/cpumanager/topology_hints_test.go @@ -49,16 +49,16 @@ func returnMachineInfo() cadvisorapi.MachineInfo { Topology: []cadvisorapi.Node{ {Id: 0, Cores: []cadvisorapi.Core{ - {SocketID: 0, Id: 0, Threads: []int{0, 6}}, - {SocketID: 0, Id: 1, Threads: []int{1, 7}}, - {SocketID: 0, Id: 2, Threads: []int{2, 8}}, + {SocketID: 0, Id: 0, Threads: []int{0, 6}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 1, Threads: []int{1, 7}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 0, Id: 2, Threads: []int{2, 8}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, }, }, {Id: 1, Cores: []cadvisorapi.Core{ - {SocketID: 1, Id: 0, Threads: []int{3, 9}}, - {SocketID: 1, Id: 1, Threads: []int{4, 10}}, - {SocketID: 1, Id: 2, Threads: []int{5, 11}}, + {SocketID: 1, Id: 0, Threads: []int{3, 9}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 1, Threads: []int{4, 10}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, + {SocketID: 1, Id: 2, Threads: []int{5, 11}, UncoreCaches: []cadvisorapi.Cache{{Id: 1}}}, }, }, }, From 234263863fa48c53578b6f08ec4c06790ff1c54b Mon Sep 17 00:00:00 2001 From: Charles Wong Date: Tue, 5 Nov 2024 13:41:02 -0600 Subject: [PATCH 2/3] UPSTREAM: 126750: update comments (cherry picked from commit b86eb19d309386162ed1b1c220e19053de566025) --- pkg/kubelet/cm/cpumanager/cpu_assignment.go | 6 ++++-- pkg/kubelet/cm/cpumanager/topology/topology.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/kubelet/cm/cpumanager/cpu_assignment.go b/pkg/kubelet/cm/cpumanager/cpu_assignment.go index 180250955023c..b4abf37124f17 100644 --- a/pkg/kubelet/cm/cpumanager/cpu_assignment.go +++ b/pkg/kubelet/cm/cpumanager/cpu_assignment.go @@ -561,11 +561,12 @@ func (a *cpuAccumulator) takeFullUncore() { func (a *cpuAccumulator) takePartialUncore(uncoreID int) { numCoresNeeded := a.numCPUsNeeded / a.topo.CPUsPerCore() - // note: we need to keep the first N free cores (physical cpus) and only we we got these expand to their - // cpus (virtual cpus). Taking directly the first M cpus (virtual cpus) leads to suboptimal allocation + // determine the N number of free cores (physical cpus) within the UncoreCache, then + // determine the M number of free cpus (virtual cpus) that correspond with the free cores freeCores := a.details.CoresNeededInUncoreCache(numCoresNeeded, uncoreID) freeCPUs := a.details.CPUsInCores(freeCores.UnsortedList()...) + // claim the cpus if the free cpus within the UncoreCache can satisfy the needed cpus claimed := (a.numCPUsNeeded == freeCPUs.Size()) klog.V(4).InfoS("takePartialUncore: trying to claim partial uncore", "uncore", uncoreID, @@ -594,6 +595,7 @@ func (a *cpuAccumulator) takeUncoreCache() { return } + // take partial UncoreCache if the CPUs needed is less than free UncoreCache size a.takePartialUncore(uncore) if a.isSatisfied() { return diff --git a/pkg/kubelet/cm/cpumanager/topology/topology.go b/pkg/kubelet/cm/cpumanager/topology/topology.go index 1de03c8fd9c78..18b38233666de 100644 --- a/pkg/kubelet/cm/cpumanager/topology/topology.go +++ b/pkg/kubelet/cm/cpumanager/topology/topology.go @@ -101,7 +101,7 @@ func (topo *CPUTopology) CPUNUMANodeID(cpu int) (int, error) { return info.NUMANodeID, nil } -// CPUInfo contains the NUMA, socket, unCoreCache and core IDs associated with a CPU. +// CPUInfo contains the NUMA, socket, UncoreCache and core IDs associated with a CPU. type CPUInfo struct { NUMANodeID int SocketID int From 6fded69bb1de9bd54d67978cadd9a3948464f662 Mon Sep 17 00:00:00 2001 From: Francesco Romani Date: Tue, 29 Oct 2024 17:31:26 +0100 Subject: [PATCH 3/3] UPSTREAM: : require configuration file enablement similarly to what we do for the managed CPU (aka workload partitioning) feature, introduce a master configuration file `/etc/kubernetes/openshift-llc-alignment` which needs to be present for the LLC alignment feature to be activated, in addition to the policy option being required. Note this replace the standard upstream feature gate check. This can be dropped when the feature per KEP https://github.com/kubernetes/enhancements/issues/4800 goes beta. Signed-off-by: Francesco Romani --- pkg/kubelet/cm/cpumanager/policy_options.go | 20 ++++++ .../cm/cpumanager/policy_options_test.go | 63 +++++++++++++++++++ pkg/kubelet/llcalign/llcalign.go | 46 ++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 pkg/kubelet/llcalign/llcalign.go diff --git a/pkg/kubelet/cm/cpumanager/policy_options.go b/pkg/kubelet/cm/cpumanager/policy_options.go index 58ad86d9dc680..b3e3e10bd444b 100644 --- a/pkg/kubelet/cm/cpumanager/policy_options.go +++ b/pkg/kubelet/cm/cpumanager/policy_options.go @@ -22,9 +22,11 @@ import ( "k8s.io/apimachinery/pkg/util/sets" utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/klog/v2" kubefeatures "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology" "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager" + "k8s.io/kubernetes/pkg/kubelet/llcalign" ) // Names of the options, as part of the user interface. @@ -56,6 +58,14 @@ func CheckPolicyOptionAvailable(option string) error { return fmt.Errorf("unknown CPU Manager Policy option: %q", option) } + // must override the base feature gate check. Relevant only for alpha (disabled by default). + // for beta options are enabled by default and we totally want to keep the possibility to + // disable them explicitly. + if alphaOptions.Has(option) && checkPolicyOptionHasEnablementFile(option) { + // note that we override the decision and shortcut exit with success + // all other cases exit early with failure. + return nil + } if alphaOptions.Has(option) && !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.CPUManagerPolicyAlphaOptions) { return fmt.Errorf("CPU Manager Policy Alpha-level Options not enabled, but option %q provided", option) } @@ -173,3 +183,13 @@ func ValidateStaticPolicyOptions(opts StaticPolicyOptions, topology *topology.CP } return nil } + +func checkPolicyOptionHasEnablementFile(option string) bool { + switch option { + case PreferAlignByUnCoreCacheOption: + val := llcalign.IsEnabled() + klog.InfoS("policy option enablement file check", "option", option, "enablementFile", val) + return val + } + return false +} diff --git a/pkg/kubelet/cm/cpumanager/policy_options_test.go b/pkg/kubelet/cm/cpumanager/policy_options_test.go index 2b14b90429f3e..7b1edc277e01b 100644 --- a/pkg/kubelet/cm/cpumanager/policy_options_test.go +++ b/pkg/kubelet/cm/cpumanager/policy_options_test.go @@ -25,6 +25,7 @@ import ( pkgfeatures "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology" "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager" + "k8s.io/kubernetes/pkg/kubelet/llcalign" ) type optionAvailTest struct { @@ -246,3 +247,65 @@ func TestPolicyOptionsCompatibility(t *testing.T) { }) } } + +func TestPolicyOptionsAvailableWithEnablement(t *testing.T) { + + type optionAvailEnabTest struct { + name string + option string + featureGate featuregate.Feature + featureGateEnable bool + featureEnablementFlag bool + expectedAvailable bool + } + + testCases := []optionAvailEnabTest{ + { + name: "all disabled", + option: PreferAlignByUnCoreCacheOption, + featureGate: pkgfeatures.CPUManagerPolicyAlphaOptions, + featureGateEnable: false, // expected standard case + featureEnablementFlag: false, + expectedAvailable: false, + }, + { + name: "all enabled", + option: PreferAlignByUnCoreCacheOption, + featureGate: pkgfeatures.CPUManagerPolicyAlphaOptions, + featureGateEnable: true, // this should not be allowed by OCP profiles + featureEnablementFlag: true, + expectedAvailable: true, + }, + { + name: "enabled by feature gate", + option: PreferAlignByUnCoreCacheOption, + featureGate: pkgfeatures.CPUManagerPolicyAlphaOptions, + featureGateEnable: true, // this should not be allowed by OCP profiles, makes no sense either + featureEnablementFlag: false, + expectedAvailable: true, + }, + { + name: "enabled by enablement file", + option: PreferAlignByUnCoreCacheOption, + featureGate: pkgfeatures.CPUManagerPolicyAlphaOptions, + featureGateEnable: false, + featureEnablementFlag: true, + expectedAvailable: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, testCase.featureGate, testCase.featureGateEnable) + oldEnablementFlag := llcalign.TestOnlySetEnabled(testCase.featureEnablementFlag) + + err := CheckPolicyOptionAvailable(testCase.option) + + _ = llcalign.TestOnlySetEnabled(oldEnablementFlag) + + isEnabled := (err == nil) + if isEnabled != testCase.expectedAvailable { + t.Errorf("option %q available got=%v expected=%v", testCase.option, isEnabled, testCase.expectedAvailable) + } + }) + } +} diff --git a/pkg/kubelet/llcalign/llcalign.go b/pkg/kubelet/llcalign/llcalign.go new file mode 100644 index 0000000000000..77293dbe52874 --- /dev/null +++ b/pkg/kubelet/llcalign/llcalign.go @@ -0,0 +1,46 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package llcalign + +import ( + "os" +) + +var ( + llcAlignmentEnabled bool + llcAlignmentFilename = "/etc/kubernetes/openshift-llc-alignment" +) + +func init() { + readEnablementFile() +} + +func readEnablementFile() { + if _, err := os.Stat(llcAlignmentFilename); err == nil { + llcAlignmentEnabled = true + } +} + +func IsEnabled() bool { + return llcAlignmentEnabled +} + +func TestOnlySetEnabled(enabled bool) bool { + oldEnabled := llcAlignmentEnabled + llcAlignmentEnabled = enabled + return oldEnabled +}