Optimize NDF FeatureMapper.Unmap for sparse feature sets

This commit is contained in:
Tim Allclair
2026-02-25 06:04:30 +00:00
parent b1b75f93d7
commit 39a4f7654a
5 changed files with 36 additions and 14 deletions

View File

@@ -1308,14 +1308,20 @@ func TestSetNodeDeclaredFeatures(t *testing.T) {
gotFeatures := ni.GetNodeDeclaredFeatures()
if !tt.featureGateEnabled {
if !gotFeatures.IsEmpty() {
got := ndfFramework.Unmap(gotFeatures)
got, err := ndfFramework.Unmap(gotFeatures)
if err != nil {
t.Fatalf("Failed to unmap features: %v", err)
}
t.Errorf("Expected GetNodeDeclaredFeatures() to return nil; got %v", got)
}
return
}
expected := ndfFramework.MustMapSorted(tt.expectedFeatures)
if !gotFeatures.Equal(expected) {
got := ndfFramework.Unmap(gotFeatures)
got, err := ndfFramework.Unmap(gotFeatures)
if err != nil {
t.Fatalf("Failed to unmap features: %v", err)
}
t.Errorf("SetNode() or GetNodeDeclaredFeatures() unexpected result, got: %v, want: %v", got, tt.expectedFeatures)
}
})

View File

@@ -18,6 +18,7 @@ package nodedeclaredfeatures
import (
"fmt"
"math/bits"
"slices"
)
@@ -91,16 +92,19 @@ func (m *FeatureMapper) mapSorted(sortedFeatures []string, ignoreUnknown bool) (
}
// Unmap returns the names of the features set in the FeatureSet (sorted).
func (m *FeatureMapper) Unmap(s FeatureSet) []string {
if s.IsEmpty() {
return nil
func (m *FeatureMapper) Unmap(s FeatureSet) ([]string, error) {
if s.size != len(m.registeredFeatures) {
return nil, fmt.Errorf("feature size mismatch! s.size=%d; len(m.registeredFeatures)=%d", s.size, len(m.registeredFeatures))
}
var keys []string
for i, k := range m.registeredFeatures {
if s.Get(i) {
keys = append(keys, k)
for i, w := range s.words {
for w != 0 {
t := bits.TrailingZeros64(w) // Find the index of the least significant bit.
index := i*64 + t
keys = append(keys, m.registeredFeatures[index])
w &^= 1 << uint(t) // Clear the least significant bit.
}
}
return keys
return keys, nil
}

View File

@@ -117,8 +117,11 @@ func TestFeatureMapper(t *testing.T) {
}
}
// Test Unmap
unmapped := mapper.Unmap(fs)
// Test Unmap and UnmapSparse
unmapped, err := mapper.Unmap(fs)
if err != nil {
t.Fatalf("unexpected Unmap error: %v", err)
}
if len(unmapped) != tt.expectedSet.Len() {
t.Errorf("len(unmapped) = %d, want %d", len(unmapped), tt.expectedSet.Len())
}

View File

@@ -143,7 +143,10 @@ func (f *Framework) MatchNodeFeatureSet(requiredFeatures FeatureSet, nodeFeature
}
diff, _ := requiredFeatures.Difference(nodeFeatures) // Difference will error IFF IsSubset also does, so we can ignore the error here.
unsatisfiedRequirements := f.Unmap(diff)
unsatisfiedRequirements, err := f.Unmap(diff)
if err != nil {
return nil, err
}
return &MatchResult{IsMatch: false, UnsatisfiedRequirements: unsatisfiedRequirements}, nil
}

View File

@@ -318,7 +318,10 @@ func TestInferForPodScheduling(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error %v", err)
}
unmappedReqs := framework.Unmap(reqs)
unmappedReqs, err := framework.Unmap(reqs)
if err != nil {
t.Fatalf("unexpected error %v", err)
}
if !reflect.DeepEqual(tc.expectedReqs, unmappedReqs) {
t.Errorf("expected %#v, got %#v", tc.expectedReqs, unmappedReqs)
}
@@ -467,7 +470,10 @@ func TestInferForPodUpdate(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error %v", err)
}
unmappedReqs := framework.Unmap(reqs)
unmappedReqs, err := framework.Unmap(reqs)
if err != nil {
t.Fatalf("unexpected error %v", err)
}
if !reflect.DeepEqual(tc.expectedReqs, unmappedReqs) {
t.Errorf("expected %#v, got %#v", tc.expectedReqs, unmappedReqs)
}