Files
node/benchmarks/database_benchmark_test.go
T

334 lines
6.9 KiB
Go
Raw Normal View History

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package benchmarks
import (
"fmt"
"testing"
2026-06-02 11:39:52 -07:00
"github.com/luxfi/crypto/hash"
"github.com/luxfi/database/memdb"
"github.com/luxfi/database/prefixdb"
"github.com/luxfi/database/versiondb"
"github.com/luxfi/ids"
)
// BenchmarkMemoryDatabase benchmarks in-memory database operations
func BenchmarkMemoryDatabase(b *testing.B) {
db := memdb.New()
defer db.Close()
key := []byte("test-key")
value := make([]byte, 1024) // 1KB value
for i := range value {
value[i] = byte(i % 256)
}
b.Run("Put", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-%d", i)...)
_ = db.Put(k, value)
}
b.SetBytes(int64(len(value)))
})
b.Run("Get", func(b *testing.B) {
// Pre-populate database
for i := 0; i < 1000; i++ {
k := append(key, fmt.Sprintf("-%d", i)...)
_ = db.Put(k, value)
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-%d", i%1000)...)
_, _ = db.Get(k)
}
})
b.Run("Delete", func(b *testing.B) {
// Pre-populate database
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-del-%d", i)...)
_ = db.Put(k, value)
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-del-%d", i)...)
_ = db.Delete(k)
}
})
b.Run("Has", func(b *testing.B) {
// Pre-populate database
for i := 0; i < 1000; i++ {
k := append(key, fmt.Sprintf("-%d", i)...)
_ = db.Put(k, value)
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-%d", i%1000)...)
_, _ = db.Has(k)
}
})
}
// BenchmarkPrefixDatabase benchmarks prefix database operations
func BenchmarkPrefixDatabase(b *testing.B) {
baseDB := memdb.New()
defer baseDB.Close()
prefix := []byte("prefix")
db := prefixdb.New(prefix, baseDB)
key := []byte("test-key")
value := make([]byte, 256) // 256 bytes value
b.Run("Put", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-%d", i)...)
_ = db.Put(k, value)
}
b.SetBytes(int64(len(value)))
})
b.Run("Get", func(b *testing.B) {
// Pre-populate
for i := 0; i < 100; i++ {
k := append(key, fmt.Sprintf("-%d", i)...)
_ = db.Put(k, value)
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-%d", i%100)...)
_, _ = db.Get(k)
}
})
}
// BenchmarkVersionDatabase benchmarks versioned database operations
func BenchmarkVersionDatabase(b *testing.B) {
baseDB := memdb.New()
defer baseDB.Close()
db := versiondb.New(baseDB)
key := []byte("test-key")
value := make([]byte, 256)
b.Run("Put", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-%d", i)...)
_ = db.Put(k, value)
}
b.SetBytes(int64(len(value)))
})
b.Run("Commit", func(b *testing.B) {
// Add some data before each commit
for i := 0; i < 10; i++ {
k := append(key, fmt.Sprintf("-pre-%d", i)...)
_ = db.Put(k, value)
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-%d", i)...)
_ = db.Put(k, value)
_ = db.Commit()
}
})
b.Run("Abort", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
k := append(key, fmt.Sprintf("-%d", i)...)
_ = db.Put(k, value)
db.Abort()
}
})
}
// BenchmarkDatabaseBatch benchmarks batch database operations
func BenchmarkDatabaseBatch(b *testing.B) {
db := memdb.New()
defer db.Close()
b.Run("SmallBatch", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
batch := db.NewBatch()
for j := 0; j < 10; j++ {
key := []byte(fmt.Sprintf("key-%d-%d", i, j))
value := []byte(fmt.Sprintf("value-%d-%d", i, j))
_ = batch.Put(key, value)
}
_ = batch.Write()
}
})
b.Run("MediumBatch", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
batch := db.NewBatch()
for j := 0; j < 100; j++ {
key := []byte(fmt.Sprintf("key-%d-%d", i, j))
value := []byte(fmt.Sprintf("value-%d-%d", i, j))
_ = batch.Put(key, value)
}
_ = batch.Write()
}
})
b.Run("LargeBatch", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
batch := db.NewBatch()
for j := 0; j < 1000; j++ {
key := []byte(fmt.Sprintf("key-%d-%d", i, j))
value := []byte(fmt.Sprintf("value-%d-%d", i, j))
_ = batch.Put(key, value)
}
_ = batch.Write()
}
})
}
// BenchmarkDatabaseIterator benchmarks database iteration
func BenchmarkDatabaseIterator(b *testing.B) {
db := memdb.New()
defer db.Close()
// Pre-populate database
numEntries := 10000
for i := 0; i < numEntries; i++ {
key := []byte(fmt.Sprintf("key-%08d", i))
value := []byte(fmt.Sprintf("value-%d", i))
_ = db.Put(key, value)
}
b.Run("FullIteration", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
iter := db.NewIterator()
count := 0
for iter.Next() {
_ = iter.Key()
_ = iter.Value()
count++
}
iter.Release()
}
})
b.Run("PrefixIteration", func(b *testing.B) {
prefix := []byte("key-0000")
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
iter := db.NewIteratorWithPrefix(prefix)
count := 0
for iter.Next() {
_ = iter.Key()
_ = iter.Value()
count++
}
iter.Release()
}
})
b.Run("RangeIteration", func(b *testing.B) {
start := []byte("key-00001000")
end := []byte("key-00002000")
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
iter := db.NewIteratorWithStartAndPrefix(start, end)
count := 0
for iter.Next() {
_ = iter.Key()
_ = iter.Value()
count++
}
iter.Release()
}
})
}
// BenchmarkDatabaseConcurrency benchmarks concurrent database access
func BenchmarkDatabaseConcurrency(b *testing.B) {
db := memdb.New()
defer db.Close()
// Pre-populate
for i := 0; i < 1000; i++ {
key := []byte(fmt.Sprintf("key-%d", i))
value := []byte(fmt.Sprintf("value-%d", i))
_ = db.Put(key, value)
}
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
key := []byte(fmt.Sprintf("key-%d", i%1000))
// Mix of operations
switch i % 3 {
case 0:
_, _ = db.Get(key)
case 1:
value := []byte(fmt.Sprintf("new-value-%d", i))
_ = db.Put(key, value)
case 2:
_, _ = db.Has(key)
}
i++
}
})
}
// BenchmarkDatabaseKeyGeneration benchmarks different key generation strategies
func BenchmarkDatabaseKeyGeneration(b *testing.B) {
b.Run("Sequential", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = []byte(fmt.Sprintf("key-%d", i))
}
})
b.Run("Hash", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
data := fmt.Sprintf("key-%d", i)
_ = hash.ComputeHash256([]byte(data))
}
})
b.Run("ID", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
id := ids.GenerateTestID()
_ = id[:]
}
})
}