Skip to content

Commit 945a439

Browse files
committed
sstable: add synthetic prefix block level benchmarks
The block level iteration benchmarks suggest that block level prefix synthesis does not significantly slow down iteration, compared to block level iteration on a block with the prefix prewritten to the key. ``` goos: linux goarch: amd64 pkg: github.com/cockroachdb/pebble/sstable cpu: Intel(R) Xeon(R) CPU @ 2.80GHz │ post.txt │ │ sec/op │ BlockIterSeekGE/syntheticPrefix=false;syntheticSuffix=false;restart=16-24 481.4n ± 3% BlockIterSeekGE/syntheticPrefix=false;syntheticSuffix=true;restart=16-24 569.7n ± 14% BlockIterSeekGE/syntheticPrefix=true;syntheticSuffix=false;restart=16-24 497.9n ± 2% BlockIterSeekGE/syntheticPrefix=true;syntheticSuffix=true;restart=16-24 604.8n ± 7% BlockIterSeekLT/syntheticPrefix=false;syntheticSuffix=false;restart=16-24 548.9n ± 3% BlockIterSeekLT/syntheticPrefix=false;syntheticSuffix=true;restart=16-24 720.1n ± 1% BlockIterSeekLT/syntheticPrefix=true;syntheticSuffix=false;restart=16-24 535.1n ± 3% BlockIterSeekLT/syntheticPrefix=true;syntheticSuffix=true;restart=16-24 713.4n ± 2% BlockIterNext/syntheticPrefix=false;syntheticSuffix=false;restart=16-24 15.90n ± 0% BlockIterNext/syntheticPrefix=false;syntheticSuffix=true;restart=16-24 22.92n ± 14% BlockIterNext/syntheticPrefix=true;syntheticSuffix=false;restart=16-24 15.44n ± 0% BlockIterNext/syntheticPrefix=true;syntheticSuffix=true;restart=16-24 22.15n ± 15% BlockIterPrev/syntheticPrefix=false;syntheticSuffix=false;restart=16-24 31.43n ± 0% BlockIterPrev/syntheticPrefix=false;syntheticSuffix=true;restart=16-24 42.23n ± 1% BlockIterPrev/syntheticPrefix=true;syntheticSuffix=false;restart=16-24 31.35n ± 0% BlockIterPrev/syntheticPrefix=true;syntheticSuffix=true;restart=16-24 41.81n ± 2% ```
1 parent cea7417 commit 945a439

File tree

1 file changed

+138
-117
lines changed

1 file changed

+138
-117
lines changed

sstable/block_test.go

Lines changed: 138 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ func TestBlockSyntheticSuffix(t *testing.T) {
585585

586586
var (
587587
benchSynthSuffix = []byte("@15")
588+
benchPrefix = []byte("2_")
588589

589590
// Use testkeys.Comparer.Compare which approximates EngineCompare by ordering
590591
// multiple keys with same prefix in descending suffix order.
@@ -602,168 +603,188 @@ func chooseOrigSuffix(rng *rand.Rand) []byte {
602603
return origSuffix
603604
}
604605

605-
func createBenchBlock(blockSize int, w *blockWriter, rng *rand.Rand) [][]byte {
606+
// createBenchBlock writes a block of keys and outputs a list of keys that will
607+
// be surfaced from the block, and the expected synthetic suffix and prefix the
608+
// block should be read with.
609+
func createBenchBlock(
610+
blockSize int, w *blockWriter, rng *rand.Rand, withSyntheticPrefix, withSyntheticSuffix bool,
611+
) ([][]byte, []byte, []byte) {
612+
606613
origSuffix := chooseOrigSuffix(rng)
607614
var ikey InternalKey
608-
var keys [][]byte
615+
var readKeys [][]byte
616+
617+
var writtenPrefix []byte
618+
if !withSyntheticPrefix {
619+
// If the keys will not be read with a synthetic prefix, write the prefix to
620+
// the block for a more comparable benchmark comparison between a block iter
621+
// with and without prefix synthesis.
622+
writtenPrefix = benchPrefix
623+
}
609624
for i := 0; w.estimatedSize() < blockSize; i++ {
610-
key := []byte(fmt.Sprintf("%05d%s", i, origSuffix))
625+
key := []byte(fmt.Sprintf("%s%05d%s", string(writtenPrefix), i, origSuffix))
611626
ikey.UserKey = key
612627
w.add(ikey, nil)
613-
keys = append(keys, key)
628+
var readKey []byte
629+
if withSyntheticPrefix {
630+
readKey = append(readKey, benchPrefix...)
631+
}
632+
readKey = append(readKey, key...)
633+
readKeys = append(readKeys, readKey)
614634
}
615-
return keys
635+
636+
var syntheticSuffix []byte
637+
var syntheticPrefix []byte
638+
if withSyntheticSuffix {
639+
syntheticSuffix = benchSynthSuffix
640+
}
641+
if withSyntheticPrefix {
642+
syntheticPrefix = []byte(benchPrefix)
643+
}
644+
return readKeys, syntheticPrefix, syntheticSuffix
616645
}
617646

618647
func BenchmarkBlockIterSeekGE(b *testing.B) {
619648
const blockSize = 32 << 10
620-
for _, withSyntheticSuffix := range []bool{false, true} {
621-
for _, restartInterval := range []int{16} {
622-
b.Run(fmt.Sprintf("syntheticSuffix=%t;restart=%d", withSyntheticSuffix, restartInterval),
623-
func(b *testing.B) {
624-
w := &blockWriter{
625-
restartInterval: restartInterval,
626-
}
627-
rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
649+
for _, withSyntheticPrefix := range []bool{false, true} {
650+
for _, withSyntheticSuffix := range []bool{false, true} {
651+
for _, restartInterval := range []int{16} {
652+
b.Run(fmt.Sprintf("syntheticPrefix=%t;syntheticSuffix=%t;restart=%d", withSyntheticPrefix, withSyntheticSuffix, restartInterval),
653+
func(b *testing.B) {
654+
w := &blockWriter{
655+
restartInterval: restartInterval,
656+
}
657+
rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
628658

629-
keys := createBenchBlock(blockSize, w, rng)
630-
var syntheticSuffix []byte
631-
if withSyntheticSuffix {
632-
syntheticSuffix = benchSynthSuffix
633-
}
659+
keys, syntheticPrefix, syntheticSuffix := createBenchBlock(blockSize, w, rng, withSyntheticPrefix, withSyntheticSuffix)
634660

635-
it, err := newBlockIter(benchCmp, benchSplit, w.finish(), IterTransforms{SyntheticSuffix: syntheticSuffix})
636-
if err != nil {
637-
b.Fatal(err)
638-
}
639-
b.ResetTimer()
640-
for i := 0; i < b.N; i++ {
641-
k := keys[rng.Intn(len(keys))]
642-
it.SeekGE(k, base.SeekGEFlagsNone)
643-
if testing.Verbose() {
644-
if !it.valid() && !withSyntheticSuffix {
645-
b.Fatal("expected to find key")
646-
}
647-
if !bytes.Equal(k, it.Key().UserKey) && !withSyntheticSuffix {
648-
b.Fatalf("expected %s, but found %s", k, it.Key().UserKey)
661+
it, err := newBlockIter(benchCmp, benchSplit, w.finish(), IterTransforms{SyntheticSuffix: syntheticSuffix, SyntheticPrefix: syntheticPrefix})
662+
if err != nil {
663+
b.Fatal(err)
664+
}
665+
b.ResetTimer()
666+
for i := 0; i < b.N; i++ {
667+
k := keys[rng.Intn(len(keys))]
668+
it.SeekGE(k, base.SeekGEFlagsNone)
669+
if testing.Verbose() {
670+
if !it.valid() && !withSyntheticSuffix {
671+
b.Fatal("expected to find key")
672+
}
673+
if !bytes.Equal(k, it.Key().UserKey) && !withSyntheticSuffix {
674+
b.Fatalf("expected %s, but found %s", k, it.Key().UserKey)
675+
}
649676
}
650677
}
651-
}
652-
})
678+
})
679+
}
653680
}
654681
}
655682
}
656683

657684
func BenchmarkBlockIterSeekLT(b *testing.B) {
658685
const blockSize = 32 << 10
659-
for _, withSyntheticSuffix := range []bool{false, true} {
660-
for _, restartInterval := range []int{16} {
661-
b.Run(fmt.Sprintf("syntheticSuffix=%t;restart=%d", withSyntheticSuffix, restartInterval),
662-
func(b *testing.B) {
663-
w := &blockWriter{
664-
restartInterval: restartInterval,
665-
}
666-
rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
686+
for _, withSyntheticPrefix := range []bool{false, true} {
687+
for _, withSyntheticSuffix := range []bool{false, true} {
688+
for _, restartInterval := range []int{16} {
689+
b.Run(fmt.Sprintf("syntheticPrefix=%t;syntheticSuffix=%t;restart=%d", withSyntheticPrefix, withSyntheticSuffix, restartInterval),
690+
func(b *testing.B) {
691+
w := &blockWriter{
692+
restartInterval: restartInterval,
693+
}
694+
rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
667695

668-
keys := createBenchBlock(blockSize, w, rng)
669-
var syntheticSuffix []byte
670-
if withSyntheticSuffix {
671-
syntheticSuffix = benchSynthSuffix
672-
}
696+
keys, syntheticPrefix, syntheticSuffix := createBenchBlock(blockSize, w, rng, withSyntheticPrefix, withSyntheticSuffix)
673697

674-
it, err := newBlockIter(benchCmp, benchSplit, w.finish(), IterTransforms{SyntheticSuffix: syntheticSuffix})
675-
if err != nil {
676-
b.Fatal(err)
677-
}
678-
b.ResetTimer()
679-
for i := 0; i < b.N; i++ {
680-
j := rng.Intn(len(keys))
681-
it.SeekLT(keys[j], base.SeekLTFlagsNone)
682-
if testing.Verbose() {
683-
if j == 0 {
684-
if it.valid() && !withSyntheticSuffix {
685-
b.Fatal("unexpected key")
686-
}
687-
} else {
688-
if !it.valid() && !withSyntheticSuffix {
689-
b.Fatal("expected to find key")
690-
}
691-
k := keys[j-1]
692-
if !bytes.Equal(k, it.Key().UserKey) && !withSyntheticSuffix {
693-
b.Fatalf("expected %s, but found %s", k, it.Key().UserKey)
698+
it, err := newBlockIter(benchCmp, benchSplit, w.finish(), IterTransforms{SyntheticSuffix: syntheticSuffix, SyntheticPrefix: syntheticPrefix})
699+
if err != nil {
700+
b.Fatal(err)
701+
}
702+
b.ResetTimer()
703+
for i := 0; i < b.N; i++ {
704+
j := rng.Intn(len(keys))
705+
it.SeekLT(keys[j], base.SeekLTFlagsNone)
706+
if testing.Verbose() {
707+
if j == 0 {
708+
if it.valid() && !withSyntheticSuffix {
709+
b.Fatal("unexpected key")
710+
}
711+
} else {
712+
if !it.valid() && !withSyntheticSuffix {
713+
b.Fatal("expected to find key")
714+
}
715+
k := keys[j-1]
716+
if !bytes.Equal(k, it.Key().UserKey) && !withSyntheticSuffix {
717+
b.Fatalf("expected %s, but found %s", k, it.Key().UserKey)
718+
}
694719
}
695720
}
696721
}
697-
}
698-
})
722+
})
723+
}
699724
}
700725
}
701726
}
702727

703728
func BenchmarkBlockIterNext(b *testing.B) {
704729
const blockSize = 32 << 10
705-
for _, withSyntheticSuffix := range []bool{false, true} {
706-
for _, restartInterval := range []int{16} {
707-
b.Run(fmt.Sprintf("syntheticSuffix=%t;restart=%d", withSyntheticSuffix, restartInterval),
708-
func(b *testing.B) {
709-
w := &blockWriter{
710-
restartInterval: restartInterval,
711-
}
712-
rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
730+
for _, withSyntheticPrefix := range []bool{false, true} {
731+
for _, withSyntheticSuffix := range []bool{false, true} {
732+
for _, restartInterval := range []int{16} {
733+
b.Run(fmt.Sprintf("syntheticPrefix=%t;syntheticSuffix=%t;restart=%d", withSyntheticPrefix, withSyntheticSuffix, restartInterval),
734+
func(b *testing.B) {
735+
w := &blockWriter{
736+
restartInterval: restartInterval,
737+
}
738+
rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
713739

714-
createBenchBlock(blockSize, w, rng)
715-
var syntheticSuffix []byte
716-
if withSyntheticSuffix {
717-
syntheticSuffix = benchSynthSuffix
718-
}
740+
_, syntheticPrefix, syntheticSuffix := createBenchBlock(blockSize, w, rng, withSyntheticPrefix, withSyntheticSuffix)
719741

720-
it, err := newBlockIter(benchCmp, benchSplit, w.finish(), IterTransforms{SyntheticSuffix: syntheticSuffix})
721-
if err != nil {
722-
b.Fatal(err)
723-
}
742+
it, err := newBlockIter(benchCmp, benchSplit, w.finish(), IterTransforms{SyntheticSuffix: syntheticSuffix, SyntheticPrefix: syntheticPrefix})
743+
if err != nil {
744+
b.Fatal(err)
745+
}
724746

725-
b.ResetTimer()
726-
for i := 0; i < b.N; i++ {
727-
if !it.valid() {
728-
it.First()
747+
b.ResetTimer()
748+
for i := 0; i < b.N; i++ {
749+
if !it.valid() {
750+
it.First()
751+
}
752+
it.Next()
729753
}
730-
it.Next()
731-
}
732-
})
754+
})
755+
}
733756
}
734757
}
735758
}
736759

737760
func BenchmarkBlockIterPrev(b *testing.B) {
738761
const blockSize = 32 << 10
739-
for _, withSyntheticSuffix := range []bool{false, true} {
740-
for _, restartInterval := range []int{16} {
741-
b.Run(fmt.Sprintf("syntheticSuffix=%t;restart=%d", withSyntheticSuffix, restartInterval),
742-
func(b *testing.B) {
743-
w := &blockWriter{
744-
restartInterval: restartInterval,
745-
}
746-
rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
762+
for _, withSyntheticPrefix := range []bool{false, true} {
763+
for _, withSyntheticSuffix := range []bool{false, true} {
764+
for _, restartInterval := range []int{16} {
765+
b.Run(fmt.Sprintf("syntheticPrefix=%t;syntheticSuffix=%t;restart=%d", withSyntheticPrefix, withSyntheticSuffix, restartInterval),
766+
func(b *testing.B) {
767+
w := &blockWriter{
768+
restartInterval: restartInterval,
769+
}
770+
rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
747771

748-
createBenchBlock(blockSize, w, rng)
749-
var syntheticSuffix []byte
750-
if withSyntheticSuffix {
751-
syntheticSuffix = benchSynthSuffix
752-
}
772+
_, syntheticPrefix, syntheticSuffix := createBenchBlock(blockSize, w, rng, withSyntheticPrefix, withSyntheticSuffix)
753773

754-
it, err := newBlockIter(benchCmp, benchSplit, w.finish(), IterTransforms{SyntheticSuffix: syntheticSuffix})
755-
if err != nil {
756-
b.Fatal(err)
757-
}
774+
it, err := newBlockIter(benchCmp, benchSplit, w.finish(), IterTransforms{SyntheticSuffix: syntheticSuffix, SyntheticPrefix: syntheticPrefix})
775+
if err != nil {
776+
b.Fatal(err)
777+
}
758778

759-
b.ResetTimer()
760-
for i := 0; i < b.N; i++ {
761-
if !it.valid() {
762-
it.Last()
779+
b.ResetTimer()
780+
for i := 0; i < b.N; i++ {
781+
if !it.valid() {
782+
it.Last()
783+
}
784+
it.Prev()
763785
}
764-
it.Prev()
765-
}
766-
})
786+
})
787+
}
767788
}
768789
}
769790
}

0 commit comments

Comments
 (0)