2019-05-22 21:16:55 +00:00
|
|
|
package mergeset
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"os"
|
2024-02-23 21:29:23 +00:00
|
|
|
"sync/atomic"
|
2019-05-22 21:16:55 +00:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func BenchmarkTableSearch(b *testing.B) {
|
|
|
|
for _, itemsCount := range []int{1e5, 1e6, 1e7} {
|
|
|
|
b.Run(fmt.Sprintf("itemsCount-%d", itemsCount), func(b *testing.B) {
|
|
|
|
benchmarkTableSearch(b, itemsCount)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func benchmarkTableSearch(b *testing.B, itemsCount int) {
|
2023-01-24 03:43:39 +00:00
|
|
|
r := rand.New(rand.NewSource(1))
|
|
|
|
|
2019-05-22 21:16:55 +00:00
|
|
|
path := fmt.Sprintf("BenchmarkTableSearch-%d", itemsCount)
|
|
|
|
if err := os.RemoveAll(path); err != nil {
|
|
|
|
b.Fatalf("cannot remove %q: %s", path, err)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
_ = os.RemoveAll(path)
|
|
|
|
}()
|
|
|
|
|
2023-01-24 03:43:39 +00:00
|
|
|
tb, items, err := newTestTable(r, path, itemsCount)
|
2019-05-22 21:16:55 +00:00
|
|
|
if err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
panic(fmt.Errorf("cannot create test table at %q with %d items: %w", path, itemsCount, err))
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Force finishing pending merges
|
|
|
|
tb.MustClose()
|
2024-02-23 21:29:23 +00:00
|
|
|
var isReadOnly atomic.Bool
|
2023-04-15 05:08:43 +00:00
|
|
|
tb = MustOpenTable(path, nil, nil, &isReadOnly)
|
2019-05-22 21:16:55 +00:00
|
|
|
defer tb.MustClose()
|
|
|
|
|
|
|
|
keys := make([][]byte, len(items))
|
|
|
|
for i, item := range items {
|
|
|
|
keys[i] = []byte(item)
|
|
|
|
}
|
|
|
|
|
|
|
|
b.Run("sequential-keys-exact", func(b *testing.B) {
|
|
|
|
benchmarkTableSearchKeys(b, tb, keys, 0)
|
|
|
|
})
|
2021-02-21 20:06:45 +00:00
|
|
|
b.Run("sequential-keys-without-suffix", func(b *testing.B) {
|
2019-05-22 21:16:55 +00:00
|
|
|
benchmarkTableSearchKeys(b, tb, keys, 4)
|
|
|
|
})
|
|
|
|
|
|
|
|
randKeys := append([][]byte{}, keys...)
|
2023-01-24 03:43:39 +00:00
|
|
|
r.Shuffle(len(randKeys), func(i, j int) {
|
2019-05-22 21:16:55 +00:00
|
|
|
randKeys[i], randKeys[j] = randKeys[j], randKeys[i]
|
|
|
|
})
|
|
|
|
b.Run("random-keys-exact", func(b *testing.B) {
|
|
|
|
benchmarkTableSearchKeys(b, tb, randKeys, 0)
|
|
|
|
})
|
2021-02-21 20:06:45 +00:00
|
|
|
b.Run("random-keys-without-suffix", func(b *testing.B) {
|
2019-05-22 21:16:55 +00:00
|
|
|
benchmarkTableSearchKeys(b, tb, randKeys, 4)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func benchmarkTableSearchKeys(b *testing.B, tb *Table, keys [][]byte, stripSuffix int) {
|
|
|
|
for _, rowsToScan := range []int{1, 10, 100, 1000} {
|
|
|
|
b.Run(fmt.Sprintf("rowsToScan-%d", rowsToScan), func(b *testing.B) {
|
|
|
|
benchmarkTableSearchKeysExt(b, tb, keys, stripSuffix, rowsToScan)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func benchmarkTableSearchKeysExt(b *testing.B, tb *Table, keys [][]byte, stripSuffix, rowsToScan int) {
|
|
|
|
searchKeysCount := 1000
|
|
|
|
if searchKeysCount >= len(keys) {
|
|
|
|
searchKeysCount = len(keys) - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
b.ReportAllocs()
|
|
|
|
b.SetBytes(int64(searchKeysCount * rowsToScan))
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
2023-01-24 03:43:39 +00:00
|
|
|
r := rand.New(rand.NewSource(1))
|
2019-05-22 21:16:55 +00:00
|
|
|
var ts TableSearch
|
2024-10-24 13:21:17 +00:00
|
|
|
ts.Init(tb, false)
|
2019-05-22 21:16:55 +00:00
|
|
|
defer ts.MustClose()
|
|
|
|
for pb.Next() {
|
2023-01-24 03:43:39 +00:00
|
|
|
startIdx := r.Intn(len(keys) - searchKeysCount)
|
2019-05-22 21:16:55 +00:00
|
|
|
searchKeys := keys[startIdx : startIdx+searchKeysCount]
|
|
|
|
for i, key := range searchKeys {
|
|
|
|
searchKey := key
|
|
|
|
if len(searchKey) < stripSuffix {
|
|
|
|
searchKey = nil
|
|
|
|
} else {
|
|
|
|
searchKey = searchKey[:len(searchKey)-stripSuffix]
|
|
|
|
}
|
|
|
|
ts.Seek(searchKey)
|
|
|
|
if !ts.NextItem() {
|
|
|
|
panic(fmt.Errorf("BUG: NextItem must return true for searchKeys[%d]=%q; err=%v", i, searchKey, ts.Error()))
|
|
|
|
}
|
|
|
|
if !bytes.HasPrefix(ts.Item, searchKey) {
|
|
|
|
panic(fmt.Errorf("BUG: unexpected item found for searchKey[%d]=%q; got %q; want %q", i, searchKey, ts.Item, key))
|
|
|
|
}
|
|
|
|
for j := 1; j < rowsToScan; j++ {
|
|
|
|
if !ts.NextItem() {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := ts.Error(); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
panic(fmt.Errorf("BUG: unexpected error for searchKeys[%d]=%q: %w", i, searchKey, err))
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|