eq2go/internal/alt_advancement/benchmark_test.go
2025-08-08 10:28:46 -05:00

406 lines
10 KiB
Go

package alt_advancement
import (
"fmt"
"math/rand"
"sync"
"testing"
"eq2emu/internal/database"
)
// Global shared master list for benchmarks to avoid repeated setup
var (
sharedAltAdvancementMasterList *MasterList
sharedAltAdvancements []*AltAdvancement
altAdvancementSetupOnce sync.Once
)
// setupSharedAltAdvancementMasterList creates the shared master list once
func setupSharedAltAdvancementMasterList(b *testing.B) {
altAdvancementSetupOnce.Do(func() {
// Create test database
db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared")
if err != nil {
b.Fatalf("Failed to create test database: %v", err)
}
sharedAltAdvancementMasterList = NewMasterList()
// Pre-populate with alternate advancements for realistic testing
const numAltAdvancements = 1000
sharedAltAdvancements = make([]*AltAdvancement, numAltAdvancements)
groups := []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC, AA_TRADESKILL, AA_PRESTIGE}
classes := []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 0 = universal, 1-10 = specific classes
for i := range numAltAdvancements {
sharedAltAdvancements[i] = New(db)
sharedAltAdvancements[i].NodeID = int32(i + 1)
sharedAltAdvancements[i].SpellID = int32(i + 1)
sharedAltAdvancements[i].Name = fmt.Sprintf("Alt Advancement %d", i+1)
sharedAltAdvancements[i].Group = groups[i%len(groups)]
sharedAltAdvancements[i].ClassReq = classes[i%len(classes)]
sharedAltAdvancements[i].MinLevel = int8(rand.Intn(50) + 1)
sharedAltAdvancements[i].RankCost = int8(rand.Intn(5) + 1)
sharedAltAdvancements[i].MaxRank = int8(rand.Intn(10) + 1)
sharedAltAdvancements[i].Col = int8(rand.Intn(11))
sharedAltAdvancements[i].Row = int8(rand.Intn(16))
sharedAltAdvancementMasterList.AddAltAdvancement(sharedAltAdvancements[i])
}
})
}
// createTestAltAdvancement creates an alternate advancement for benchmarking
func createTestAltAdvancement(b *testing.B, id int32) *AltAdvancement {
b.Helper()
// Use nil database for benchmarking in-memory operations
aa := New(nil)
aa.NodeID = id
aa.SpellID = id
aa.Name = fmt.Sprintf("Benchmark AA %d", id)
aa.Group = []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC}[id%4]
aa.ClassReq = int8((id % 10) + 1)
aa.MinLevel = int8((id % 50) + 1)
aa.RankCost = int8(rand.Intn(5) + 1)
aa.MaxRank = int8(rand.Intn(10) + 1)
aa.Col = int8(rand.Intn(11))
aa.Row = int8(rand.Intn(16))
return aa
}
// BenchmarkAltAdvancementCreation measures alternate advancement creation performance
func BenchmarkAltAdvancementCreation(b *testing.B) {
db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared")
if err != nil {
b.Fatalf("Failed to create test database: %v", err)
}
defer db.Close()
b.ResetTimer()
b.Run("Sequential", func(b *testing.B) {
for i := 0; i < b.N; i++ {
aa := New(db)
aa.NodeID = int32(i)
aa.SpellID = int32(i)
aa.Name = fmt.Sprintf("AA %d", i)
_ = aa
}
})
b.Run("Parallel", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
id := int32(0)
for pb.Next() {
aa := New(db)
aa.NodeID = id
aa.SpellID = id
aa.Name = fmt.Sprintf("AA %d", id)
id++
_ = aa
}
})
})
}
// BenchmarkAltAdvancementOperations measures individual alternate advancement operations
func BenchmarkAltAdvancementOperations(b *testing.B) {
aa := createTestAltAdvancement(b, 1001)
b.Run("GetID", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = aa.GetID()
}
})
})
b.Run("IsNew", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = aa.IsNew()
}
})
})
b.Run("Clone", func(b *testing.B) {
for b.Loop() {
_ = aa.Clone()
}
})
b.Run("IsValid", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = aa.IsValid()
}
})
})
}
// BenchmarkMasterListOperations measures master list performance
func BenchmarkMasterListOperations(b *testing.B) {
setupSharedAltAdvancementMasterList(b)
ml := sharedAltAdvancementMasterList
b.Run("GetAltAdvancement", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
id := int32(rand.Intn(1000) + 1)
_ = ml.GetAltAdvancement(id)
}
})
})
b.Run("AddAltAdvancement", func(b *testing.B) {
// Create a separate master list for add operations
addML := NewMasterList()
startID := int32(10000)
// Pre-create AAs to measure just the Add operation
aasToAdd := make([]*AltAdvancement, b.N)
for i := 0; i < b.N; i++ {
aasToAdd[i] = createTestAltAdvancement(b, startID+int32(i))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
addML.AddAltAdvancement(aasToAdd[i])
}
})
b.Run("GetAltAdvancementsByGroup", func(b *testing.B) {
groups := []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC, AA_TRADESKILL, AA_PRESTIGE}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
group := groups[rand.Intn(len(groups))]
_ = ml.GetAltAdvancementsByGroup(group)
}
})
})
b.Run("GetAltAdvancementsByClass", func(b *testing.B) {
classes := []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
class := classes[rand.Intn(len(classes))]
_ = ml.GetAltAdvancementsByClass(class)
}
})
})
b.Run("GetAltAdvancementsByLevel", func(b *testing.B) {
for b.Loop() {
level := int8(rand.Intn(50) + 1)
_ = ml.GetAltAdvancementsByLevel(level)
}
})
b.Run("GetAltAdvancementsByGroupAndClass", func(b *testing.B) {
groups := []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC}
classes := []int8{1, 2, 3, 4, 5}
for b.Loop() {
group := groups[rand.Intn(len(groups))]
class := classes[rand.Intn(len(classes))]
_ = ml.GetAltAdvancementsByGroupAndClass(group, class)
}
})
b.Run("GetGroups", func(b *testing.B) {
for b.Loop() {
_ = ml.GetGroups()
}
})
b.Run("GetClasses", func(b *testing.B) {
for b.Loop() {
_ = ml.GetClasses()
}
})
b.Run("Size", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = ml.Size()
}
})
})
}
// BenchmarkConcurrentOperations tests mixed workload performance
func BenchmarkConcurrentOperations(b *testing.B) {
setupSharedAltAdvancementMasterList(b)
ml := sharedAltAdvancementMasterList
b.Run("MixedOperations", func(b *testing.B) {
groups := []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC, AA_TRADESKILL, AA_PRESTIGE}
classes := []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
switch rand.Intn(8) {
case 0:
id := int32(rand.Intn(1000) + 1)
_ = ml.GetAltAdvancement(id)
case 1:
group := groups[rand.Intn(len(groups))]
_ = ml.GetAltAdvancementsByGroup(group)
case 2:
class := classes[rand.Intn(len(classes))]
_ = ml.GetAltAdvancementsByClass(class)
case 3:
level := int8(rand.Intn(50) + 1)
_ = ml.GetAltAdvancementsByLevel(level)
case 4:
group := groups[rand.Intn(len(groups))]
class := classes[rand.Intn(len(classes))]
_ = ml.GetAltAdvancementsByGroupAndClass(group, class)
case 5:
_ = ml.GetGroups()
case 6:
_ = ml.GetClasses()
case 7:
_ = ml.Size()
}
}
})
})
}
// BenchmarkMemoryAllocation measures memory allocation patterns
func BenchmarkMemoryAllocation(b *testing.B) {
db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared")
if err != nil {
b.Fatalf("Failed to create test database: %v", err)
}
defer db.Close()
b.Run("AltAdvancementAllocation", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
aa := New(db)
aa.NodeID = int32(i)
aa.SpellID = int32(i)
aa.Name = fmt.Sprintf("AA %d", i)
_ = aa
}
})
b.Run("MasterListAllocation", func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
ml := NewMasterList()
_ = ml
}
})
b.Run("AddAltAdvancement_Allocations", func(b *testing.B) {
b.ReportAllocs()
ml := NewMasterList()
for i := 0; i < b.N; i++ {
aa := createTestAltAdvancement(b, int32(i+1))
ml.AddAltAdvancement(aa)
}
})
b.Run("GetAltAdvancementsByGroup_Allocations", func(b *testing.B) {
setupSharedAltAdvancementMasterList(b)
ml := sharedAltAdvancementMasterList
b.ReportAllocs()
b.ResetTimer()
for b.Loop() {
_ = ml.GetAltAdvancementsByGroup(AA_CLASS)
}
})
b.Run("GetGroups_Allocations", func(b *testing.B) {
setupSharedAltAdvancementMasterList(b)
ml := sharedAltAdvancementMasterList
b.ReportAllocs()
b.ResetTimer()
for b.Loop() {
_ = ml.GetGroups()
}
})
}
// BenchmarkUpdateOperations measures update performance
func BenchmarkUpdateOperations(b *testing.B) {
setupSharedAltAdvancementMasterList(b)
ml := sharedAltAdvancementMasterList
b.Run("UpdateAltAdvancement", func(b *testing.B) {
// Create AAs to update
updateAAs := make([]*AltAdvancement, b.N)
for i := 0; i < b.N; i++ {
updateAAs[i] = createTestAltAdvancement(b, int32((i%1000)+1))
updateAAs[i].Name = "Updated Name"
updateAAs[i].Group = AA_SUBCLASS
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = ml.UpdateAltAdvancement(updateAAs[i])
}
})
b.Run("RemoveAltAdvancement", func(b *testing.B) {
// Create a separate master list for removal testing
removeML := NewMasterList()
// Add AAs to remove
for i := 0; i < b.N; i++ {
aa := createTestAltAdvancement(b, int32(i+1))
removeML.AddAltAdvancement(aa)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
removeML.RemoveAltAdvancement(int32(i + 1))
}
})
}
// BenchmarkValidation measures validation performance
func BenchmarkValidation(b *testing.B) {
setupSharedAltAdvancementMasterList(b)
ml := sharedAltAdvancementMasterList
b.Run("ValidateAll", func(b *testing.B) {
for b.Loop() {
_ = ml.ValidateAll()
}
})
b.Run("IndividualValidation", func(b *testing.B) {
aa := createTestAltAdvancement(b, 1001)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = aa.IsValid()
}
})
})
}
// BenchmarkCloneOperations measures cloning performance
func BenchmarkCloneOperations(b *testing.B) {
setupSharedAltAdvancementMasterList(b)
ml := sharedAltAdvancementMasterList
b.Run("GetAltAdvancementClone", func(b *testing.B) {
for b.Loop() {
id := int32(rand.Intn(1000) + 1)
_ = ml.GetAltAdvancementClone(id)
}
})
b.Run("DirectClone", func(b *testing.B) {
aa := createTestAltAdvancement(b, 1001)
for b.Loop() {
_ = aa.Clone()
}
})
}