Skip to content

Commit 47a61c5

Browse files
authored
Merge pull request kubernetes#130967 from aojea/listers
benchmarks inefficiency on listers linear search lookup
2 parents f2d8eb0 + 33fbce7 commit 47a61c5

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cache
18+
19+
import (
20+
"fmt"
21+
"testing"
22+
23+
"k8s.io/apimachinery/pkg/api/meta"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
"k8s.io/apimachinery/pkg/labels"
26+
)
27+
28+
func TestListAll(t *testing.T) {
29+
testCases := []struct {
30+
name string
31+
numObjects int
32+
numMatching int
33+
matchingLabels map[string]string
34+
selector labels.Selector
35+
expectedCount int
36+
}{
37+
{
38+
name: "subset match",
39+
numObjects: 100000,
40+
numMatching: 10000,
41+
matchingLabels: map[string]string{"match": "true"},
42+
selector: labels.SelectorFromSet(map[string]string{"match": "true"}),
43+
expectedCount: 10000,
44+
},
45+
{
46+
name: "all match",
47+
numObjects: 1000000,
48+
numMatching: 0,
49+
matchingLabels: map[string]string{},
50+
selector: labels.Everything(),
51+
expectedCount: 1000000,
52+
},
53+
{
54+
name: "no match",
55+
numObjects: 1000000,
56+
numMatching: 0,
57+
matchingLabels: map[string]string{"nomatch": "true"},
58+
selector: labels.SelectorFromSet(map[string]string{"match": "true"}),
59+
expectedCount: 0,
60+
},
61+
}
62+
63+
for _, tc := range testCases {
64+
t.Run(tc.name, func(t *testing.T) {
65+
store := mustCreateStore(tc.numObjects, tc.numMatching, tc.matchingLabels)
66+
67+
var matchingObjects int
68+
appendFn := func(obj interface{}) {
69+
matchingObjects++
70+
}
71+
72+
err := ListAll(store, tc.selector, appendFn)
73+
if err != nil {
74+
t.Fatalf("ListAll returned an error: %v", err)
75+
}
76+
77+
if matchingObjects != tc.expectedCount {
78+
t.Errorf("ListAll returned %d objects, expected %d", matchingObjects, tc.expectedCount)
79+
}
80+
})
81+
}
82+
}
83+
84+
func mustCreateStore(numObjects int, numMatching int, labels map[string]string) Store {
85+
if numMatching > numObjects {
86+
panic("there can not be more matches than objects")
87+
}
88+
store := NewStore(func(obj interface{}) (string, error) {
89+
meta, err := meta.Accessor(obj)
90+
if err != nil {
91+
return "", err
92+
}
93+
return meta.GetName(), nil
94+
})
95+
// add matching objects to the store
96+
for i := 0; i < numObjects; i++ {
97+
obj := &metav1.PartialObjectMetadata{
98+
ObjectMeta: metav1.ObjectMeta{
99+
Name: fmt.Sprintf("obj-%d", i),
100+
Labels: map[string]string{},
101+
},
102+
}
103+
if i < numMatching {
104+
obj.Labels = labels
105+
}
106+
err := store.Add(obj)
107+
if err != nil {
108+
panic("unexpected error")
109+
}
110+
}
111+
return store
112+
}
113+
114+
func benchmarkLister(b *testing.B, numObjects int, numMatching int, label map[string]string) {
115+
store := mustCreateStore(numObjects, numMatching, label)
116+
selector := labels.SelectorFromSet(label)
117+
b.ResetTimer()
118+
for n := 0; n < b.N; n++ {
119+
err := ListAll(store, selector, func(m interface{}) {
120+
})
121+
if err != nil {
122+
b.Fatalf("ListAll returned an error: %v", err)
123+
}
124+
}
125+
}
126+
127+
func BenchmarkLister_Match_1k_100(b *testing.B) {
128+
benchmarkLister(b, 1000, 100, map[string]string{"match": "true"})
129+
}
130+
func BenchmarkLister_Match_10k_100(b *testing.B) {
131+
benchmarkLister(b, 10000, 100, map[string]string{"match": "true"})
132+
}
133+
134+
func BenchmarkLister_Match_100k_100(b *testing.B) {
135+
benchmarkLister(b, 100000, 100, map[string]string{"match": "true"})
136+
}
137+
138+
func BenchmarkLister_Match_1M_100(b *testing.B) {
139+
benchmarkLister(b, 1000000, 100, map[string]string{"match": "true"})
140+
}

0 commit comments

Comments
 (0)