430 lines
10 KiB
Go
430 lines
10 KiB
Go
package ground_spawn
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"testing"
|
|
|
|
"eq2emu/internal/database"
|
|
)
|
|
|
|
// Mock implementations for benchmarking
|
|
|
|
// mockPlayer implements Player interface for benchmarks
|
|
type mockPlayer struct {
|
|
level int16
|
|
location int32
|
|
name string
|
|
}
|
|
|
|
func (p *mockPlayer) GetLevel() int16 { return p.level }
|
|
func (p *mockPlayer) GetLocation() int32 { return p.location }
|
|
func (p *mockPlayer) GetName() string { return p.name }
|
|
|
|
// mockSkill implements Skill interface for benchmarks
|
|
type mockSkill struct {
|
|
current int16
|
|
max int16
|
|
}
|
|
|
|
func (s *mockSkill) GetCurrentValue() int16 { return s.current }
|
|
func (s *mockSkill) GetMaxValue() int16 { return s.max }
|
|
|
|
// createTestGroundSpawn creates a ground spawn for benchmarking
|
|
func createTestGroundSpawn(b *testing.B, id int32) *GroundSpawn {
|
|
b.Helper()
|
|
|
|
db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared")
|
|
if err != nil {
|
|
b.Fatalf("Failed to create test database: %v", err)
|
|
}
|
|
|
|
gs := New(db)
|
|
gs.GroundSpawnID = id
|
|
gs.Name = fmt.Sprintf("Benchmark Node %d", id)
|
|
gs.CollectionSkill = "Mining"
|
|
gs.NumberHarvests = 5
|
|
gs.AttemptsPerHarvest = 1
|
|
gs.CurrentHarvests = 5
|
|
gs.X, gs.Y, gs.Z = float32(rand.Intn(1000)), float32(rand.Intn(1000)), float32(rand.Intn(1000))
|
|
gs.ZoneID = int32(rand.Intn(10) + 1)
|
|
gs.GridID = int32(rand.Intn(100))
|
|
|
|
// Add mock harvest entries for realistic benchmarking
|
|
gs.HarvestEntries = []*HarvestEntry{
|
|
{
|
|
GroundSpawnID: id,
|
|
MinSkillLevel: 10,
|
|
MinAdventureLevel: 1,
|
|
BonusTable: false,
|
|
Harvest1: 80.0,
|
|
Harvest3: 20.0,
|
|
Harvest5: 10.0,
|
|
HarvestImbue: 5.0,
|
|
HarvestRare: 2.0,
|
|
Harvest10: 1.0,
|
|
},
|
|
{
|
|
GroundSpawnID: id,
|
|
MinSkillLevel: 50,
|
|
MinAdventureLevel: 10,
|
|
BonusTable: true,
|
|
Harvest1: 90.0,
|
|
Harvest3: 30.0,
|
|
Harvest5: 15.0,
|
|
HarvestImbue: 8.0,
|
|
HarvestRare: 5.0,
|
|
Harvest10: 2.0,
|
|
},
|
|
}
|
|
|
|
// Add mock harvest items
|
|
gs.HarvestItems = []*HarvestEntryItem{
|
|
{GroundSpawnID: id, ItemID: 1001, IsRare: ItemRarityNormal, GridID: 0, Quantity: 1},
|
|
{GroundSpawnID: id, ItemID: 1002, IsRare: ItemRarityNormal, GridID: 0, Quantity: 1},
|
|
{GroundSpawnID: id, ItemID: 1003, IsRare: ItemRarityRare, GridID: 0, Quantity: 1},
|
|
{GroundSpawnID: id, ItemID: 1004, IsRare: ItemRarityImbue, GridID: 0, Quantity: 1},
|
|
}
|
|
|
|
return gs
|
|
}
|
|
|
|
// BenchmarkGroundSpawnCreation measures ground spawn creation performance
|
|
func BenchmarkGroundSpawnCreation(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++ {
|
|
gs := New(db)
|
|
gs.GroundSpawnID = int32(i)
|
|
gs.Name = fmt.Sprintf("Node %d", i)
|
|
_ = gs
|
|
}
|
|
})
|
|
|
|
b.Run("Parallel", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
id := int32(0)
|
|
for pb.Next() {
|
|
gs := New(db)
|
|
gs.GroundSpawnID = id
|
|
gs.Name = fmt.Sprintf("Node %d", id)
|
|
id++
|
|
_ = gs
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// BenchmarkGroundSpawnState measures state operations
|
|
func BenchmarkGroundSpawnState(b *testing.B) {
|
|
gs := createTestGroundSpawn(b, 1001)
|
|
|
|
b.Run("IsAvailable", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = gs.IsAvailable()
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("IsDepleted", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = gs.IsDepleted()
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("GetHarvestMessageName", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = gs.GetHarvestMessageName(true, false)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("GetHarvestSpellType", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = gs.GetHarvestSpellType()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// BenchmarkHarvestAlgorithm measures the core harvest processing performance
|
|
func BenchmarkHarvestAlgorithm(b *testing.B) {
|
|
gs := createTestGroundSpawn(b, 1001)
|
|
|
|
player := &mockPlayer{level: 50, location: 1, name: "BenchmarkPlayer"}
|
|
skill := &mockSkill{current: 75, max: 100}
|
|
|
|
b.Run("ProcessHarvest", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
// Reset harvest count for consistent benchmarking
|
|
gs.CurrentHarvests = gs.NumberHarvests
|
|
|
|
_, err := gs.ProcessHarvest(player, skill, 75)
|
|
if err != nil {
|
|
b.Fatalf("Harvest failed: %v", err)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("FilterHarvestTables", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = gs.filterHarvestTables(player, 75)
|
|
}
|
|
})
|
|
|
|
b.Run("SelectHarvestTable", func(b *testing.B) {
|
|
tables := gs.filterHarvestTables(player, 75)
|
|
for i := 0; i < b.N; i++ {
|
|
_ = gs.selectHarvestTable(tables, 75)
|
|
}
|
|
})
|
|
|
|
b.Run("DetermineHarvestType", func(b *testing.B) {
|
|
tables := gs.filterHarvestTables(player, 75)
|
|
table := gs.selectHarvestTable(tables, 75)
|
|
for i := 0; i < b.N; i++ {
|
|
_ = gs.determineHarvestType(table, false)
|
|
}
|
|
})
|
|
|
|
b.Run("AwardHarvestItems", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = gs.awardHarvestItems(HarvestType3Items, player)
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkMasterListOperations measures master list performance
|
|
func BenchmarkMasterListOperations(b *testing.B) {
|
|
ml := NewMasterList()
|
|
|
|
// Pre-populate with ground spawns for realistic testing
|
|
const numSpawns = 1000
|
|
spawns := make([]*GroundSpawn, numSpawns)
|
|
|
|
b.StopTimer()
|
|
for i := 0; i < numSpawns; i++ {
|
|
spawns[i] = createTestGroundSpawn(b, int32(i+1))
|
|
spawns[i].ZoneID = int32(i%10 + 1) // Distribute across 10 zones
|
|
spawns[i].CollectionSkill = []string{"Mining", "Gathering", "Fishing", "Trapping"}[i%4]
|
|
ml.AddGroundSpawn(spawns[i])
|
|
}
|
|
b.StartTimer()
|
|
|
|
b.Run("GetGroundSpawn", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
id := int32((rand.Intn(numSpawns) + 1))
|
|
_ = ml.GetGroundSpawn(id)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("AddGroundSpawn", func(b *testing.B) {
|
|
startID := int32(numSpawns + 1)
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
gs := createTestGroundSpawn(b, startID+int32(i))
|
|
ml.AddGroundSpawn(gs)
|
|
}
|
|
})
|
|
|
|
b.Run("GetByZone", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
zoneID := int32(rand.Intn(10) + 1)
|
|
_ = ml.GetByZone(zoneID)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("GetBySkill", func(b *testing.B) {
|
|
skills := []string{"Mining", "Gathering", "Fishing", "Trapping"}
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
skill := skills[rand.Intn(len(skills))]
|
|
_ = ml.GetBySkill(skill)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("GetAvailableSpawns", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.GetAvailableSpawns()
|
|
}
|
|
})
|
|
|
|
b.Run("GetDepletedSpawns", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.GetDepletedSpawns()
|
|
}
|
|
})
|
|
|
|
b.Run("GetStatistics", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.GetStatistics()
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkConcurrentHarvesting measures concurrent harvest performance
|
|
func BenchmarkConcurrentHarvesting(b *testing.B) {
|
|
const numSpawns = 100
|
|
spawns := make([]*GroundSpawn, numSpawns)
|
|
|
|
for i := 0; i < numSpawns; i++ {
|
|
spawns[i] = createTestGroundSpawn(b, int32(i+1))
|
|
}
|
|
|
|
player := &mockPlayer{level: 50, location: 1, name: "BenchmarkPlayer"}
|
|
skill := &mockSkill{current: 75, max: 100}
|
|
|
|
b.Run("ConcurrentHarvesting", func(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
spawnIdx := rand.Intn(numSpawns)
|
|
gs := spawns[spawnIdx]
|
|
|
|
// Reset if depleted
|
|
if gs.IsDepleted() {
|
|
gs.Respawn()
|
|
}
|
|
|
|
_, _ = gs.ProcessHarvest(player, skill, 75)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 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("GroundSpawnAllocation", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
gs := New(db)
|
|
gs.GroundSpawnID = int32(i)
|
|
gs.HarvestEntries = make([]*HarvestEntry, 2)
|
|
gs.HarvestItems = make([]*HarvestEntryItem, 4)
|
|
_ = gs
|
|
}
|
|
})
|
|
|
|
b.Run("MasterListAllocation", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
ml := NewMasterList()
|
|
_ = ml
|
|
}
|
|
})
|
|
|
|
b.Run("HarvestResultAllocation", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
result := &HarvestResult{
|
|
Success: true,
|
|
HarvestType: HarvestType3Items,
|
|
ItemsAwarded: make([]*HarvestedItem, 3),
|
|
MessageText: "You harvested 3 items",
|
|
SkillGained: false,
|
|
}
|
|
_ = result
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkRespawnOperations measures respawn performance
|
|
func BenchmarkRespawnOperations(b *testing.B) {
|
|
gs := createTestGroundSpawn(b, 1001)
|
|
|
|
b.Run("Respawn", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
gs.CurrentHarvests = 0 // Deplete
|
|
gs.Respawn()
|
|
}
|
|
})
|
|
|
|
b.Run("RespawnWithRandomHeading", func(b *testing.B) {
|
|
gs.RandomizeHeading = true
|
|
for i := 0; i < b.N; i++ {
|
|
gs.CurrentHarvests = 0 // Deplete
|
|
gs.Respawn()
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkStringOperations measures string processing performance
|
|
func BenchmarkStringOperations(b *testing.B) {
|
|
skills := []string{"Mining", "Gathering", "Collecting", "Fishing", "Trapping", "Foresting", "Unknown"}
|
|
|
|
b.Run("HarvestMessageGeneration", func(b *testing.B) {
|
|
gs := createTestGroundSpawn(b, 1001)
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
skill := skills[rand.Intn(len(skills))]
|
|
gs.CollectionSkill = skill
|
|
_ = gs.GetHarvestMessageName(rand.Intn(2) == 1, rand.Intn(2) == 1)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("SpellTypeGeneration", func(b *testing.B) {
|
|
gs := createTestGroundSpawn(b, 1001)
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
skill := skills[rand.Intn(len(skills))]
|
|
gs.CollectionSkill = skill
|
|
_ = gs.GetHarvestSpellType()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// BenchmarkComparisonWithOldSystem provides comparison benchmarks
|
|
// These would help measure the performance improvement from modernization
|
|
func BenchmarkComparisonWithOldSystem(b *testing.B) {
|
|
ml := NewMasterList()
|
|
const numSpawns = 1000
|
|
|
|
// Setup
|
|
b.StopTimer()
|
|
for i := 0; i < numSpawns; i++ {
|
|
gs := createTestGroundSpawn(b, int32(i+1))
|
|
ml.AddGroundSpawn(gs)
|
|
}
|
|
b.StartTimer()
|
|
|
|
b.Run("ModernizedLookup", func(b *testing.B) {
|
|
// Modern generic-based lookup
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
id := int32(rand.Intn(numSpawns) + 1)
|
|
_ = ml.GetGroundSpawn(id)
|
|
}
|
|
})
|
|
})
|
|
|
|
b.Run("ModernizedFiltering", func(b *testing.B) {
|
|
// Modern filter-based operations
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ml.GetAvailableSpawns()
|
|
}
|
|
})
|
|
} |