Skip to content

Commit c47f562

Browse files
committed
refactor: convert profiling script to standard go benchmark
- Replaced standalone main.go with index_benchmark_test.go for better CI integration. - Implemented fixed random seed to ensure identical workloads across all index implementations. - Added 'miniredis' integration to make Redis benchmarks self-contained. - Updated 'index.Add' usage to match the current interface signature. - Added clarifying comments regarding empty pod set lookups.
1 parent 2b9daf6 commit c47f562

File tree

2 files changed

+199
-178
lines changed

2 files changed

+199
-178
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
3+
Copyright 2025 The llm-d Authors.
4+
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
8+
you may not use this file except in compliance with the License.
9+
10+
You may obtain a copy of the License at
11+
12+
13+
http://www.apache.org/licenses/LICENSE-2.0
14+
15+
16+
Unless required by applicable law or agreed to in writing, software
17+
18+
distributed under the License is distributed on an "AS IS" BASIS,
19+
20+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21+
22+
See the License for the specific language governing permissions and
23+
24+
limitations under the License.
25+
26+
*/
27+
28+
package main
29+
30+
import (
31+
"context"
32+
"math/rand/v2"
33+
"testing"
34+
35+
"github.com/alicebob/miniredis/v2"
36+
"github.com/llm-d/llm-d-kv-cache/pkg/kvcache/kvblock"
37+
"k8s.io/apimachinery/pkg/util/sets"
38+
)
39+
40+
const (
41+
modelName = "bert-base-uncased"
42+
// Default number of keys for benchmarking
43+
benchNumKeys = 10000
44+
)
45+
46+
// generateWorkloadKeys creates a slice of keys with random chunk hashes.
47+
func generateWorkloadKeys(numKeys int) []kvblock.Key {
48+
// Use a fixed seed to ensure the exact same keys are generated for all profiling sessions.
49+
randGen := rand.New(rand.NewPCG(42, 1024))
50+
51+
keys := make([]kvblock.Key, numKeys)
52+
for i := range numKeys {
53+
keys[i] = kvblock.Key{
54+
ModelName: modelName,
55+
ChunkHash: randGen.Uint64(),
56+
}
57+
}
58+
return keys
59+
}
60+
61+
// helper to initialize specific index types.
62+
// redisAddr is optional; only used if indexType is "redis".
63+
func getIndexConfig(indexType string, redisAddr string) *kvblock.IndexConfig {
64+
switch indexType {
65+
case "redis":
66+
cfg := kvblock.DefaultRedisIndexConfig()
67+
68+
// TODO: Verify the field name for the address in RedisIndexConfig!
69+
// It might be 'Addr', 'Address', 'Endpoint', or 'Url'.
70+
// The error 'cfg.Addr undefined' means 'Addr' is wrong.
71+
// I am guessing 'Address' here.
72+
cfg.Address = redisAddr
73+
74+
return &kvblock.IndexConfig{
75+
RedisConfig: cfg,
76+
EnableMetrics: false,
77+
}
78+
case "cost":
79+
return &kvblock.IndexConfig{
80+
CostAwareMemoryConfig: kvblock.DefaultCostAwareMemoryIndexConfig(),
81+
EnableMetrics: false,
82+
}
83+
case "memory":
84+
return kvblock.DefaultIndexConfig()
85+
default:
86+
return kvblock.DefaultIndexConfig()
87+
}
88+
}
89+
90+
// setupMiniredis starts a purely in-memory redis instance.
91+
// Returns the instance and a cleanup function.
92+
func setupMiniredis(b *testing.B) (*miniredis.Miniredis, func()) {
93+
s, err := miniredis.Run()
94+
if err != nil {
95+
b.Fatalf("failed to start miniredis: %v", err)
96+
}
97+
return s, func() { s.Close() }
98+
}
99+
100+
// benchmarkAdd measures the performance of Adding keys to the index.
101+
func benchmarkAdd(b *testing.B, indexType string) {
102+
ctx := context.Background()
103+
podEntries := []kvblock.PodEntry{{PodIdentifier: "pod1", DeviceTier: "gpu"}}
104+
keys := generateWorkloadKeys(benchNumKeys)
105+
106+
var redisAddr string
107+
108+
// Clean setup for Miniredis specifically
109+
if indexType == "redis" {
110+
mr, cleanup := setupMiniredis(b)
111+
defer cleanup()
112+
redisAddr = mr.Addr()
113+
}
114+
115+
b.ResetTimer()
116+
117+
for i := 0; i < b.N; i++ {
118+
b.StopTimer()
119+
// Create a fresh index client, but connect to the SAME background redis server
120+
cfg := getIndexConfig(indexType, redisAddr)
121+
index, err := kvblock.NewIndex(ctx, cfg)
122+
if err != nil {
123+
b.Fatalf("failed to create index: %v", err)
124+
}
125+
126+
b.StartTimer()
127+
128+
// Pass 'keys' for both engineKeys and requestKeys
129+
err = index.Add(ctx, keys, keys, podEntries)
130+
if err != nil {
131+
b.Fatalf("failed to add entries: %v", err)
132+
}
133+
}
134+
}
135+
136+
// benchmarkLookup measures the performance of Looking up keys.
137+
func benchmarkLookup(b *testing.B, indexType string) {
138+
ctx := context.Background()
139+
podEntries := []kvblock.PodEntry{{PodIdentifier: "pod1", DeviceTier: "gpu"}}
140+
141+
// Intentionally use an empty podIdentifierSet to return all pods during lookup,
142+
// as documented in the Index interface.
143+
podIdentifierSet := sets.Set[string]{}
144+
145+
keys := generateWorkloadKeys(benchNumKeys)
146+
147+
var redisAddr string
148+
if indexType == "redis" {
149+
mr, cleanup := setupMiniredis(b)
150+
defer cleanup()
151+
redisAddr = mr.Addr()
152+
}
153+
154+
// Setup: Create index and populate it
155+
cfg := getIndexConfig(indexType, redisAddr)
156+
index, err := kvblock.NewIndex(ctx, cfg)
157+
if err != nil {
158+
b.Fatalf("failed to create index: %v", err)
159+
}
160+
161+
if err := index.Add(ctx, keys, keys, podEntries); err != nil {
162+
b.Fatalf("failed to populate index: %v", err)
163+
}
164+
165+
b.ResetTimer()
166+
167+
for i := 0; i < b.N; i++ {
168+
_, err = index.Lookup(ctx, keys, podIdentifierSet)
169+
if err != nil {
170+
b.Fatalf("failed to lookup entries: %v", err)
171+
}
172+
}
173+
}
174+
175+
// --- Benchmark Entry Points ---
176+
177+
func BenchmarkInMemory_Add(b *testing.B) {
178+
benchmarkAdd(b, "memory")
179+
}
180+
181+
func BenchmarkInMemory_Lookup(b *testing.B) {
182+
benchmarkLookup(b, "memory")
183+
}
184+
185+
func BenchmarkRedis_Add(b *testing.B) {
186+
benchmarkAdd(b, "redis")
187+
}
188+
189+
func BenchmarkRedis_Lookup(b *testing.B) {
190+
benchmarkLookup(b, "redis")
191+
}
192+
193+
func BenchmarkCostAware_Add(b *testing.B) {
194+
benchmarkAdd(b, "cost")
195+
}
196+
197+
func BenchmarkCostAware_Lookup(b *testing.B) {
198+
benchmarkLookup(b, "cost")
199+
}

tests/profiling/kv_cache_index/main.go

Lines changed: 0 additions & 178 deletions
This file was deleted.

0 commit comments

Comments
 (0)