Updates to single-numa-node policy:

- Remove getHintMatch method.
- Replace with simplified versions of mergePermutation and
iterateAllProviderTopologyHints methods - as used in best-effort.
- Remove getHintMatch unit tests.
This commit is contained in:
nolancon 2019-12-10 05:05:26 +00:00
parent b5ca4989e3
commit e3d0c9397f
2 changed files with 66 additions and 205 deletions

View File

@ -20,7 +20,6 @@ import (
"k8s.io/klog" "k8s.io/klog"
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask" "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
"k8s.io/kubernetes/pkg/kubelet/lifecycle" "k8s.io/kubernetes/pkg/kubelet/lifecycle"
"sort"
) )
type singleNumaNodePolicy struct { type singleNumaNodePolicy struct {
@ -55,49 +54,59 @@ func (p *singleNumaNodePolicy) canAdmitPodResult(hint *TopologyHint) lifecycle.P
} }
} }
// Get the narrowest hint aligned to one NUMA node // Iterate over all permutations of hints in 'allProviderHints [][]TopologyHint'.
// assumption: allResourcesHints are not empty, all hints have NUMAAffinity != nil, //
// all hints have exactly one NUMA node set in NUMANodeAffinity // This procedure is implemented as a recursive function over the set of hints
func (p *singleNumaNodePolicy) getHintMatch(allResourcesHints [][]TopologyHint) (bool, TopologyHint) { // in 'allproviderHints[i]'. It applies the function 'callback' to each
// Sort all resources hints // permutation as it is found. It is the equivalent of:
for _, resource := range allResourcesHints { //
sort.Slice(resource, func(i, j int) bool { // for i := 0; i < len(providerHints[0]); i++
if resource[i].NUMANodeAffinity.GetBits()[0] < resource[j].NUMANodeAffinity.GetBits()[0] { // for j := 0; j < len(providerHints[1]); j++
return true // for k := 0; k < len(providerHints[2]); k++
} // ...
return false // for z := 0; z < len(providerHints[-1]); z++
}) // permutation := []TopologyHint{
// providerHints[0][i],
} // providerHints[1][j],
// find a match by searching a hint of one resource in the rest // providerHints[2][k],
var match TopologyHint // ...
var foundMatch bool // providerHints[-1][z]
// }
if len(allResourcesHints) == 1 { // callback(permutation)
match = allResourcesHints[0][0] func (p *singleNumaNodePolicy) iterateAllProviderTopologyHints(allProviderHints [][]TopologyHint, callback func([]TopologyHint)) {
match.Preferred = true // Internal helper function to accumulate the permutation before calling the callback.
return true, match var iterate func(i int, accum []TopologyHint)
} iterate = func(i int, accum []TopologyHint) {
for _, candidate := range allResourcesHints[0] { // Base case: we have looped through all providers and have a full permutation.
foundMatch = true if i == len(allProviderHints) {
for _, hints := range allResourcesHints[1:] { callback(accum)
res := sort.Search(len(hints), func(i int) bool { return
return hints[i].NUMANodeAffinity.GetBits()[0] >= candidate.NUMANodeAffinity.GetBits()[0]
})
if res >= len(hints) ||
!hints[res].NUMANodeAffinity.IsEqual(candidate.NUMANodeAffinity) {
// hint not found, move to next hint from allResourcesHints[0]
foundMatch = false
break
}
} }
if foundMatch {
match = candidate // Loop through all hints for provider 'i', and recurse to build the
match.Preferred = true // the permutation of this hint with all hints from providers 'i++'.
break for j := range allProviderHints[i] {
iterate(i+1, append(accum, allProviderHints[i][j]))
} }
} }
return foundMatch, match iterate(0, []TopologyHint{})
}
// Merge a TopologyHints permutation to a single hint by performing a bitwise-AND
// of their affinity masks. At this point, all hints have single numa node affinity
// and preferred-true.
func (p *singleNumaNodePolicy) mergePermutation(permutation []TopologyHint) TopologyHint {
preferred := true
var numaAffinities []bitmask.BitMask
for _, hint := range permutation {
numaAffinities = append(numaAffinities, hint.NUMANodeAffinity)
}
// Merge the affinities using a bitwise-and operation.
mergedAffinity, _ := bitmask.NewBitMask(p.numaNodes...)
mergedAffinity.And(numaAffinities...)
// Build a mergedHint from the merged affinity mask.
return TopologyHint{mergedAffinity, preferred}
} }
// Return hints that have valid bitmasks with exactly one bit set. Also return bool // Return hints that have valid bitmasks with exactly one bit set. Also return bool
@ -197,12 +206,22 @@ func (p *singleNumaNodePolicy) mergeProvidersHints(providersHints []map[string][
} }
} }
found, match := p.getHintMatch(allResourcesHints) p.iterateAllProviderTopologyHints(allResourcesHints, func(permutation []TopologyHint) {
if found { mergedHint := p.mergePermutation(permutation)
klog.Infof("[topologymanager] single-numa-node policy match: %v", match) // Only consider mergedHints that result in a NUMANodeAffinity == 1 to
return match // replace the current defaultHint.
} if mergedHint.NUMANodeAffinity.Count() != 1 {
klog.Infof("[topologymanager] single-numa-node no match: %v", defaultHint) return
}
// If the current defaultHint is the same size as the new mergedHint,
// do not update defaultHint
if mergedHint.NUMANodeAffinity.Count() == defaultHint.NUMANodeAffinity.Count() {
return
}
// In all other cases, update defaultHint to the current mergedHint
defaultHint = mergedHint
})
return defaultHint return defaultHint
} }

View File

@ -231,164 +231,6 @@ func TestPolicySingleNumaNodeFilterHints(t *testing.T) {
} }
} }
func TestPolicySingleNumaNodeGetHintMatch(t *testing.T) {
tcases := []struct {
name string
resources [][]TopologyHint
expectedFound bool
expectedMatch TopologyHint
}{
{
name: "match single resource single hint",
resources: [][]TopologyHint{
{
{NUMANodeAffinity: NewTestBitMask(3), Preferred: true},
},
},
expectedFound: true,
expectedMatch: TopologyHint{
NUMANodeAffinity: NewTestBitMask(3),
Preferred: true,
},
},
{
name: "match single resource multiple hints (Selected hint preferred is true) 1/2",
resources: [][]TopologyHint{
{
{NUMANodeAffinity: NewTestBitMask(3), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(5), Preferred: false},
{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(1), Preferred: true},
},
},
expectedFound: true,
expectedMatch: TopologyHint{
NUMANodeAffinity: NewTestBitMask(1),
Preferred: true,
},
},
{
name: "match single resource multiple hints (Selected hint preferred is false) 2/2",
resources: [][]TopologyHint{
{
{NUMANodeAffinity: NewTestBitMask(3), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(5), Preferred: false},
{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(1), Preferred: false},
},
},
expectedFound: true,
expectedMatch: TopologyHint{
NUMANodeAffinity: NewTestBitMask(1),
Preferred: true,
},
},
{
name: "match multiple resource single hint",
resources: [][]TopologyHint{
{
{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
},
{
{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
},
{
{NUMANodeAffinity: NewTestBitMask(2), Preferred: false},
},
},
expectedFound: true,
expectedMatch: TopologyHint{
NUMANodeAffinity: NewTestBitMask(2),
Preferred: true,
},
},
{
name: "match multiple resource single hint no match",
resources: [][]TopologyHint{
{
{NUMANodeAffinity: NewTestBitMask(0), Preferred: true},
},
{
{NUMANodeAffinity: NewTestBitMask(1), Preferred: true},
},
{
{NUMANodeAffinity: NewTestBitMask(2), Preferred: false},
},
},
expectedFound: false,
expectedMatch: TopologyHint{},
},
{
name: "multiple resources no match",
resources: [][]TopologyHint{
{
{NUMANodeAffinity: NewTestBitMask(3), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(4), Preferred: false},
{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(1), Preferred: false},
},
{
{NUMANodeAffinity: NewTestBitMask(3), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(5), Preferred: false},
{NUMANodeAffinity: NewTestBitMask(0), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(1), Preferred: false},
},
{
{NUMANodeAffinity: NewTestBitMask(0), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(5), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(4), Preferred: true},
},
},
expectedFound: false,
expectedMatch: TopologyHint{},
},
{
name: "multiple resources with match",
resources: [][]TopologyHint{
{
{NUMANodeAffinity: NewTestBitMask(3), Preferred: false},
{NUMANodeAffinity: NewTestBitMask(4), Preferred: false},
{NUMANodeAffinity: NewTestBitMask(2), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(1), Preferred: true},
},
{
{NUMANodeAffinity: NewTestBitMask(3), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(5), Preferred: false},
{NUMANodeAffinity: NewTestBitMask(0), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(1), Preferred: false},
},
{
{NUMANodeAffinity: NewTestBitMask(0), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(5), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(4), Preferred: true},
{NUMANodeAffinity: NewTestBitMask(3), Preferred: true},
},
},
expectedFound: true,
expectedMatch: TopologyHint{
NUMANodeAffinity: NewTestBitMask(3),
Preferred: true,
},
},
}
numaNodes := []int{0, 1, 2, 3, 4, 5}
for _, tc := range tcases {
policy := NewSingleNumaNodePolicy(numaNodes)
found, match := policy.(*singleNumaNodePolicy).getHintMatch(tc.resources)
if found != tc.expectedFound {
t.Errorf("Test Case: %s", tc.name)
t.Errorf("Expected found to be %v, got %v", tc.expectedFound, found)
}
if found {
if !match.IsEqual(tc.expectedMatch) {
t.Errorf("Test Case: %s", tc.name)
t.Errorf("Expected match to be %v, got %v", tc.expectedMatch, match)
}
}
}
}
func TestPolicySingleNumaNodeMerge(t *testing.T) { func TestPolicySingleNumaNodeMerge(t *testing.T) {
numaNodes := []int{0, 1} numaNodes := []int{0, 1}
policy := NewSingleNumaNodePolicy(numaNodes) policy := NewSingleNumaNodePolicy(numaNodes)