mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 12:15:52 +00:00
Add topology helper and tests to cpumanager.
This commit is contained in:
parent
e4d5565228
commit
242439c9d7
@ -1,9 +1,16 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["doc.go"],
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"topology.go",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/kubelet/cm/cpuset:go_default_library",
|
||||
"//vendor/github.com/google/cadvisor/info/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
@ -19,3 +26,10 @@ filegroup(
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["topology_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = ["//vendor/github.com/google/cadvisor/info/v1:go_default_library"],
|
||||
)
|
||||
|
169
pkg/kubelet/cm/cpumanager/topology/topology.go
Normal file
169
pkg/kubelet/cm/cpumanager/topology/topology.go
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
Copyright 2017 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 topology
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
||||
)
|
||||
|
||||
// CPUDetails is a map from CPU ID to Core ID and Socket ID.
|
||||
type CPUDetails map[int]CPUInfo
|
||||
|
||||
// CPUTopology contains details of node cpu, where :
|
||||
// CPU - logical CPU, cadvisor - thread
|
||||
// Core - physical CPU, cadvisor - Core
|
||||
// Socket - socket, cadvisor - Node
|
||||
type CPUTopology struct {
|
||||
NumCPUs int
|
||||
NumCores int
|
||||
NumSockets int
|
||||
CPUDetails CPUDetails
|
||||
}
|
||||
|
||||
// CPUsPerCore returns the number of logical CPUs are associated with
|
||||
// each core.
|
||||
func (topo *CPUTopology) CPUsPerCore() int {
|
||||
if topo.NumCores == 0 {
|
||||
return 0
|
||||
}
|
||||
return topo.NumCPUs / topo.NumCores
|
||||
}
|
||||
|
||||
// CPUsPerSocket returns the number of logical CPUs are associated with
|
||||
// each socket.
|
||||
func (topo *CPUTopology) CPUsPerSocket() int {
|
||||
if topo.NumSockets == 0 {
|
||||
return 0
|
||||
}
|
||||
return topo.NumCPUs / topo.NumSockets
|
||||
}
|
||||
|
||||
// CPUInfo contains the socket and core IDs associated with a CPU.
|
||||
type CPUInfo struct {
|
||||
SocketID int
|
||||
CoreID int
|
||||
}
|
||||
|
||||
// KeepOnly returns a new CPUDetails object with only the supplied cpus.
|
||||
func (d CPUDetails) KeepOnly(cpus cpuset.CPUSet) CPUDetails {
|
||||
result := CPUDetails{}
|
||||
for cpu, info := range d {
|
||||
if cpus.Contains(cpu) {
|
||||
result[cpu] = info
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Sockets returns all of the socket IDs associated with the CPUs in this
|
||||
// CPUDetails.
|
||||
func (d CPUDetails) Sockets() cpuset.CPUSet {
|
||||
b := cpuset.NewBuilder()
|
||||
for _, info := range d {
|
||||
b.Add(info.SocketID)
|
||||
}
|
||||
return b.Result()
|
||||
}
|
||||
|
||||
// CPUsInSocket returns all of the logical CPU IDs associated with the
|
||||
// given socket ID in this CPUDetails.
|
||||
func (d CPUDetails) CPUsInSocket(id int) cpuset.CPUSet {
|
||||
b := cpuset.NewBuilder()
|
||||
for cpu, info := range d {
|
||||
if info.SocketID == id {
|
||||
b.Add(cpu)
|
||||
}
|
||||
}
|
||||
return b.Result()
|
||||
}
|
||||
|
||||
// Cores returns all of the core IDs associated with the CPUs in this
|
||||
// CPUDetails.
|
||||
func (d CPUDetails) Cores() cpuset.CPUSet {
|
||||
b := cpuset.NewBuilder()
|
||||
for _, info := range d {
|
||||
b.Add(info.CoreID)
|
||||
}
|
||||
return b.Result()
|
||||
}
|
||||
|
||||
// CoresInSocket returns all of the core IDs associated with the given
|
||||
// socket ID in this CPUDetails.
|
||||
func (d CPUDetails) CoresInSocket(id int) cpuset.CPUSet {
|
||||
b := cpuset.NewBuilder()
|
||||
for _, info := range d {
|
||||
if info.SocketID == id {
|
||||
b.Add(info.CoreID)
|
||||
}
|
||||
}
|
||||
return b.Result()
|
||||
}
|
||||
|
||||
// CPUs returns all of the logical CPU IDs in this CPUDetails.
|
||||
func (d CPUDetails) CPUs() cpuset.CPUSet {
|
||||
b := cpuset.NewBuilder()
|
||||
for cpuID := range d {
|
||||
b.Add(cpuID)
|
||||
}
|
||||
return b.Result()
|
||||
}
|
||||
|
||||
// CPUsInCore returns all of the logical CPU IDs associated with the
|
||||
// given core ID in this CPUDetails.
|
||||
func (d CPUDetails) CPUsInCore(id int) cpuset.CPUSet {
|
||||
b := cpuset.NewBuilder()
|
||||
for cpu, info := range d {
|
||||
if info.CoreID == id {
|
||||
b.Add(cpu)
|
||||
}
|
||||
}
|
||||
return b.Result()
|
||||
}
|
||||
|
||||
// Discover returns CPUTopology based on cadvisor node info
|
||||
func Discover(machineInfo *cadvisorapi.MachineInfo) (*CPUTopology, error) {
|
||||
|
||||
if machineInfo.NumCores == 0 {
|
||||
return nil, fmt.Errorf("could not detect number of cpus")
|
||||
}
|
||||
|
||||
CPUDetails := CPUDetails{}
|
||||
|
||||
numCPUs := machineInfo.NumCores
|
||||
numPhysicalCores := 0
|
||||
for _, socket := range machineInfo.Topology {
|
||||
numPhysicalCores += len(socket.Cores)
|
||||
for _, core := range socket.Cores {
|
||||
for _, cpu := range core.Threads {
|
||||
CPUDetails[cpu] = CPUInfo{
|
||||
CoreID: core.Id,
|
||||
SocketID: socket.Id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &CPUTopology{
|
||||
NumCPUs: numCPUs,
|
||||
NumSockets: len(machineInfo.Topology),
|
||||
NumCores: numPhysicalCores,
|
||||
CPUDetails: CPUDetails,
|
||||
}, nil
|
||||
}
|
123
pkg/kubelet/cm/cpumanager/topology/topology_test.go
Normal file
123
pkg/kubelet/cm/cpumanager/topology/topology_test.go
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
Copyright 2017 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 topology
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
|
||||
func Test_Discover(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args *cadvisorapi.MachineInfo
|
||||
want *CPUTopology
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "FailNumCores",
|
||||
args: &cadvisorapi.MachineInfo{
|
||||
NumCores: 0,
|
||||
},
|
||||
want: &CPUTopology{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "OneSocketHT",
|
||||
args: &cadvisorapi.MachineInfo{
|
||||
NumCores: 8,
|
||||
Topology: []cadvisorapi.Node{
|
||||
{Id: 0,
|
||||
Cores: []cadvisorapi.Core{
|
||||
{Id: 0, Threads: []int{0, 4}},
|
||||
{Id: 1, Threads: []int{1, 5}},
|
||||
{Id: 2, Threads: []int{2, 6}},
|
||||
{Id: 3, Threads: []int{3, 7}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &CPUTopology{
|
||||
NumCPUs: 8,
|
||||
NumSockets: 1,
|
||||
NumCores: 4,
|
||||
CPUDetails: map[int]CPUInfo{
|
||||
0: {CoreID: 0, SocketID: 0},
|
||||
1: {CoreID: 1, SocketID: 0},
|
||||
2: {CoreID: 2, SocketID: 0},
|
||||
3: {CoreID: 3, SocketID: 0},
|
||||
4: {CoreID: 0, SocketID: 0},
|
||||
5: {CoreID: 1, SocketID: 0},
|
||||
6: {CoreID: 2, SocketID: 0},
|
||||
7: {CoreID: 3, SocketID: 0},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "DualSocketNoHT",
|
||||
args: &cadvisorapi.MachineInfo{
|
||||
NumCores: 4,
|
||||
Topology: []cadvisorapi.Node{
|
||||
{Id: 0,
|
||||
Cores: []cadvisorapi.Core{
|
||||
{Id: 0, Threads: []int{0}},
|
||||
{Id: 2, Threads: []int{2}},
|
||||
},
|
||||
},
|
||||
{Id: 1,
|
||||
Cores: []cadvisorapi.Core{
|
||||
{Id: 1, Threads: []int{1}},
|
||||
{Id: 3, Threads: []int{3}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &CPUTopology{
|
||||
NumCPUs: 4,
|
||||
NumSockets: 2,
|
||||
NumCores: 4,
|
||||
CPUDetails: map[int]CPUInfo{
|
||||
0: {CoreID: 0, SocketID: 0},
|
||||
1: {CoreID: 1, SocketID: 1},
|
||||
2: {CoreID: 2, SocketID: 0},
|
||||
3: {CoreID: 3, SocketID: 1},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Discover(tt.args)
|
||||
if err != nil {
|
||||
if tt.wantErr {
|
||||
t.Logf("Discover() expected error = %v", err)
|
||||
} else {
|
||||
t.Errorf("Discover() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Discover() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user