Skip to content

Commit 9b2540f

Browse files
committed
test: introduce single level iterator lazy load benchmark test
Related to #3248 and #4916
1 parent c07b2f7 commit 9b2540f

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// Copyright 2025 The LevelDB-Go and Pebble Authors. All rights reserved. Use
2+
// of this source code is governed by a BSD-style license that can be found in
3+
// the LICENSE file.
4+
5+
package sstable
6+
7+
import (
8+
"context"
9+
"fmt"
10+
"testing"
11+
12+
"github.com/cockroachdb/pebble/bloom"
13+
"github.com/cockroachdb/pebble/internal/base"
14+
"github.com/cockroachdb/pebble/objstorage/objstorageprovider"
15+
"github.com/cockroachdb/pebble/sstable/block"
16+
"github.com/cockroachdb/pebble/vfs"
17+
)
18+
19+
var (
20+
benchReader *Reader
21+
benchIterOpts IterOptions
22+
)
23+
24+
func init() {
25+
mem := vfs.NewMem()
26+
f, err := mem.Create("bench.sst", vfs.WriteCategoryUnspecified)
27+
if err != nil {
28+
panic(err)
29+
}
30+
31+
w := NewWriter(objstorageprovider.NewFileWritable(f), WriterOptions{
32+
BlockSize: 4096,
33+
IndexBlockSize: 4096,
34+
FilterPolicy: bloom.FilterPolicy(10),
35+
TableFormat: TableFormatPebblev3,
36+
Comparer: base.DefaultComparer,
37+
MergerName: base.DefaultMerger.Name,
38+
})
39+
40+
const numKeys = 10000
41+
for i := range numKeys {
42+
key := fmt.Appendf(nil, "key%08d", i)
43+
value := fmt.Sprintf("value%d", i)
44+
if err := w.Set(key, []byte(value)); err != nil {
45+
panic(err)
46+
}
47+
}
48+
if err := w.Close(); err != nil {
49+
panic(err)
50+
}
51+
52+
f2, err := mem.Open("bench.sst")
53+
if err != nil {
54+
panic(err)
55+
}
56+
57+
benchReader, err = newReader(f2, ReaderOptions{
58+
Comparer: base.DefaultComparer,
59+
Merger: base.DefaultMerger,
60+
})
61+
if err != nil {
62+
panic(err)
63+
}
64+
65+
var stats base.InternalIteratorStats
66+
var bufferPool block.BufferPool
67+
bufferPool.Init(5)
68+
69+
benchIterOpts = IterOptions{
70+
Transforms: NoTransforms,
71+
FilterBlockSizeLimit: NeverUseFilterBlock,
72+
Env: ReadEnv{Block: block.ReadEnv{Stats: &stats, BufferPool: &bufferPool}},
73+
ReaderProvider: MakeTrivialReaderProvider(benchReader),
74+
}
75+
}
76+
77+
// BenchmarkIteratorConstruction measures pure iterator construction performance
78+
// How to: go test -bench=BenchmarkIteratorConstruction -run=^$ ./sstable
79+
func BenchmarkIteratorConstruction(b *testing.B) {
80+
b.ResetTimer()
81+
for i := 0; i < b.N; i++ {
82+
iter, err := newRowBlockSingleLevelIterator(context.Background(), benchReader, benchIterOpts)
83+
if err != nil {
84+
b.Fatal(err)
85+
}
86+
iter.Close()
87+
}
88+
}
89+
90+
// BenchmarkIteratorFirst measures first-access First() call performance
91+
// How to: go test -bench=BenchmarkIteratorFirst -run=^$ ./sstable
92+
func BenchmarkIteratorFirst(b *testing.B) {
93+
b.ResetTimer()
94+
for i := 0; i < b.N; i++ {
95+
b.StopTimer()
96+
iter, err := newRowBlockSingleLevelIterator(context.Background(), benchReader, benchIterOpts)
97+
if err != nil {
98+
b.Fatal(err)
99+
}
100+
b.StartTimer()
101+
_ = iter.First() // Only this is timed
102+
b.StopTimer()
103+
iter.Close()
104+
}
105+
}
106+
107+
// BenchmarkIteratorSeekGE measures first-access SeekGE() call performance
108+
// How to: go test -bench=BenchmarkIteratorSeekGE -run=^$ ./sstable
109+
func BenchmarkIteratorSeekGE(b *testing.B) {
110+
key := []byte("key00005000") // Middle key
111+
b.ResetTimer()
112+
for i := 0; i < b.N; i++ {
113+
b.StopTimer()
114+
iter, err := newRowBlockSingleLevelIterator(context.Background(), benchReader, benchIterOpts)
115+
if err != nil {
116+
b.Fatal(err)
117+
}
118+
b.StartTimer()
119+
_ = iter.SeekGE(key, base.SeekGEFlagsNone) // Only this is timed
120+
b.StopTimer()
121+
iter.Close()
122+
}
123+
}
124+
125+
// BenchmarkIteratorSeekPrefixGE_Hit measures first-access SeekPrefixGE() call performance with existing prefix
126+
// How to: go test -bench=BenchmarkIteratorSeekPrefixGE_Hit -run=^$ ./sstable
127+
func BenchmarkIteratorSeekPrefixGE_Hit(b *testing.B) {
128+
// Setup iterator options with bloom filter enabled
129+
var stats base.InternalIteratorStats
130+
var bufferPool block.BufferPool
131+
bufferPool.Init(5)
132+
defer bufferPool.Release()
133+
134+
iterOpts := IterOptions{
135+
Transforms: NoTransforms,
136+
FilterBlockSizeLimit: AlwaysUseFilterBlock, // Enable bloom filter
137+
Env: ReadEnv{Block: block.ReadEnv{Stats: &stats, BufferPool: &bufferPool}},
138+
ReaderProvider: MakeTrivialReaderProvider(benchReader),
139+
}
140+
141+
// Use existing prefix that will be found
142+
prefix := []byte("key00005000")
143+
144+
b.ResetTimer()
145+
for i := 0; i < b.N; i++ {
146+
b.StopTimer()
147+
iter, err := newRowBlockSingleLevelIterator(context.Background(), benchReader, iterOpts)
148+
if err != nil {
149+
b.Fatal(err)
150+
}
151+
b.StartTimer()
152+
_ = iter.SeekPrefixGE(prefix, prefix, base.SeekGEFlagsNone) // Only this is timed
153+
b.StopTimer()
154+
iter.Close()
155+
}
156+
}
157+
158+
// BenchmarkIteratorSeekPrefixGE_NoHit measures first-access SeekPrefixGE() call performance with non-existing prefix
159+
// How to: go test -bench=BenchmarkIteratorSeekPrefixGE_NoHit -run=^$ ./sstable
160+
func BenchmarkIteratorSeekPrefixGE_NoHit(b *testing.B) {
161+
var stats base.InternalIteratorStats
162+
var bufferPool block.BufferPool
163+
bufferPool.Init(5)
164+
defer bufferPool.Release()
165+
166+
iterOpts := IterOptions{
167+
Transforms: NoTransforms,
168+
FilterBlockSizeLimit: AlwaysUseFilterBlock, // Enable bloom filter
169+
Env: ReadEnv{Block: block.ReadEnv{Stats: &stats, BufferPool: &bufferPool}},
170+
ReaderProvider: MakeTrivialReaderProvider(benchReader),
171+
}
172+
173+
// Use non-existing prefix that will be rejected by bloom filter
174+
prefix := []byte("notfound")
175+
176+
b.ResetTimer()
177+
for i := 0; i < b.N; i++ {
178+
b.StopTimer()
179+
iter, err := newRowBlockSingleLevelIterator(context.Background(), benchReader, iterOpts)
180+
if err != nil {
181+
b.Fatal(err)
182+
}
183+
b.StartTimer()
184+
_ = iter.SeekPrefixGE(prefix, prefix, base.SeekGEFlagsNone) // Only this is timed
185+
b.StopTimer()
186+
iter.Close()
187+
}
188+
}

0 commit comments

Comments
 (0)