210 lines
4.5 KiB
Go
210 lines
4.5 KiB
Go
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"testing"
|
|
)
|
|
|
|
// testItem implements Identifiable for benchmarking
|
|
type testItem struct {
|
|
id int32
|
|
name string
|
|
value int32
|
|
flag bool
|
|
}
|
|
|
|
func (t *testItem) GetID() int32 { return t.id }
|
|
|
|
// BenchmarkMasterListOperations benchmarks the generic MasterList
|
|
func BenchmarkMasterListOperations(b *testing.B) {
|
|
// Create master list with test data
|
|
ml := NewMasterList[int32, *testItem]()
|
|
const numItems = 10000
|
|
|
|
// Pre-populate
|
|
b.StopTimer()
|
|
for i := 0; i < numItems; i++ {
|
|
item := &testItem{
|
|
id: int32(i + 1),
|
|
name: fmt.Sprintf("Item %d", i+1),
|
|
value: int32(rand.Intn(100)),
|
|
flag: rand.Intn(2) == 1,
|
|
}
|
|
ml.Add(item)
|
|
}
|
|
b.StartTimer()
|
|
|
|
b.Run("Get", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
id := int32(rand.Intn(numItems) + 1)
|
|
_ = ml.Get(id)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("Filter_10Percent", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.Filter(func(item *testItem) bool {
|
|
return item.value < 10 // ~10% match
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("Filter_50Percent", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.Filter(func(item *testItem) bool {
|
|
return item.value < 50 // ~50% match
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("Filter_90Percent", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.Filter(func(item *testItem) bool {
|
|
return item.value < 90 // ~90% match
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("Count_10Percent", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.Count(func(item *testItem) bool {
|
|
return item.value < 10
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("Count_50Percent", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.Count(func(item *testItem) bool {
|
|
return item.value < 50
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("Find", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
targetValue := int32(rand.Intn(100))
|
|
_, _ = ml.Find(func(item *testItem) bool {
|
|
return item.value == targetValue
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("ForEach", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
ml.ForEach(func(id int32, item *testItem) {
|
|
_ = item.value + 1 // Minimal work
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("WithReadLock", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
ml.WithReadLock(func(items map[int32]*testItem) {
|
|
count := 0
|
|
for _, item := range items {
|
|
if item.value < 50 {
|
|
count++
|
|
}
|
|
}
|
|
_ = count
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("FilterWithCapacity_Accurate", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.FilterWithCapacity(func(item *testItem) bool {
|
|
return item.value < 50
|
|
}, 5000) // Accurate estimate: 50% of 10k = 5k
|
|
}
|
|
})
|
|
|
|
b.Run("FilterInto_Reuse", func(b *testing.B) {
|
|
var reusableSlice []*testItem
|
|
for i := 0; i < b.N; i++ {
|
|
reusableSlice = ml.FilterInto(func(item *testItem) bool {
|
|
return item.value < 50
|
|
}, reusableSlice)
|
|
}
|
|
})
|
|
|
|
b.Run("CountAndFilter_Combined", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = ml.CountAndFilter(func(item *testItem) bool {
|
|
return item.value < 50
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkMemoryAllocations tests allocation patterns
|
|
func BenchmarkMemoryAllocations(b *testing.B) {
|
|
ml := NewMasterList[int32, *testItem]()
|
|
const numItems = 1000
|
|
|
|
// Pre-populate
|
|
for i := 0; i < numItems; i++ {
|
|
item := &testItem{
|
|
id: int32(i + 1),
|
|
name: fmt.Sprintf("Item %d", i+1),
|
|
value: int32(rand.Intn(100)),
|
|
flag: rand.Intn(2) == 1,
|
|
}
|
|
ml.Add(item)
|
|
}
|
|
|
|
b.Run("Filter_Allocations", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.Filter(func(item *testItem) bool {
|
|
return item.value < 50
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("GetAll_Allocations", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.GetAll()
|
|
}
|
|
})
|
|
|
|
b.Run("GetAllSlice_Allocations", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.GetAllSlice()
|
|
}
|
|
})
|
|
|
|
b.Run("FilterWithCapacity_Allocations", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.FilterWithCapacity(func(item *testItem) bool {
|
|
return item.value < 50
|
|
}, 500) // Accurate capacity estimate
|
|
}
|
|
})
|
|
|
|
b.Run("FilterInto_Allocations", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
reusableSlice := make([]*testItem, 0, 600) // Pre-sized
|
|
for i := 0; i < b.N; i++ {
|
|
reusableSlice = ml.FilterInto(func(item *testItem) bool {
|
|
return item.value < 50
|
|
}, reusableSlice)
|
|
}
|
|
})
|
|
|
|
b.Run("CountAndFilter_Allocations", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = ml.CountAndFilter(func(item *testItem) bool {
|
|
return item.value < 50
|
|
})
|
|
}
|
|
})
|
|
} |