This adds benchmarks for various APBF methods including the primary APBF filter operations of Add and Contains for both the worst case when an item matches the filter as well as the average case when an item does NOT match the filter. The following shows the results on a Ryzen 7 1700 processor: capacity=1000, fprate=0.1% -------------------------- BenchmarkAdd 7759802 155.0 ns/op 0 B/op 0 allocs/op BenchmarkContainsTrue 6865903 175.6 ns/op 0 B/op 0 allocs/op BenchmarkContainsFalse 25966916 45.29 ns/op 0 B/op 0 allocs/op BenchmarkReset 1020956 117.0 ns/op 0 B/op 0 allocs/op capacity=1000, fprate=0.01% --------------------------- BenchmarkAdd 5748116 207.3 ns/op 0 B/op 0 allocs/op BenchmarkContainsTrue 5351280 229.6 ns/op 0 B/op 0 allocs/op BenchmarkContainsFalse 24985996 45.29 ns/op 0 B/op 0 allocs/op BenchmarkReset 8158309 145.0 ns/op 0 B/op 0 allocs/op capacity=1000, fprate=0.001% ---------------------------- BenchmarkAdd 4868738 244.8 ns/op 0 B/op 0 allocs/op BenchmarkContainsTrue 4579454 270.8 ns/op 0 B/op 0 allocs/op BenchmarkContainsFalse 26251509 45.48 ns/op 0 B/op 0 allocs/op BenchmarkReset 6912676 175.1 ns/op 0 B/op 0 allocs/op capacity=10000, fprate=0.001% ----------------------------- BenchmarkReset 749980 1625 ns/op 0 B/op 0 allocs/op capacity=100000, fprate=0.01% ----------------------------- BenchmarkAdd 6168745 203.1 ns/op 0 B/op 0 allocs/op BenchmarkContainsTrue 5849455 210.5 ns/op 0 B/op 0 allocs/op BenchmarkContainsFalse 26782545 44.06 ns/op 0 B/op 0 allocs/op capacity=100000, fprate=0.0001% ------------------------------- BenchmarkAdd 4348394 265.8 ns/op 0 B/op 0 allocs/op BenchmarkContainsTrue 4236044 282.3 ns/op 0 B/op 0 allocs/op BenchmarkContainsFalse 27291583 44.61 ns/op 0 B/op 0 allocs/op BenchmarkReset 67348 17542 ns/op 0 B/op 0 allocs/op capacity=100000, fprate=0.00001% -------------------------------- BenchmarkAdd 2702445 462.8 ns/op 0 B/op 0 allocs/op
228 lines
5.1 KiB
Go
228 lines
5.1 KiB
Go
// Copyright (c) 2021 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package apbf
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"testing"
|
|
)
|
|
|
|
// BenchmarkAdd benchmarks adding items to an APBF for various capacities and
|
|
// false positive rates.
|
|
func BenchmarkAdd(b *testing.B) {
|
|
benches := []struct {
|
|
capacity uint32 // target capacity
|
|
fpRate float64 // target false positive rate
|
|
}{{
|
|
capacity: 1000,
|
|
fpRate: 0.001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.0001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.00001,
|
|
}, {
|
|
capacity: 100000,
|
|
fpRate: 0.0001,
|
|
}, {
|
|
capacity: 100000,
|
|
fpRate: 0.000001,
|
|
}, {
|
|
capacity: 1000000,
|
|
fpRate: 0.0000001,
|
|
}}
|
|
|
|
for benchIdx := range benches {
|
|
bench := benches[benchIdx]
|
|
benchName := fmt.Sprintf("capacity=%d/fprate=%0.7f", bench.capacity,
|
|
bench.fpRate)
|
|
b.Run(benchName, func(b *testing.B) {
|
|
filter := NewFilter(bench.capacity, bench.fpRate)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
var data [4]byte
|
|
for i := 0; i < b.N; i++ {
|
|
binary.LittleEndian.PutUint32(data[:], uint32(i))
|
|
filter.Add(data[:])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// BenchmarkContainsTrue benchmarks membership queries on an APBF for various
|
|
// capacities and false positive rates when the item exists in the filter and
|
|
// is near worst case performance.
|
|
func BenchmarkContainsTrue(b *testing.B) {
|
|
benches := []struct {
|
|
capacity uint32 // target capacity
|
|
fpRate float64 // target false positive rate
|
|
}{{
|
|
capacity: 1000,
|
|
fpRate: 0.001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.0001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.00001,
|
|
}, {
|
|
capacity: 100000,
|
|
fpRate: 0.0001,
|
|
}, {
|
|
capacity: 100000,
|
|
fpRate: 0.000001,
|
|
}}
|
|
|
|
for benchIdx := range benches {
|
|
bench := benches[benchIdx]
|
|
benchName := fmt.Sprintf("capacity=%d/fprate=%0.6f", bench.capacity,
|
|
bench.fpRate)
|
|
b.Run(benchName, func(b *testing.B) {
|
|
// Load the filter so the benchmark is with a max capacity filter.
|
|
filter := NewFilter(bench.capacity, bench.fpRate)
|
|
var data [4]byte
|
|
for i := uint32(0); i < filter.Capacity(); i++ {
|
|
binary.LittleEndian.PutUint32(data[:], i)
|
|
filter.Add(data[:])
|
|
}
|
|
binary.LittleEndian.PutUint32(data[:], filter.Capacity()/2)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
filter.Contains(data[:])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// BenchmarkContainsFalse benchmarks membership queries on an APBF for various
|
|
// capacities and false positive rates when the item does not exist in the
|
|
// filter.
|
|
func BenchmarkContainsFalse(b *testing.B) {
|
|
benches := []struct {
|
|
capacity uint32 // target capacity
|
|
fpRate float64 // target false positive rate
|
|
}{{
|
|
capacity: 1000,
|
|
fpRate: 0.001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.0001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.00001,
|
|
}, {
|
|
capacity: 100000,
|
|
fpRate: 0.0001,
|
|
}, {
|
|
capacity: 100000,
|
|
fpRate: 0.000001,
|
|
}}
|
|
|
|
for benchIdx := range benches {
|
|
bench := benches[benchIdx]
|
|
benchName := fmt.Sprintf("capacity=%d/fprate=%0.6f", bench.capacity,
|
|
bench.fpRate)
|
|
b.Run(benchName, func(b *testing.B) {
|
|
// Load the filter so the benchmark is with a max capacity filter.
|
|
filter := NewFilter(bench.capacity, bench.fpRate)
|
|
var data [4]byte
|
|
for i := uint32(0); i < filter.Capacity(); i++ {
|
|
binary.LittleEndian.PutUint32(data[:], i)
|
|
filter.Add(data[:])
|
|
}
|
|
binary.LittleEndian.PutUint32(data[:], filter.Capacity()+1)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
filter.Contains(data[:])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// BenchmarkReset benchmarks resetting an APBF for various capacities and false
|
|
// positive rates.
|
|
func BenchmarkReset(b *testing.B) {
|
|
benches := []struct {
|
|
capacity uint32 // target capacity
|
|
fpRate float64 // target false positive rate
|
|
}{{
|
|
capacity: 1000,
|
|
fpRate: 0.001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.0001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.00001,
|
|
}, {
|
|
capacity: 10000,
|
|
fpRate: 0.00001,
|
|
}, {
|
|
capacity: 100000,
|
|
fpRate: 0.000001,
|
|
}}
|
|
|
|
for benchIdx := range benches {
|
|
bench := benches[benchIdx]
|
|
benchName := fmt.Sprintf("capacity=%d/fprate=%0.6f", bench.capacity,
|
|
bench.fpRate)
|
|
b.Run(benchName, func(b *testing.B) {
|
|
filter := NewFilter(bench.capacity, bench.fpRate)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
filter.Reset()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// BenchmarkNewFilter benchmarks creating a new APBF for various capacities and
|
|
// false positive rates.
|
|
func BenchmarkNewFilter(b *testing.B) {
|
|
benches := []struct {
|
|
capacity uint32 // target capacity
|
|
fpRate float64 // target false positive rate
|
|
}{{
|
|
capacity: 1000,
|
|
fpRate: 0.001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.0001,
|
|
}, {
|
|
capacity: 1000,
|
|
fpRate: 0.00001,
|
|
}, {
|
|
capacity: 10000,
|
|
fpRate: 0.00001,
|
|
}, {
|
|
capacity: 100000,
|
|
fpRate: 0.000001,
|
|
}}
|
|
|
|
var noElide *Filter
|
|
for benchIdx := range benches {
|
|
bench := benches[benchIdx]
|
|
benchName := fmt.Sprintf("capacity=%d/fprate=%0.6f", bench.capacity,
|
|
bench.fpRate)
|
|
b.Run(benchName, func(b *testing.B) {
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
noElide = NewFilter(bench.capacity, bench.fpRate)
|
|
}
|
|
})
|
|
}
|
|
_ = noElide
|
|
}
|