2017-01-12 21:25:31 +01:00
|
|
|
// Copyright (c) 2016-2017 The btcsuite developers
|
|
|
|
// Copyright (c) 2016-2017 The Lightning Network Developers
|
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package gcs_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"math/rand"
|
|
|
|
"testing"
|
|
|
|
|
2021-09-10 22:30:39 +02:00
|
|
|
"github.com/lbryio/lbcutil/gcs"
|
2017-01-12 21:25:31 +01:00
|
|
|
)
|
|
|
|
|
2017-06-09 02:46:02 +02:00
|
|
|
func genRandFilterElements(numElements uint) ([][]byte, error) {
|
|
|
|
testContents := make([][]byte, numElements)
|
2018-07-20 05:48:07 +02:00
|
|
|
for i := range testContents {
|
2017-06-09 02:46:02 +02:00
|
|
|
randElem := make([]byte, 32)
|
|
|
|
if _, err := rand.Read(randElem); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
testContents[i] = randElem
|
|
|
|
}
|
|
|
|
|
|
|
|
return testContents, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
generatedFilter *gcs.Filter
|
|
|
|
filterErr error
|
|
|
|
)
|
|
|
|
|
2017-01-12 21:25:31 +01:00
|
|
|
// BenchmarkGCSFilterBuild benchmarks building a filter.
|
2017-06-09 02:46:02 +02:00
|
|
|
func BenchmarkGCSFilterBuild50000(b *testing.B) {
|
|
|
|
var testKey [gcs.KeySize]byte
|
2017-01-12 21:25:31 +01:00
|
|
|
for i := 0; i < gcs.KeySize; i += 4 {
|
2017-06-09 02:46:02 +02:00
|
|
|
binary.BigEndian.PutUint32(testKey[i:], rand.Uint32())
|
|
|
|
}
|
2018-05-29 05:22:24 +02:00
|
|
|
|
2017-06-09 02:46:02 +02:00
|
|
|
randFilterElems, genErr := genRandFilterElements(50000)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("unable to generate random item: %v", genErr)
|
2017-01-12 21:25:31 +01:00
|
|
|
}
|
2018-05-29 05:22:24 +02:00
|
|
|
|
2018-07-20 05:48:07 +02:00
|
|
|
b.ReportAllocs()
|
|
|
|
b.ResetTimer()
|
2017-01-12 21:25:31 +01:00
|
|
|
|
2017-06-09 02:46:02 +02:00
|
|
|
var localFilter *gcs.Filter
|
2017-01-12 21:25:31 +01:00
|
|
|
for i := 0; i < b.N; i++ {
|
2018-05-29 05:19:14 +02:00
|
|
|
localFilter, err = gcs.BuildGCSFilter(
|
2018-05-29 05:22:24 +02:00
|
|
|
P, M, key, randFilterElems,
|
2018-05-29 05:19:14 +02:00
|
|
|
)
|
2017-06-09 02:46:02 +02:00
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("unable to generate filter: %v", err)
|
|
|
|
}
|
2017-01-12 21:25:31 +01:00
|
|
|
}
|
2017-06-09 02:46:02 +02:00
|
|
|
generatedFilter = localFilter
|
2017-01-12 21:25:31 +01:00
|
|
|
}
|
|
|
|
|
2017-06-09 03:25:38 +02:00
|
|
|
// BenchmarkGCSFilterBuild benchmarks building a filter.
|
|
|
|
func BenchmarkGCSFilterBuild100000(b *testing.B) {
|
|
|
|
var testKey [gcs.KeySize]byte
|
|
|
|
for i := 0; i < gcs.KeySize; i += 4 {
|
|
|
|
binary.BigEndian.PutUint32(testKey[i:], rand.Uint32())
|
|
|
|
}
|
2018-05-29 05:22:24 +02:00
|
|
|
|
2017-06-09 03:25:38 +02:00
|
|
|
randFilterElems, genErr := genRandFilterElements(100000)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("unable to generate random item: %v", genErr)
|
|
|
|
}
|
2018-05-29 05:22:24 +02:00
|
|
|
|
2018-07-20 05:48:07 +02:00
|
|
|
b.ReportAllocs()
|
|
|
|
b.ResetTimer()
|
2017-06-09 03:25:38 +02:00
|
|
|
|
|
|
|
var localFilter *gcs.Filter
|
|
|
|
for i := 0; i < b.N; i++ {
|
2018-05-29 05:22:24 +02:00
|
|
|
localFilter, err = gcs.BuildGCSFilter(
|
|
|
|
P, M, key, randFilterElems,
|
|
|
|
)
|
2017-06-09 03:25:38 +02:00
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("unable to generate filter: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
generatedFilter = localFilter
|
|
|
|
}
|
|
|
|
|
2017-06-09 02:46:02 +02:00
|
|
|
var (
|
|
|
|
match bool
|
|
|
|
)
|
|
|
|
|
2017-01-12 21:25:31 +01:00
|
|
|
// BenchmarkGCSFilterMatch benchmarks querying a filter for a single value.
|
|
|
|
func BenchmarkGCSFilterMatch(b *testing.B) {
|
2018-05-29 05:22:24 +02:00
|
|
|
filter, err := gcs.BuildGCSFilter(P, M, key, contents)
|
2017-01-12 21:25:31 +01:00
|
|
|
if err != nil {
|
2017-06-09 02:46:02 +02:00
|
|
|
b.Fatalf("Failed to build filter")
|
2017-01-12 21:25:31 +01:00
|
|
|
}
|
|
|
|
|
2018-07-20 05:48:07 +02:00
|
|
|
b.ReportAllocs()
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
|
|
var localMatch bool
|
2017-01-12 21:25:31 +01:00
|
|
|
for i := 0; i < b.N; i++ {
|
2017-06-09 02:46:02 +02:00
|
|
|
localMatch, err = filter.Match(key, []byte("Nate"))
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("unable to match filter: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
localMatch, err = filter.Match(key, []byte("Nates"))
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("unable to match filter: %v", err)
|
|
|
|
}
|
2017-01-12 21:25:31 +01:00
|
|
|
}
|
2017-06-09 02:46:02 +02:00
|
|
|
match = localMatch
|
2017-01-12 21:25:31 +01:00
|
|
|
}
|
|
|
|
|
2018-07-20 05:48:07 +02:00
|
|
|
var (
|
|
|
|
randElems1, _ = genRandFilterElements(1)
|
|
|
|
randElems10, _ = genRandFilterElements(10)
|
|
|
|
randElems100, _ = genRandFilterElements(100)
|
|
|
|
randElems1000, _ = genRandFilterElements(1000)
|
|
|
|
randElems10000, _ = genRandFilterElements(10000)
|
|
|
|
randElems100000, _ = genRandFilterElements(100000)
|
|
|
|
randElems1000000, _ = genRandFilterElements(1000000)
|
|
|
|
randElems10000000, _ = genRandFilterElements(10000000)
|
|
|
|
|
|
|
|
filterElems1000, _ = genRandFilterElements(1000)
|
|
|
|
filter1000, _ = gcs.BuildGCSFilter(P, M, key, filterElems1000)
|
|
|
|
filterElems5000, _ = genRandFilterElements(5000)
|
|
|
|
filter5000, _ = gcs.BuildGCSFilter(P, M, key, filterElems5000)
|
|
|
|
filterElems10000, _ = genRandFilterElements(10000)
|
|
|
|
filter10000, _ = gcs.BuildGCSFilter(P, M, key, filterElems10000)
|
|
|
|
)
|
|
|
|
|
|
|
|
// matchAnyBenchmarks contains combinations of random filters and queries used
|
|
|
|
// to measure performance of various MatchAny implementations.
|
|
|
|
var matchAnyBenchmarks = []struct {
|
|
|
|
name string
|
|
|
|
query [][]byte
|
|
|
|
filter *gcs.Filter
|
|
|
|
}{
|
|
|
|
{"q100-f1K", randElems100, filter1000},
|
|
|
|
{"q1K-f1K", randElems1000, filter1000},
|
|
|
|
{"q10K-f1K", randElems10000, filter1000},
|
|
|
|
{"q100K-f1K", randElems100000, filter1000},
|
|
|
|
{"q1M-f1K", randElems1000000, filter1000},
|
|
|
|
{"q10M-f1K", randElems10000000, filter1000},
|
|
|
|
|
|
|
|
{"q100-f5K", randElems100, filter5000},
|
|
|
|
{"q1K-f5K", randElems1000, filter5000},
|
|
|
|
{"q10K-f5K", randElems10000, filter5000},
|
|
|
|
{"q100K-f5K", randElems100000, filter5000},
|
|
|
|
{"q1M-f5K", randElems1000000, filter5000},
|
|
|
|
{"q10M-f5K", randElems10000000, filter5000},
|
|
|
|
|
|
|
|
{"q100-f10K", randElems100, filter10000},
|
|
|
|
{"q1K-f10K", randElems1000, filter10000},
|
|
|
|
{"q10K-f10K", randElems10000, filter10000},
|
|
|
|
{"q100K-f10K", randElems100000, filter10000},
|
|
|
|
{"q1M-f10K", randElems1000000, filter10000},
|
|
|
|
{"q10M-f10K", randElems10000000, filter10000},
|
|
|
|
}
|
|
|
|
|
|
|
|
// BenchmarkGCSFilterMatchAny benchmarks the sort-and-zip MatchAny impl.
|
|
|
|
func BenchmarkGCSFilterZipMatchAny(b *testing.B) {
|
|
|
|
for _, test := range matchAnyBenchmarks {
|
|
|
|
b.Run(test.name, func(b *testing.B) {
|
|
|
|
b.ReportAllocs()
|
|
|
|
|
|
|
|
var (
|
|
|
|
localMatch bool
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
localMatch, err = test.filter.ZipMatchAny(
|
|
|
|
key, test.query,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("unable to match filter: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
match = localMatch
|
|
|
|
})
|
2017-06-09 02:46:02 +02:00
|
|
|
}
|
2018-07-20 05:48:07 +02:00
|
|
|
}
|
2017-06-09 02:46:02 +02:00
|
|
|
|
2018-07-20 05:48:07 +02:00
|
|
|
// BenchmarkGCSFilterMatchAny benchmarks the hash-join MatchAny impl.
|
|
|
|
func BenchmarkGCSFilterHashMatchAny(b *testing.B) {
|
|
|
|
for _, test := range matchAnyBenchmarks {
|
|
|
|
b.Run(test.name, func(b *testing.B) {
|
|
|
|
b.ReportAllocs()
|
|
|
|
|
|
|
|
var (
|
|
|
|
localMatch bool
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
localMatch, err = test.filter.HashMatchAny(
|
|
|
|
key, test.query,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("unable to match filter: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
match = localMatch
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// BenchmarkGCSFilterMatchAny benchmarks the hybrid MatchAny impl.
|
|
|
|
func BenchmarkGCSFilterMatchAny(b *testing.B) {
|
|
|
|
for _, test := range matchAnyBenchmarks {
|
|
|
|
b.Run(test.name, func(b *testing.B) {
|
|
|
|
b.ReportAllocs()
|
|
|
|
|
|
|
|
var (
|
|
|
|
localMatch bool
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
localMatch, err = test.filter.MatchAny(
|
|
|
|
key, test.query,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("unable to match filter: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
match = localMatch
|
|
|
|
})
|
2017-01-12 21:25:31 +01:00
|
|
|
}
|
|
|
|
}
|