eq2go/internal/entity/benchmark_test.go

507 lines
12 KiB
Go

package entity
import (
"fmt"
"math/rand"
"testing"
"time"
)
// BenchmarkEntityCreation measures entity creation performance
func BenchmarkEntityCreation(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
entity := NewEntity()
_ = entity
}
})
}
// BenchmarkEntityCombatState measures combat state operations
func BenchmarkEntityCombatState(b *testing.B) {
entity := NewEntity()
b.Run("Sequential", func(b *testing.B) {
for i := 0; i < b.N; i++ {
entity.SetInCombat(true)
_ = entity.IsInCombat()
entity.SetInCombat(false)
}
})
b.Run("Parallel", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
entity.SetInCombat(true)
_ = entity.IsInCombat()
entity.SetInCombat(false)
}
})
})
}
// BenchmarkEntityCastingState measures casting state operations
func BenchmarkEntityCastingState(b *testing.B) {
entity := NewEntity()
b.Run("Sequential", func(b *testing.B) {
for i := 0; i < b.N; i++ {
entity.SetCasting(true)
_ = entity.IsCasting()
entity.SetCasting(false)
}
})
b.Run("Parallel", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
entity.SetCasting(true)
_ = entity.IsCasting()
entity.SetCasting(false)
}
})
})
}
// BenchmarkEntityStatCalculations measures stat calculation performance
func BenchmarkEntityStatCalculations(b *testing.B) {
entity := NewEntity()
info := entity.GetInfoStruct()
// Set up some base stats
info.SetStr(100.0)
info.SetSta(100.0)
info.SetAgi(100.0)
info.SetWis(100.0)
info.SetIntel(100.0)
b.Run("GetStats", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = entity.GetStr()
_ = entity.GetSta()
_ = entity.GetAgi()
_ = entity.GetWis()
_ = entity.GetIntel()
}
})
b.Run("GetStatsParallel", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = entity.GetStr()
_ = entity.GetSta()
_ = entity.GetAgi()
_ = entity.GetWis()
_ = entity.GetIntel()
}
})
})
b.Run("GetPrimaryStat", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = entity.GetPrimaryStat()
}
})
b.Run("CalculateBonuses", func(b *testing.B) {
for i := 0; i < b.N; i++ {
entity.CalculateBonuses()
}
})
}
// BenchmarkEntitySpellEffects measures spell effect operations
func BenchmarkEntitySpellEffects(b *testing.B) {
entity := NewEntity()
b.Run("AddRemoveSpellEffect", func(b *testing.B) {
for i := 0; i < b.N; i++ {
spellID := int32(i + 1000)
entity.AddSpellEffect(spellID, 123, 30.0)
entity.RemoveSpellEffect(spellID)
}
})
b.Run("AddRemoveSpellEffectParallel", func(b *testing.B) {
var counter int64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
spellID := int32(counter + 1000)
counter++
entity.AddSpellEffect(spellID, 123, 30.0)
entity.RemoveSpellEffect(spellID)
}
})
})
}
// BenchmarkEntityMaintainedSpells measures maintained spell operations
func BenchmarkEntityMaintainedSpells(b *testing.B) {
entity := NewEntity()
info := entity.GetInfoStruct()
info.SetMaxConcentration(1000) // Large pool for benchmarking
b.Run("AddRemoveMaintainedSpell", func(b *testing.B) {
for i := 0; i < b.N; i++ {
spellID := int32(i + 2000)
if entity.AddMaintainedSpell("Benchmark Spell", spellID, 60.0, 1) {
entity.RemoveMaintainedSpell(spellID)
}
}
})
b.Run("AddRemoveMaintainedSpellParallel", func(b *testing.B) {
var counter int64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
spellID := int32(counter + 2000)
counter++
if entity.AddMaintainedSpell("Benchmark Spell", spellID, 60.0, 1) {
entity.RemoveMaintainedSpell(spellID)
}
}
})
})
}
// BenchmarkInfoStructBasicOps measures basic InfoStruct operations
func BenchmarkInfoStructBasicOps(b *testing.B) {
info := NewInfoStruct()
b.Run("SetGetName", func(b *testing.B) {
for i := 0; i < b.N; i++ {
info.SetName("BenchmarkCharacter")
_ = info.GetName()
}
})
b.Run("SetGetNameParallel", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
info.SetName("BenchmarkCharacter")
_ = info.GetName()
}
})
})
b.Run("SetGetLevel", func(b *testing.B) {
for i := 0; i < b.N; i++ {
info.SetLevel(int16(i % 100))
_ = info.GetLevel()
}
})
b.Run("SetGetStats", func(b *testing.B) {
for i := 0; i < b.N; i++ {
val := float32(i % 1000)
info.SetStr(val)
info.SetSta(val)
info.SetAgi(val)
info.SetWis(val)
info.SetIntel(val)
_ = info.GetStr()
_ = info.GetSta()
_ = info.GetAgi()
_ = info.GetWis()
_ = info.GetIntel()
}
})
}
// BenchmarkInfoStructConcentration measures concentration operations
func BenchmarkInfoStructConcentration(b *testing.B) {
info := NewInfoStruct()
info.SetMaxConcentration(1000)
b.Run("AddRemoveConcentration", func(b *testing.B) {
for i := 0; i < b.N; i++ {
amount := int16(i%10 + 1)
if info.AddConcentration(amount) {
info.RemoveConcentration(amount)
}
}
})
b.Run("AddRemoveConcentrationParallel", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
amount := int16(1) // Use small amount to reduce contention
if info.AddConcentration(amount) {
info.RemoveConcentration(amount)
}
}
})
})
}
// BenchmarkInfoStructCoins measures coin operations
func BenchmarkInfoStructCoins(b *testing.B) {
info := NewInfoStruct()
b.Run("AddRemoveCoins", func(b *testing.B) {
for i := 0; i < b.N; i++ {
amount := int32(i%10000 + 1)
info.AddCoins(amount)
info.RemoveCoins(amount / 2)
}
})
b.Run("AddRemoveCoinsParallel", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
amount := int32(100)
info.AddCoins(amount)
info.RemoveCoins(amount / 2)
}
})
})
b.Run("GetCoins", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = info.GetCoins()
}
})
}
// BenchmarkInfoStructResistances measures resistance operations
func BenchmarkInfoStructResistances(b *testing.B) {
info := NewInfoStruct()
resistTypes := []string{"heat", "cold", "magic", "mental", "divine", "disease", "poison"}
b.Run("SetGetResistances", func(b *testing.B) {
for i := 0; i < b.N; i++ {
resistType := resistTypes[i%len(resistTypes)]
value := int16(i % 100)
info.SetResistance(resistType, value)
_ = info.GetResistance(resistType)
}
})
b.Run("SetGetResistancesParallel", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
resistType := resistTypes[rand.Intn(len(resistTypes))]
value := int16(rand.Intn(100))
info.SetResistance(resistType, value)
_ = info.GetResistance(resistType)
}
})
})
}
// BenchmarkInfoStructClone measures clone operations
func BenchmarkInfoStructClone(b *testing.B) {
info := NewInfoStruct()
// Set up some state to clone
info.SetName("Original Character")
info.SetLevel(50)
info.SetStr(100.0)
info.SetSta(120.0)
info.SetAgi(90.0)
info.SetWis(110.0)
info.SetIntel(105.0)
info.AddConcentration(5)
info.AddCoins(50000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
clone := info.Clone()
_ = clone
}
}
// BenchmarkEntityPetManagement measures pet management operations
func BenchmarkEntityPetManagement(b *testing.B) {
entity := NewEntity()
b.Run("SetGetPet", func(b *testing.B) {
for i := 0; i < b.N; i++ {
pet := NewEntity()
entity.SetPet(pet)
_ = entity.GetPet()
entity.SetPet(nil)
}
})
b.Run("SetGetPetParallel", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
pet := NewEntity()
entity.SetPet(pet)
_ = entity.GetPet()
entity.SetPet(nil)
}
})
})
b.Run("AllPetTypes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
pet := NewEntity()
entity.SetPet(pet)
_ = entity.GetPet()
entity.SetCharmedPet(pet)
_ = entity.GetCharmedPet()
entity.SetDeityPet(pet)
_ = entity.GetDeityPet()
entity.SetCosmeticPet(pet)
_ = entity.GetCosmeticPet()
// Clear all pets
entity.SetPet(nil)
entity.SetCharmedPet(nil)
entity.SetDeityPet(nil)
entity.SetCosmeticPet(nil)
}
})
}
// BenchmarkConcurrentWorkload simulates a realistic concurrent workload
func BenchmarkConcurrentWorkload(b *testing.B) {
numEntities := 100
entities := make([]*Entity, numEntities)
// Create entities
for i := 0; i < numEntities; i++ {
entities[i] = NewEntity()
info := entities[i].GetInfoStruct()
info.SetMaxConcentration(50)
info.SetName("Entity" + string(rune('A'+i%26)))
info.SetLevel(int16(i%100 + 1))
}
b.ResetTimer()
b.Run("MixedOperations", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
entityIdx := rand.Intn(numEntities)
entity := entities[entityIdx]
switch rand.Intn(10) {
case 0, 1: // Combat state changes (20%)
entity.SetInCombat(rand.Intn(2) == 1)
_ = entity.IsInCombat()
case 2, 3: // Stat reads (20%)
_ = entity.GetStr()
_ = entity.GetSta()
_ = entity.GetPrimaryStat()
case 4: // Spell effects (10%)
spellID := int32(rand.Intn(1000) + 10000)
entity.AddSpellEffect(spellID, int32(entityIdx), 30.0)
entity.RemoveSpellEffect(spellID)
case 5: // Maintained spells (10%)
spellID := int32(rand.Intn(100) + 20000)
if entity.AddMaintainedSpell("Workload Spell", spellID, 60.0, 1) {
entity.RemoveMaintainedSpell(spellID)
}
case 6, 7: // InfoStruct operations (20%)
info := entity.GetInfoStruct()
info.SetStr(float32(rand.Intn(200) + 50))
_ = info.GetStr()
case 8: // Coin operations (10%)
info := entity.GetInfoStruct()
info.AddCoins(int32(rand.Intn(1000)))
_ = info.GetCoins()
case 9: // Resistance operations (10%)
info := entity.GetInfoStruct()
resistTypes := []string{"heat", "cold", "magic", "mental"}
resistType := resistTypes[rand.Intn(len(resistTypes))]
info.SetResistance(resistType, int16(rand.Intn(100)))
_ = info.GetResistance(resistType)
}
}
})
})
}
// BenchmarkMemoryAllocation measures memory allocation patterns
func BenchmarkMemoryAllocation(b *testing.B) {
b.Run("EntityAllocation", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
entity := NewEntity()
_ = entity
}
})
b.Run("InfoStructAllocation", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
info := NewInfoStruct()
_ = info
}
})
b.Run("CloneAllocation", func(b *testing.B) {
info := NewInfoStruct()
info.SetName("Test")
info.SetLevel(50)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
clone := info.Clone()
_ = clone
}
})
}
// BenchmarkContention measures performance under high contention
func BenchmarkContention(b *testing.B) {
entity := NewEntity()
info := entity.GetInfoStruct()
info.SetMaxConcentration(10) // Low limit to create contention
b.Run("HighContentionConcentration", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if info.AddConcentration(1) {
// Hold for a brief moment to increase contention
time.Sleep(time.Nanosecond)
info.RemoveConcentration(1)
}
}
})
})
b.Run("HighContentionSpellEffects", func(b *testing.B) {
var spellCounter int64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
spellID := int32(spellCounter % 100) // Reuse spell IDs to create contention
spellCounter++
entity.AddSpellEffect(spellID, 123, 30.0)
entity.RemoveSpellEffect(spellID)
}
})
})
}
// BenchmarkScalability tests performance as load increases
func BenchmarkScalability(b *testing.B) {
goroutineCounts := []int{1, 2, 4, 8, 16, 32, 64}
for _, numGoroutines := range goroutineCounts {
b.Run(fmt.Sprintf("Goroutines_%d", numGoroutines), func(b *testing.B) {
entity := NewEntity()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
entity.SetInCombat(true)
_ = entity.IsInCombat()
entity.SetInCombat(false)
}
})
})
}
}