Skip to content

Commit 50f9ea7

Browse files
authored
Merge pull request kubernetes#85798 from nolancon/merge-policy-rebase
Updated - topologymanager: Add Merge method to Policy
2 parents 6278df2 + 7069b1d commit 50f9ea7

10 files changed

+936
-202
lines changed

pkg/kubelet/cm/topologymanager/policy.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package topologymanager
1818

1919
import (
20+
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
2021
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
2122
)
2223

@@ -28,3 +29,70 @@ type Policy interface {
2829
// and a Pod Admit Handler Response based on hints and policy type
2930
Merge(providersHints []map[string][]TopologyHint) (TopologyHint, lifecycle.PodAdmitResult)
3031
}
32+
33+
// Merge a TopologyHints permutation to a single hint by performing a bitwise-AND
34+
// of their affinity masks. The hint shall be preferred if all hits in the permutation
35+
// are preferred.
36+
func mergePermutation(numaNodes []int, permutation []TopologyHint) TopologyHint {
37+
// Get the NUMANodeAffinity from each hint in the permutation and see if any
38+
// of them encode unpreferred allocations.
39+
preferred := true
40+
defaultAffinity, _ := bitmask.NewBitMask(numaNodes...)
41+
var numaAffinities []bitmask.BitMask
42+
for _, hint := range permutation {
43+
// Only consider hints that have an actual NUMANodeAffinity set.
44+
if hint.NUMANodeAffinity == nil {
45+
numaAffinities = append(numaAffinities, defaultAffinity)
46+
} else {
47+
numaAffinities = append(numaAffinities, hint.NUMANodeAffinity)
48+
}
49+
50+
if !hint.Preferred {
51+
preferred = false
52+
}
53+
}
54+
55+
// Merge the affinities using a bitwise-and operation.
56+
mergedAffinity := bitmask.And(defaultAffinity, numaAffinities...)
57+
// Build a mergedHint from the merged affinity mask, indicating if an
58+
// preferred allocation was used to generate the affinity mask or not.
59+
return TopologyHint{mergedAffinity, preferred}
60+
}
61+
62+
// Iterate over all permutations of hints in 'allProviderHints [][]TopologyHint'.
63+
//
64+
// This procedure is implemented as a recursive function over the set of hints
65+
// in 'allproviderHints[i]'. It applies the function 'callback' to each
66+
// permutation as it is found. It is the equivalent of:
67+
//
68+
// for i := 0; i < len(providerHints[0]); i++
69+
// for j := 0; j < len(providerHints[1]); j++
70+
// for k := 0; k < len(providerHints[2]); k++
71+
// ...
72+
// for z := 0; z < len(providerHints[-1]); z++
73+
// permutation := []TopologyHint{
74+
// providerHints[0][i],
75+
// providerHints[1][j],
76+
// providerHints[2][k],
77+
// ...
78+
// providerHints[-1][z]
79+
// }
80+
// callback(permutation)
81+
func iterateAllProviderTopologyHints(allProviderHints [][]TopologyHint, callback func([]TopologyHint)) {
82+
// Internal helper function to accumulate the permutation before calling the callback.
83+
var iterate func(i int, accum []TopologyHint)
84+
iterate = func(i int, accum []TopologyHint) {
85+
// Base case: we have looped through all providers and have a full permutation.
86+
if i == len(allProviderHints) {
87+
callback(accum)
88+
return
89+
}
90+
91+
// Loop through all hints for provider 'i', and recurse to build the
92+
// the permutation of this hint with all hints from providers 'i++'.
93+
for j := range allProviderHints[i] {
94+
iterate(i+1, append(accum, allProviderHints[i][j]))
95+
}
96+
}
97+
iterate(0, []TopologyHint{})
98+
}

pkg/kubelet/cm/topologymanager/policy_best_effort.go

Lines changed: 4 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -48,54 +48,16 @@ func (p *bestEffortPolicy) canAdmitPodResult(hint *TopologyHint) lifecycle.PodAd
4848
}
4949

5050
func (p *bestEffortPolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, lifecycle.PodAdmitResult) {
51-
hint := mergeProvidersHints(p, p.numaNodes, providersHints)
51+
hint := p.mergeProvidersHints(providersHints)
5252
admit := p.canAdmitPodResult(&hint)
5353
return hint, admit
5454
}
5555

56-
// Iterate over all permutations of hints in 'allProviderHints [][]TopologyHint'.
57-
//
58-
// This procedure is implemented as a recursive function over the set of hints
59-
// in 'allproviderHints[i]'. It applies the function 'callback' to each
60-
// permutation as it is found. It is the equivalent of:
61-
//
62-
// for i := 0; i < len(providerHints[0]); i++
63-
// for j := 0; j < len(providerHints[1]); j++
64-
// for k := 0; k < len(providerHints[2]); k++
65-
// ...
66-
// for z := 0; z < len(providerHints[-1]); z++
67-
// permutation := []TopologyHint{
68-
// providerHints[0][i],
69-
// providerHints[1][j],
70-
// providerHints[2][k],
71-
// ...
72-
// providerHints[-1][z]
73-
// }
74-
// callback(permutation)
75-
func iterateAllProviderTopologyHints(allProviderHints [][]TopologyHint, callback func([]TopologyHint)) {
76-
// Internal helper function to accumulate the permutation before calling the callback.
77-
var iterate func(i int, accum []TopologyHint)
78-
iterate = func(i int, accum []TopologyHint) {
79-
// Base case: we have looped through all providers and have a full permutation.
80-
if i == len(allProviderHints) {
81-
callback(accum)
82-
return
83-
}
84-
85-
// Loop through all hints for provider 'i', and recurse to build the
86-
// the permutation of this hint with all hints from providers 'i++'.
87-
for j := range allProviderHints[i] {
88-
iterate(i+1, append(accum, allProviderHints[i][j]))
89-
}
90-
}
91-
iterate(0, []TopologyHint{})
92-
}
93-
9456
// Merge the hints from all hint providers to find the best one.
95-
func mergeProvidersHints(policy Policy, numaNodes []int, providersHints []map[string][]TopologyHint) TopologyHint {
57+
func (p *bestEffortPolicy) mergeProvidersHints(providersHints []map[string][]TopologyHint) TopologyHint {
9658
// Set the default affinity as an any-numa affinity containing the list
9759
// of NUMA Nodes available on this machine.
98-
defaultAffinity, _ := bitmask.NewBitMask(numaNodes...)
60+
defaultAffinity, _ := bitmask.NewBitMask(p.numaNodes...)
9961

10062
// Loop through all hint providers and save an accumulated list of the
10163
// hints returned by each hint provider. If no hints are provided, assume
@@ -136,33 +98,7 @@ func mergeProvidersHints(policy Policy, numaNodes []int, providersHints []map[st
13698
iterateAllProviderTopologyHints(allProviderHints, func(permutation []TopologyHint) {
13799
// Get the NUMANodeAffinity from each hint in the permutation and see if any
138100
// of them encode unpreferred allocations.
139-
preferred := true
140-
var numaAffinities []bitmask.BitMask
141-
for _, hint := range permutation {
142-
if hint.NUMANodeAffinity == nil {
143-
numaAffinities = append(numaAffinities, defaultAffinity)
144-
} else {
145-
numaAffinities = append(numaAffinities, hint.NUMANodeAffinity)
146-
}
147-
148-
if !hint.Preferred {
149-
preferred = false
150-
}
151-
152-
// Special case PolicySingleNumaNode to only prefer hints where
153-
// all providers have a single NUMA affinity set.
154-
if policy != nil && policy.Name() == PolicySingleNumaNode && hint.NUMANodeAffinity != nil && hint.NUMANodeAffinity.Count() > 1 {
155-
preferred = false
156-
}
157-
}
158-
159-
// Merge the affinities using a bitwise-and operation.
160-
mergedAffinity, _ := bitmask.NewBitMask(numaNodes...)
161-
mergedAffinity.And(numaAffinities...)
162-
163-
// Build a mergedHintfrom the merged affinity mask, indicating if an
164-
// preferred allocation was used to generate the affinity mask or not.
165-
mergedHint := TopologyHint{mergedAffinity, preferred}
101+
mergedHint := mergePermutation(p.numaNodes, permutation)
166102

167103
// Only consider mergedHints that result in a NUMANodeAffinity > 0 to
168104
// replace the current bestHint.

pkg/kubelet/cm/topologymanager/policy_best_effort_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestPolicyBestEffortCanAdmitPodResult(t *testing.T) {
4949
}
5050
}
5151

52-
func TestBestEffortPolicyMerge(t *testing.T) {
52+
func TestPolicyBestEffortMerge(t *testing.T) {
5353
numaNodes := []int{0, 1}
5454
policy := NewBestEffortPolicy(numaNodes)
5555

pkg/kubelet/cm/topologymanager/policy_none_test.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ limitations under the License.
1717
package topologymanager
1818

1919
import (
20+
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
2021
"testing"
2122
)
2223

23-
func TestName(t *testing.T) {
24+
func TestPolicyNoneName(t *testing.T) {
2425
tcases := []struct {
2526
name string
2627
expected string
@@ -65,3 +66,47 @@ func TestPolicyNoneCanAdmitPodResult(t *testing.T) {
6566
}
6667
}
6768
}
69+
70+
func TestPolicyNoneMerge(t *testing.T) {
71+
tcases := []struct {
72+
name string
73+
providersHints []map[string][]TopologyHint
74+
expectedHint TopologyHint
75+
expectedAdmit lifecycle.PodAdmitResult
76+
}{
77+
{
78+
name: "merged empty providers hints",
79+
providersHints: []map[string][]TopologyHint{},
80+
expectedHint: TopologyHint{},
81+
expectedAdmit: lifecycle.PodAdmitResult{Admit: true},
82+
},
83+
{
84+
name: "merge with a single provider with a single preferred resource",
85+
providersHints: []map[string][]TopologyHint{
86+
{
87+
"resource": {{NUMANodeAffinity: NewTestBitMask(0, 1), Preferred: true}},
88+
},
89+
},
90+
expectedHint: TopologyHint{},
91+
expectedAdmit: lifecycle.PodAdmitResult{Admit: true},
92+
},
93+
{
94+
name: "merge with a single provider with a single non-preferred resource",
95+
providersHints: []map[string][]TopologyHint{
96+
{
97+
"resource": {{NUMANodeAffinity: NewTestBitMask(0, 1), Preferred: false}},
98+
},
99+
},
100+
expectedHint: TopologyHint{},
101+
expectedAdmit: lifecycle.PodAdmitResult{Admit: true},
102+
},
103+
}
104+
105+
for _, tc := range tcases {
106+
policy := NewNonePolicy()
107+
result, admit := policy.Merge(tc.providersHints)
108+
if !result.IsEqual(tc.expectedHint) || admit.Admit != tc.expectedAdmit.Admit {
109+
t.Errorf("Test Case: %s: Expected merge hint to be %v, got %v", tc.name, tc.expectedHint, result)
110+
}
111+
}
112+
}

pkg/kubelet/cm/topologymanager/policy_restricted.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func (p *restrictedPolicy) canAdmitPodResult(hint *TopologyHint) lifecycle.PodAd
5252
}
5353

5454
func (p *restrictedPolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, lifecycle.PodAdmitResult) {
55-
hint := mergeProvidersHints(p, p.numaNodes, providersHints)
55+
hint := p.mergeProvidersHints(providersHints)
5656
admit := p.canAdmitPodResult(&hint)
5757
return hint, admit
5858
}

pkg/kubelet/cm/topologymanager/policy_restricted_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ import (
2020
"testing"
2121
)
2222

23+
func TestPolicyRestrictedName(t *testing.T) {
24+
tcases := []struct {
25+
name string
26+
expected string
27+
}{
28+
{
29+
name: "New Restricted Policy",
30+
expected: "restricted",
31+
},
32+
}
33+
for _, tc := range tcases {
34+
policy := NewRestrictedPolicy([]int{0, 1})
35+
if policy.Name() != tc.expected {
36+
t.Errorf("Expected Policy Name to be %s, got %s", tc.expected, policy.Name())
37+
}
38+
}
39+
}
40+
2341
func TestPolicyRestrictedCanAdmitPodResult(t *testing.T) {
2442
tcases := []struct {
2543
name string
@@ -58,7 +76,7 @@ func TestPolicyRestrictedCanAdmitPodResult(t *testing.T) {
5876
}
5977
}
6078

61-
func TestRestrictedPolicyMerge(t *testing.T) {
79+
func TestPolicyRestrictedMerge(t *testing.T) {
6280
numaNodes := []int{0, 1}
6381
policy := NewRestrictedPolicy(numaNodes)
6482

0 commit comments

Comments
 (0)