549 lines
13 KiB
Go
549 lines
13 KiB
Go
package appearances
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"sync"
|
|
"testing"
|
|
|
|
"eq2emu/internal/database"
|
|
)
|
|
|
|
// Global shared master list for benchmarks to avoid repeated setup
|
|
var (
|
|
sharedAppearanceMasterList *MasterList
|
|
sharedAppearances []*Appearance
|
|
appearanceSetupOnce sync.Once
|
|
)
|
|
|
|
// setupSharedAppearanceMasterList creates the shared master list once
|
|
func setupSharedAppearanceMasterList(b *testing.B) {
|
|
appearanceSetupOnce.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)
|
|
}
|
|
|
|
sharedAppearanceMasterList = NewMasterList()
|
|
|
|
// Pre-populate with appearances for realistic testing
|
|
const numAppearances = 1000
|
|
sharedAppearances = make([]*Appearance, numAppearances)
|
|
|
|
clientVersions := []int16{1096, 1200, 1300, 1400, 1500}
|
|
nameTemplates := []string{
|
|
"Human %s",
|
|
"Elf %s",
|
|
"Dwarf %s",
|
|
"Halfling %s",
|
|
"Barbarian %s",
|
|
"Dark Elf %s",
|
|
"Wood Elf %s",
|
|
"High Elf %s",
|
|
"Gnome %s",
|
|
"Troll %s",
|
|
}
|
|
genders := []string{"Male", "Female"}
|
|
|
|
for i := range numAppearances {
|
|
sharedAppearances[i] = NewWithData(
|
|
int32(i+1),
|
|
fmt.Sprintf(nameTemplates[i%len(nameTemplates)], genders[i%len(genders)]),
|
|
clientVersions[i%len(clientVersions)],
|
|
db,
|
|
)
|
|
|
|
sharedAppearanceMasterList.AddAppearance(sharedAppearances[i])
|
|
}
|
|
})
|
|
}
|
|
|
|
// createTestAppearance creates an appearance for benchmarking
|
|
func createTestAppearance(b *testing.B, id int32) *Appearance {
|
|
b.Helper()
|
|
|
|
// Use nil database for benchmarking in-memory operations
|
|
clientVersions := []int16{1096, 1200, 1300, 1400, 1500}
|
|
nameTemplates := []string{"Human", "Elf", "Dwarf", "Halfling"}
|
|
genders := []string{"Male", "Female"}
|
|
|
|
app := NewWithData(
|
|
id,
|
|
fmt.Sprintf("Benchmark %s %s", nameTemplates[id%int32(len(nameTemplates))], genders[id%2]),
|
|
clientVersions[id%int32(len(clientVersions))],
|
|
nil,
|
|
)
|
|
|
|
return app
|
|
}
|
|
|
|
// BenchmarkAppearanceCreation measures appearance creation performance
|
|
func BenchmarkAppearanceCreation(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++ {
|
|
app := New(db)
|
|
app.ID = int32(i)
|
|
app.Name = fmt.Sprintf("Appearance %d", i)
|
|
app.MinClient = 1096
|
|
_ = app
|
|
}
|
|
})
|
|
|
|
b.Run("Parallel", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
id := int32(0)
|
|
for pb.Next() {
|
|
app := New(db)
|
|
app.ID = id
|
|
app.Name = fmt.Sprintf("Appearance %d", id)
|
|
app.MinClient = 1096
|
|
id++
|
|
_ = app
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("NewWithData", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := NewWithData(int32(i), fmt.Sprintf("Appearance %d", i), 1096, db)
|
|
_ = app
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkAppearanceOperations measures individual appearance operations
|
|
func BenchmarkAppearanceOperations(b *testing.B) {
|
|
app := createTestAppearance(b, 1001)
|
|
|
|
b.Run("GetID", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = app.GetID()
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("GetName", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = app.GetName()
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("GetMinClientVersion", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = app.GetMinClientVersion()
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("IsCompatibleWithClient", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = app.IsCompatibleWithClient(1200)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("Clone", func(b *testing.B) {
|
|
for b.Loop() {
|
|
_ = app.Clone()
|
|
}
|
|
})
|
|
|
|
b.Run("IsNew", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = app.IsNew()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// BenchmarkMasterListOperations measures master list performance
|
|
func BenchmarkMasterListOperations(b *testing.B) {
|
|
setupSharedAppearanceMasterList(b)
|
|
ml := sharedAppearanceMasterList
|
|
|
|
b.Run("GetAppearance", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
id := int32(rand.Intn(1000) + 1)
|
|
_ = ml.GetAppearance(id)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("AddAppearance", func(b *testing.B) {
|
|
// Create a separate master list for add operations
|
|
addML := NewMasterList()
|
|
startID := int32(10000)
|
|
// Pre-create appearances to measure just the Add operation
|
|
appsToAdd := make([]*Appearance, b.N)
|
|
for i := 0; i < b.N; i++ {
|
|
appsToAdd[i] = createTestAppearance(b, startID+int32(i))
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
addML.AddAppearance(appsToAdd[i])
|
|
}
|
|
})
|
|
|
|
b.Run("GetAppearanceSafe", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
id := int32(rand.Intn(1000) + 1)
|
|
_, _ = ml.GetAppearanceSafe(id)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("HasAppearance", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
id := int32(rand.Intn(1000) + 1)
|
|
_ = ml.HasAppearance(id)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("FindAppearancesByMinClient", func(b *testing.B) {
|
|
clientVersions := []int16{1096, 1200, 1300, 1400, 1500}
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
version := clientVersions[rand.Intn(len(clientVersions))]
|
|
_ = ml.FindAppearancesByMinClient(version)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("GetCompatibleAppearances", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
version := int16(1200 + rand.Intn(300))
|
|
_ = ml.GetCompatibleAppearances(version)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("FindAppearancesByName", func(b *testing.B) {
|
|
searchTerms := []string{"human", "male", "elf", "female", "dwarf"}
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
term := searchTerms[rand.Intn(len(searchTerms))]
|
|
_ = ml.FindAppearancesByName(term)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("GetAppearancesByIDRange", func(b *testing.B) {
|
|
for b.Loop() {
|
|
start := int32(rand.Intn(900) + 1)
|
|
end := start + int32(rand.Intn(100)+10)
|
|
_ = ml.GetAppearancesByIDRange(start, end)
|
|
}
|
|
})
|
|
|
|
b.Run("GetAppearancesByClientRange", func(b *testing.B) {
|
|
for b.Loop() {
|
|
minVersion := int16(1096 + rand.Intn(200))
|
|
maxVersion := minVersion + int16(rand.Intn(200))
|
|
_ = ml.GetAppearancesByClientRange(minVersion, maxVersion)
|
|
}
|
|
})
|
|
|
|
b.Run("GetClientVersions", func(b *testing.B) {
|
|
for b.Loop() {
|
|
_ = ml.GetClientVersions()
|
|
}
|
|
})
|
|
|
|
b.Run("Size", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = ml.Size()
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("GetAppearanceCount", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = ml.GetAppearanceCount()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// BenchmarkConcurrentOperations tests mixed workload performance
|
|
func BenchmarkConcurrentOperations(b *testing.B) {
|
|
setupSharedAppearanceMasterList(b)
|
|
ml := sharedAppearanceMasterList
|
|
|
|
b.Run("MixedOperations", func(b *testing.B) {
|
|
clientVersions := []int16{1096, 1200, 1300, 1400, 1500}
|
|
searchTerms := []string{"human", "male", "elf", "female", "dwarf"}
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
switch rand.Intn(10) {
|
|
case 0:
|
|
id := int32(rand.Intn(1000) + 1)
|
|
_ = ml.GetAppearance(id)
|
|
case 1:
|
|
id := int32(rand.Intn(1000) + 1)
|
|
_, _ = ml.GetAppearanceSafe(id)
|
|
case 2:
|
|
id := int32(rand.Intn(1000) + 1)
|
|
_ = ml.HasAppearance(id)
|
|
case 3:
|
|
version := clientVersions[rand.Intn(len(clientVersions))]
|
|
_ = ml.FindAppearancesByMinClient(version)
|
|
case 4:
|
|
version := int16(1200 + rand.Intn(300))
|
|
_ = ml.GetCompatibleAppearances(version)
|
|
case 5:
|
|
term := searchTerms[rand.Intn(len(searchTerms))]
|
|
_ = ml.FindAppearancesByName(term)
|
|
case 6:
|
|
start := int32(rand.Intn(900) + 1)
|
|
end := start + int32(rand.Intn(100)+10)
|
|
_ = ml.GetAppearancesByIDRange(start, end)
|
|
case 7:
|
|
minVersion := int16(1096 + rand.Intn(200))
|
|
maxVersion := minVersion + int16(rand.Intn(200))
|
|
_ = ml.GetAppearancesByClientRange(minVersion, maxVersion)
|
|
case 8:
|
|
_ = ml.GetClientVersions()
|
|
case 9:
|
|
_ = 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("AppearanceAllocation", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
app := New(db)
|
|
app.ID = int32(i)
|
|
app.Name = fmt.Sprintf("Appearance %d", i)
|
|
app.MinClient = 1096
|
|
_ = app
|
|
}
|
|
})
|
|
|
|
b.Run("NewWithDataAllocation", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
app := NewWithData(int32(i), fmt.Sprintf("Appearance %d", i), 1096, db)
|
|
_ = app
|
|
}
|
|
})
|
|
|
|
b.Run("MasterListAllocation", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
ml := NewMasterList()
|
|
_ = ml
|
|
}
|
|
})
|
|
|
|
b.Run("AddAppearance_Allocations", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
ml := NewMasterList()
|
|
for i := 0; i < b.N; i++ {
|
|
app := createTestAppearance(b, int32(i+1))
|
|
ml.AddAppearance(app)
|
|
}
|
|
})
|
|
|
|
b.Run("FindAppearancesByMinClient_Allocations", func(b *testing.B) {
|
|
setupSharedAppearanceMasterList(b)
|
|
ml := sharedAppearanceMasterList
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for b.Loop() {
|
|
_ = ml.FindAppearancesByMinClient(1096)
|
|
}
|
|
})
|
|
|
|
b.Run("FindAppearancesByName_Allocations", func(b *testing.B) {
|
|
setupSharedAppearanceMasterList(b)
|
|
ml := sharedAppearanceMasterList
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for b.Loop() {
|
|
_ = ml.FindAppearancesByName("human")
|
|
}
|
|
})
|
|
|
|
b.Run("GetClientVersions_Allocations", func(b *testing.B) {
|
|
setupSharedAppearanceMasterList(b)
|
|
ml := sharedAppearanceMasterList
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for b.Loop() {
|
|
_ = ml.GetClientVersions()
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkUpdateOperations measures update performance
|
|
func BenchmarkUpdateOperations(b *testing.B) {
|
|
setupSharedAppearanceMasterList(b)
|
|
ml := sharedAppearanceMasterList
|
|
|
|
b.Run("UpdateAppearance", func(b *testing.B) {
|
|
// Create appearances to update
|
|
updateApps := make([]*Appearance, b.N)
|
|
for i := 0; i < b.N; i++ {
|
|
updateApps[i] = createTestAppearance(b, int32((i%1000)+1))
|
|
updateApps[i].Name = "Updated Name"
|
|
updateApps[i].MinClient = 1600
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.UpdateAppearance(updateApps[i])
|
|
}
|
|
})
|
|
|
|
b.Run("RemoveAppearance", func(b *testing.B) {
|
|
// Create a separate master list for removal testing
|
|
removeML := NewMasterList()
|
|
|
|
// Add appearances to remove
|
|
for i := 0; i < b.N; i++ {
|
|
app := createTestAppearance(b, int32(i+1))
|
|
removeML.AddAppearance(app)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
removeML.RemoveAppearance(int32(i + 1))
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkValidation measures validation performance
|
|
func BenchmarkValidation(b *testing.B) {
|
|
setupSharedAppearanceMasterList(b)
|
|
ml := sharedAppearanceMasterList
|
|
|
|
b.Run("ValidateAppearances", func(b *testing.B) {
|
|
for b.Loop() {
|
|
_ = ml.ValidateAppearances()
|
|
}
|
|
})
|
|
|
|
b.Run("IsValid", func(b *testing.B) {
|
|
for b.Loop() {
|
|
_ = ml.IsValid()
|
|
}
|
|
})
|
|
|
|
b.Run("IndividualValidation", func(b *testing.B) {
|
|
app := createTestAppearance(b, 1001)
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = app.IsCompatibleWithClient(1200)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// BenchmarkCloneOperations measures cloning performance
|
|
func BenchmarkCloneOperations(b *testing.B) {
|
|
setupSharedAppearanceMasterList(b)
|
|
ml := sharedAppearanceMasterList
|
|
|
|
b.Run("GetAppearanceClone", func(b *testing.B) {
|
|
for b.Loop() {
|
|
id := int32(rand.Intn(1000) + 1)
|
|
_ = ml.GetAppearanceClone(id)
|
|
}
|
|
})
|
|
|
|
b.Run("DirectClone", func(b *testing.B) {
|
|
app := createTestAppearance(b, 1001)
|
|
for b.Loop() {
|
|
_ = app.Clone()
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkStatistics measures statistics performance
|
|
func BenchmarkStatistics(b *testing.B) {
|
|
setupSharedAppearanceMasterList(b)
|
|
ml := sharedAppearanceMasterList
|
|
|
|
b.Run("GetStatistics", func(b *testing.B) {
|
|
for b.Loop() {
|
|
_ = ml.GetStatistics()
|
|
}
|
|
})
|
|
|
|
b.Run("GetAllAppearances", func(b *testing.B) {
|
|
for b.Loop() {
|
|
_ = ml.GetAllAppearances()
|
|
}
|
|
})
|
|
|
|
b.Run("GetAllAppearancesList", func(b *testing.B) {
|
|
for b.Loop() {
|
|
_ = ml.GetAllAppearancesList()
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkStringOperations measures string operations performance
|
|
func BenchmarkStringOperations(b *testing.B) {
|
|
b.Run("GetAppearanceType", func(b *testing.B) {
|
|
typeNames := []string{"hair_color1", "skin_color", "eye_color", "unknown_type"}
|
|
for b.Loop() {
|
|
typeName := typeNames[rand.Intn(len(typeNames))]
|
|
_ = GetAppearanceType(typeName)
|
|
}
|
|
})
|
|
|
|
b.Run("GetAppearanceTypeName", func(b *testing.B) {
|
|
typeConstants := []int8{AppearanceHairColor1, AppearanceSkinColor, AppearanceEyeColor, -1}
|
|
for b.Loop() {
|
|
typeConst := typeConstants[rand.Intn(len(typeConstants))]
|
|
_ = GetAppearanceTypeName(typeConst)
|
|
}
|
|
})
|
|
|
|
b.Run("ContainsSubstring", func(b *testing.B) {
|
|
testStrings := []string{"Human Male Fighter", "Elf Female Mage", "Dwarf Male Warrior"}
|
|
searchTerms := []string{"human", "male", "elf", "notfound"}
|
|
|
|
for b.Loop() {
|
|
str := testStrings[rand.Intn(len(testStrings))]
|
|
term := searchTerms[rand.Intn(len(searchTerms))]
|
|
_ = contains(str, term)
|
|
}
|
|
})
|
|
}
|