Skip to content

Commit 65445b8

Browse files
committed
rowblk: add cockroach microbenchmarks
Add microbenchmarks for block building and iteration that use Cockroach MVCC keys. These microbenchmarks will provide a comparison point for colblk equivalents.
1 parent cda4471 commit 65445b8

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

internal/crdbtest/crdbtest.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ import (
1010
"bytes"
1111
"cmp"
1212
"encoding/binary"
13+
"fmt"
14+
"slices"
15+
"time"
1316

1417
"github.com/cockroachdb/errors"
1518
"github.com/cockroachdb/pebble/internal/base"
19+
"golang.org/x/exp/rand"
1620
)
1721

1822
const withWall = 9
@@ -303,3 +307,45 @@ func getKeyPartFromEngineKey(engineKey []byte) (key []byte, ok bool) {
303307
// Key excludes the sentinel byte.
304308
return engineKey[:keyPartEnd], true
305309
}
310+
311+
// KeyConfig configures the shape of the random keys generated.
312+
type KeyConfig struct {
313+
PrefixAlphabetLen int // Number of bytes in the alphabet used for the prefix.
314+
PrefixLenShared int // Number of bytes shared by all key prefixes.
315+
PrefixLen int // Number of bytes in the prefix.
316+
BaseWallTime uint64 // Smallest MVCC WallTime.
317+
Logical uint32 // MVCC logical time for all keys.
318+
}
319+
320+
func (cfg KeyConfig) String() string {
321+
return fmt.Sprintf("AlphaLen=%d,Shared=%d,PrefixLen=%d,Logical=%d",
322+
cfg.PrefixAlphabetLen, cfg.PrefixLenShared, cfg.PrefixLen, cfg.Logical)
323+
}
324+
325+
// RandomKVs constructs count random KVs with the provided parameters.
326+
func RandomKVs(rng *rand.Rand, count int, cfg KeyConfig, valueLen int) (keys, vals [][]byte) {
327+
sharedPrefix := make([]byte, cfg.PrefixLenShared)
328+
for i := 0; i < len(sharedPrefix); i++ {
329+
sharedPrefix[i] = byte(rng.Intn(cfg.PrefixAlphabetLen) + 'a')
330+
}
331+
332+
keys = make([][]byte, count)
333+
vals = make([][]byte, count)
334+
for i := range keys {
335+
keys[i] = randCockroachKey(rng, cfg, sharedPrefix)
336+
vals[i] = make([]byte, valueLen)
337+
rng.Read(vals[i])
338+
}
339+
slices.SortFunc(keys, Compare)
340+
return keys, vals
341+
}
342+
343+
func randCockroachKey(rng *rand.Rand, cfg KeyConfig, blockPrefix []byte) []byte {
344+
key := make([]byte, 0, cfg.PrefixLen+MaxSuffixLen)
345+
key = append(key, blockPrefix...)
346+
wallTime := cfg.BaseWallTime + rng.Uint64n(uint64(time.Hour))
347+
for len(key) < cfg.PrefixLen {
348+
key = append(key, byte(rng.Intn(cfg.PrefixAlphabetLen)+'a'))
349+
}
350+
return EncodeTimestamp(key, wallTime, cfg.Logical)
351+
}

sstable/rowblk/rowblk_bench_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/cockroachdb/pebble/internal/base"
14+
"github.com/cockroachdb/pebble/internal/crdbtest"
1415
"github.com/cockroachdb/pebble/internal/testkeys"
1516
"github.com/cockroachdb/pebble/sstable/block"
1617
"golang.org/x/exp/rand"
@@ -213,3 +214,131 @@ func BenchmarkBlockIterPrev(b *testing.B) {
213214
}
214215
}
215216
}
217+
218+
func BenchmarkCockroachDataBlockWriter(b *testing.B) {
219+
for _, alphaLen := range []int{4, 8, 26} {
220+
for _, lenSharedPct := range []float64{0.25, 0.5} {
221+
for _, prefixLen := range []int{8, 32, 128} {
222+
lenShared := int(float64(prefixLen) * lenSharedPct)
223+
for _, valueLen := range []int{8, 128, 1024} {
224+
keyConfig := crdbtest.KeyConfig{
225+
PrefixAlphabetLen: alphaLen,
226+
PrefixLen: prefixLen,
227+
PrefixLenShared: lenShared,
228+
Logical: 0,
229+
BaseWallTime: uint64(time.Now().UnixNano()),
230+
}
231+
b.Run(fmt.Sprintf("%s,valueLen=%d", keyConfig, valueLen), func(b *testing.B) {
232+
benchmarkCockroachDataBlockWriter(b, keyConfig, valueLen)
233+
})
234+
}
235+
}
236+
}
237+
}
238+
}
239+
240+
func benchmarkCockroachDataBlockWriter(b *testing.B, keyConfig crdbtest.KeyConfig, valueLen int) {
241+
const targetBlockSize = 32 << 10
242+
seed := uint64(time.Now().UnixNano())
243+
rng := rand.New(rand.NewSource(seed))
244+
keys, values := crdbtest.RandomKVs(rng, targetBlockSize/valueLen, keyConfig, valueLen)
245+
246+
var w Writer
247+
w.RestartInterval = 16
248+
b.ResetTimer()
249+
for i := 0; i < b.N; i++ {
250+
w.Reset()
251+
var j int
252+
var prevKeyLen int
253+
for w.EstimatedSize() < targetBlockSize {
254+
ik := base.MakeInternalKey(keys[j], base.SeqNum(rng.Uint64n(uint64(base.SeqNumMax))), base.InternalKeyKindSet)
255+
var samePrefix bool
256+
if j > 0 {
257+
samePrefix = bytes.Equal(keys[j], keys[j-1])
258+
}
259+
w.AddWithOptionalValuePrefix(
260+
ik, false, values[j], prevKeyLen, true, block.InPlaceValuePrefix(samePrefix), samePrefix)
261+
j++
262+
prevKeyLen = len(ik.UserKey)
263+
}
264+
w.Finish()
265+
}
266+
}
267+
268+
func BenchmarkCockroachDataBlockIter(b *testing.B) {
269+
for _, alphaLen := range []int{4, 8, 26} {
270+
for _, lenSharedPct := range []float64{0.25, 0.5} {
271+
for _, prefixLen := range []int{8, 32, 128} {
272+
lenShared := int(float64(prefixLen) * lenSharedPct)
273+
for _, logical := range []uint32{0, 1} {
274+
for _, valueLen := range []int{8, 128, 1024} {
275+
keyConfig := crdbtest.KeyConfig{
276+
PrefixAlphabetLen: alphaLen,
277+
PrefixLen: prefixLen,
278+
PrefixLenShared: lenShared,
279+
Logical: logical,
280+
BaseWallTime: uint64(time.Now().UnixNano()),
281+
}
282+
b.Run(fmt.Sprintf("%s,value=%d", keyConfig, valueLen),
283+
func(b *testing.B) {
284+
benchmarkCockroachDataBlockIter(b, keyConfig, valueLen)
285+
})
286+
}
287+
}
288+
}
289+
}
290+
}
291+
}
292+
293+
func benchmarkCockroachDataBlockIter(b *testing.B, keyConfig crdbtest.KeyConfig, valueLen int) {
294+
const targetBlockSize = 32 << 10
295+
seed := uint64(time.Now().UnixNano())
296+
rng := rand.New(rand.NewSource(seed))
297+
keys, values := crdbtest.RandomKVs(rng, targetBlockSize/valueLen, keyConfig, valueLen)
298+
299+
var w Writer
300+
w.RestartInterval = 16
301+
var count int
302+
var prevKeyLen int
303+
for w.EstimatedSize() < targetBlockSize {
304+
ik := base.MakeInternalKey(keys[count], base.SeqNum(rng.Uint64n(uint64(base.SeqNumMax))), base.InternalKeyKindSet)
305+
var samePrefix bool
306+
if count > 0 {
307+
samePrefix = bytes.Equal(keys[count], keys[count-1])
308+
}
309+
w.AddWithOptionalValuePrefix(
310+
ik, false, values[count], prevKeyLen, true, block.InPlaceValuePrefix(samePrefix), samePrefix)
311+
count++
312+
prevKeyLen = len(ik.UserKey)
313+
}
314+
serializedBlock := w.Finish()
315+
var it Iter
316+
it.Init(crdbtest.Compare, crdbtest.Split, serializedBlock, block.NoTransforms)
317+
avgRowSize := float64(len(serializedBlock)) / float64(count)
318+
319+
b.Run("Next", func(b *testing.B) {
320+
kv := it.First()
321+
b.ResetTimer()
322+
for i := 0; i < b.N; i++ {
323+
if kv == nil {
324+
kv = it.First()
325+
} else {
326+
kv = it.Next()
327+
}
328+
}
329+
b.StopTimer()
330+
b.ReportMetric(avgRowSize, "bytes/row")
331+
})
332+
b.Run("SeekGE", func(b *testing.B) {
333+
rng := rand.New(rand.NewSource(seed))
334+
b.ResetTimer()
335+
for i := 0; i < b.N; i++ {
336+
k := keys[rng.Intn(count)]
337+
if kv := it.SeekGE(k, base.SeekGEFlagsNone); kv == nil {
338+
b.Fatalf("%q not found", k)
339+
}
340+
}
341+
b.StopTimer()
342+
b.ReportMetric(avgRowSize, "bytes/row")
343+
})
344+
}

0 commit comments

Comments
 (0)