710 lines
20 KiB
Markdown
710 lines
20 KiB
Markdown
# Phase 1: Immediate Optimizations Implementation
|
|
|
|
## Overview
|
|
These optimizations can be implemented immediately with minimal risk to provide 60-70% performance improvement while more complex architectural changes are developed.
|
|
|
|
## 1. Manager Update Throttling System
|
|
|
|
### File: `src/modules/Playerbot/Core/Managers/UpdateThrottler.h`
|
|
```cpp
|
|
#pragma once
|
|
|
|
#include "Define.h"
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <array>
|
|
|
|
namespace Playerbot
|
|
{
|
|
|
|
/**
|
|
* @brief Intelligent update throttling based on manager priority and system load
|
|
*
|
|
* This system reduces update frequency for non-critical managers under load
|
|
* while maintaining responsiveness for critical operations.
|
|
*/
|
|
class UpdateThrottler
|
|
{
|
|
public:
|
|
enum class Priority : uint8
|
|
{
|
|
CRITICAL = 0, // Combat, movement - every frame
|
|
HIGH = 1, // Quest, trade - 10 Hz (100ms)
|
|
MEDIUM = 2, // Gathering, group - 2 Hz (500ms)
|
|
LOW = 3, // Auction, crafting - 1 Hz (1000ms)
|
|
BACKGROUND = 4 // Statistics, cleanup - 0.1 Hz (10000ms)
|
|
};
|
|
|
|
struct ThrottleConfig
|
|
{
|
|
uint32 baseInterval; // Base update interval in ms
|
|
uint32 loadMultiplier; // Multiplier under high load
|
|
uint32 maxInterval; // Maximum interval cap
|
|
bool skipUnderLoad; // Can skip entirely under extreme load
|
|
};
|
|
|
|
private:
|
|
// Configuration per priority level
|
|
static constexpr std::array<ThrottleConfig, 5> s_configs = {{
|
|
{0, 1, 0, false}, // CRITICAL - never throttle
|
|
{100, 2, 500, false}, // HIGH - throttle to 200ms under load
|
|
{500, 3, 2000, false}, // MEDIUM - throttle to 1500ms under load
|
|
{1000, 5, 10000, true}, // LOW - throttle heavily or skip
|
|
{10000, 10, 60000, true} // BACKGROUND - aggressive throttling
|
|
}};
|
|
|
|
// Per-manager state
|
|
struct ManagerState
|
|
{
|
|
Priority priority;
|
|
uint32 lastUpdate{0};
|
|
uint32 nextUpdate{0};
|
|
std::atomic<uint32> skipCount{0};
|
|
std::atomic<uint64> totalUpdates{0};
|
|
};
|
|
|
|
std::unordered_map<std::string, ManagerState> _managerStates;
|
|
std::atomic<float> _systemLoad{0.0f}; // 0.0 = idle, 1.0 = full load
|
|
std::atomic<uint32> _activeBots{0};
|
|
|
|
public:
|
|
static UpdateThrottler& Instance()
|
|
{
|
|
static UpdateThrottler instance;
|
|
return instance;
|
|
}
|
|
|
|
/**
|
|
* Register a manager with its priority
|
|
*/
|
|
void RegisterManager(std::string const& managerId, Priority priority)
|
|
{
|
|
_managerStates[managerId] = {priority, 0, 0, {0}, {0}};
|
|
}
|
|
|
|
/**
|
|
* Check if a manager should update this frame
|
|
*/
|
|
bool ShouldUpdate(std::string const& managerId, uint32 currentTime)
|
|
{
|
|
auto it = _managerStates.find(managerId);
|
|
if (it == _managerStates.end())
|
|
return true; // Unknown managers always update (safety)
|
|
|
|
auto& state = it->second;
|
|
|
|
// Critical always updates
|
|
if (state.priority == Priority::CRITICAL)
|
|
return true;
|
|
|
|
// Check if enough time has passed
|
|
if (currentTime < state.nextUpdate)
|
|
{
|
|
state.skipCount.fetch_add(1, std::memory_order_relaxed);
|
|
return false;
|
|
}
|
|
|
|
// Calculate next update time based on load
|
|
auto& config = s_configs[static_cast<size_t>(state.priority)];
|
|
float load = _systemLoad.load(std::memory_order_relaxed);
|
|
|
|
// Skip entirely under extreme load if configured
|
|
if (config.skipUnderLoad && load > 0.9f)
|
|
{
|
|
state.skipCount.fetch_add(1, std::memory_order_relaxed);
|
|
return false;
|
|
}
|
|
|
|
// Calculate throttled interval
|
|
uint32 interval = config.baseInterval;
|
|
if (load > 0.5f)
|
|
{
|
|
interval = std::min(
|
|
static_cast<uint32>(interval * (1.0f + load * config.loadMultiplier)),
|
|
config.maxInterval
|
|
);
|
|
}
|
|
|
|
// Schedule next update
|
|
state.lastUpdate = currentTime;
|
|
state.nextUpdate = currentTime + interval;
|
|
state.totalUpdates.fetch_add(1, std::memory_order_relaxed);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Update system load metric (called by main update loop)
|
|
*/
|
|
void UpdateSystemLoad(uint32 frameTime, uint32 targetFrameTime = 50)
|
|
{
|
|
// Simple load calculation: frameTime / targetTime
|
|
float load = static_cast<float>(frameTime) / targetFrameTime;
|
|
_systemLoad.store(std::clamp(load, 0.0f, 1.0f), std::memory_order_relaxed);
|
|
}
|
|
|
|
/**
|
|
* Update active bot count for load calculations
|
|
*/
|
|
void SetActiveBots(uint32 count)
|
|
{
|
|
_activeBots.store(count, std::memory_order_relaxed);
|
|
|
|
// Adjust load based on bot count (>100 bots increases load factor)
|
|
if (count > 100)
|
|
{
|
|
float botLoad = static_cast<float>(count - 100) / 1000.0f; // 0.1 per 100 bots
|
|
float currentLoad = _systemLoad.load(std::memory_order_relaxed);
|
|
_systemLoad.store(std::min(currentLoad + botLoad, 1.0f), std::memory_order_relaxed);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get statistics for monitoring
|
|
*/
|
|
struct Stats
|
|
{
|
|
uint32 activeBots;
|
|
float systemLoad;
|
|
std::unordered_map<std::string, uint64> managerUpdates;
|
|
std::unordered_map<std::string, uint32> managerSkips;
|
|
};
|
|
|
|
Stats GetStats() const
|
|
{
|
|
Stats stats;
|
|
stats.activeBots = _activeBots.load(std::memory_order_relaxed);
|
|
stats.systemLoad = _systemLoad.load(std::memory_order_relaxed);
|
|
|
|
for (auto const& [id, state] : _managerStates)
|
|
{
|
|
stats.managerUpdates[id] = state.totalUpdates.load(std::memory_order_relaxed);
|
|
stats.managerSkips[id] = state.skipCount.load(std::memory_order_relaxed);
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
};
|
|
|
|
} // namespace Playerbot
|
|
```
|
|
|
|
## 2. Lock-Free Read Optimizations
|
|
|
|
### File: `src/modules/Playerbot/Economy/AuctionManagerOptimized.cpp`
|
|
```cpp
|
|
// Modifications to AuctionManager to reduce lock contention
|
|
|
|
// BEFORE (Current implementation):
|
|
ItemPriceData AuctionManager::GetItemPriceData(uint32 itemId) const
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(_mutex); // BLOCKS ALL READS!
|
|
auto it = _priceCache.find(itemId);
|
|
if (it != _priceCache.end())
|
|
return it->second;
|
|
return ItemPriceData();
|
|
}
|
|
|
|
// AFTER (Optimized with RCU pattern):
|
|
class AuctionManager : public BehaviorManager
|
|
{
|
|
private:
|
|
// Read-Copy-Update pattern for lock-free reads
|
|
struct PriceCacheRCU
|
|
{
|
|
std::unordered_map<uint32, ItemPriceData> data;
|
|
std::atomic<uint64> version{0};
|
|
};
|
|
|
|
std::atomic<PriceCacheRCU*> _priceCache{nullptr};
|
|
PriceCacheRCU _cacheBuffers[2]; // Double buffering
|
|
std::atomic<int> _activeBuffer{0};
|
|
std::mutex _writeMutex; // Only for writes (rare)
|
|
|
|
public:
|
|
// Lock-free read (99% of operations)
|
|
ItemPriceData GetItemPriceData(uint32 itemId) const
|
|
{
|
|
// No lock needed! Atomic pointer read
|
|
auto* cache = _priceCache.load(std::memory_order_acquire);
|
|
if (!cache)
|
|
return ItemPriceData();
|
|
|
|
auto it = cache->data.find(itemId);
|
|
if (it != cache->data.end())
|
|
return it->second; // Copy is cheap (POD structure)
|
|
|
|
return ItemPriceData();
|
|
}
|
|
|
|
// Write operations (rare - only during market scan)
|
|
void UpdatePriceData(uint32 itemId, ItemPriceData const& data)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_writeMutex); // Only writers lock
|
|
|
|
// Get inactive buffer
|
|
int inactive = (_activeBuffer.load() + 1) % 2;
|
|
auto& newCache = _cacheBuffers[inactive];
|
|
|
|
// Copy current data to inactive buffer
|
|
auto* current = _priceCache.load(std::memory_order_acquire);
|
|
if (current)
|
|
newCache.data = current->data;
|
|
|
|
// Update the data
|
|
newCache.data[itemId] = data;
|
|
newCache.version.fetch_add(1, std::memory_order_relaxed);
|
|
|
|
// Atomic swap to new cache
|
|
_priceCache.store(&newCache, std::memory_order_release);
|
|
_activeBuffer.store(inactive, std::memory_order_release);
|
|
}
|
|
};
|
|
```
|
|
|
|
## 3. Batched Manager Updates
|
|
|
|
### File: `src/modules/Playerbot/Core/Managers/BatchedUpdateSystem.h`
|
|
```cpp
|
|
#pragma once
|
|
|
|
#include "Define.h"
|
|
#include <vector>
|
|
#include <span>
|
|
#include <execution>
|
|
|
|
namespace Playerbot
|
|
{
|
|
|
|
/**
|
|
* @brief Batches manager updates across multiple bots for cache efficiency
|
|
*/
|
|
class BatchedUpdateSystem
|
|
{
|
|
public:
|
|
struct UpdateBatch
|
|
{
|
|
std::vector<BotAI*> bots;
|
|
uint32 diff;
|
|
uint32 updateCount{0};
|
|
};
|
|
|
|
/**
|
|
* Process bot updates in batches for better cache locality
|
|
*/
|
|
static void ProcessBotBatch(std::span<BotAI*> bots, uint32 diff)
|
|
{
|
|
constexpr size_t BATCH_SIZE = 16; // Optimal for cache line
|
|
|
|
// Group bots by update needs
|
|
std::vector<BotAI*> questUpdates;
|
|
std::vector<BotAI*> auctionUpdates;
|
|
std::vector<BotAI*> gatheringUpdates;
|
|
|
|
auto& throttler = UpdateThrottler::Instance();
|
|
uint32 currentTime = getMSTime();
|
|
|
|
// First pass: Determine which managers need updates
|
|
for (auto* bot : bots)
|
|
{
|
|
if (!bot)
|
|
continue;
|
|
|
|
if (throttler.ShouldUpdate("QuestManager", currentTime))
|
|
questUpdates.push_back(bot);
|
|
|
|
if (throttler.ShouldUpdate("AuctionManager", currentTime))
|
|
auctionUpdates.push_back(bot);
|
|
|
|
if (throttler.ShouldUpdate("GatheringManager", currentTime))
|
|
gatheringUpdates.push_back(bot);
|
|
}
|
|
|
|
// Batch process each manager type
|
|
BatchProcessQuests(questUpdates, diff);
|
|
BatchProcessAuctions(auctionUpdates, diff);
|
|
BatchProcessGathering(gatheringUpdates, diff);
|
|
}
|
|
|
|
private:
|
|
/**
|
|
* Batch process quest updates - single lock for all bots
|
|
*/
|
|
static void BatchProcessQuests(std::vector<BotAI*> const& bots, uint32 diff)
|
|
{
|
|
if (bots.empty())
|
|
return;
|
|
|
|
// Prepare batch data
|
|
struct QuestUpdate
|
|
{
|
|
Player* bot;
|
|
std::vector<uint32> questIds;
|
|
std::vector<QuestObjective> objectives;
|
|
};
|
|
|
|
std::vector<QuestUpdate> updates;
|
|
updates.reserve(bots.size());
|
|
|
|
// Collect all quest data first (no locks)
|
|
for (auto* botAI : bots)
|
|
{
|
|
auto* bot = botAI->GetBot();
|
|
if (!bot)
|
|
continue;
|
|
|
|
QuestUpdate update;
|
|
update.bot = bot;
|
|
|
|
// Collect active quests
|
|
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
|
|
{
|
|
uint32 questId = bot->GetQuestSlotQuestId(slot);
|
|
if (questId != 0)
|
|
update.questIds.push_back(questId);
|
|
}
|
|
|
|
updates.push_back(std::move(update));
|
|
}
|
|
|
|
// Process all quest updates in single operation
|
|
// This is where we'd have ONE lock instead of N locks
|
|
QuestManager::BatchUpdateQuests(updates, diff);
|
|
}
|
|
|
|
/**
|
|
* Batch process auction house operations
|
|
*/
|
|
static void BatchProcessAuctions(std::vector<BotAI*> const& bots, uint32 diff)
|
|
{
|
|
if (bots.empty())
|
|
return;
|
|
|
|
// Group by auction house (faction-specific)
|
|
std::unordered_map<uint32, std::vector<Player*>> ahGroups;
|
|
|
|
for (auto* botAI : bots)
|
|
{
|
|
auto* bot = botAI->GetBot();
|
|
if (!bot)
|
|
continue;
|
|
|
|
uint32 ahId = GetAuctionHouseIdForBot(bot);
|
|
ahGroups[ahId].push_back(bot);
|
|
}
|
|
|
|
// Process each auction house group together
|
|
for (auto const& [ahId, botGroup] : ahGroups)
|
|
{
|
|
// Single market scan for all bots in this AH
|
|
AuctionManager::BatchScanMarket(ahId, botGroup);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Batch process gathering node detection
|
|
*/
|
|
static void BatchProcessGathering(std::vector<BotAI*> const& bots, uint32 diff)
|
|
{
|
|
if (bots.empty())
|
|
return;
|
|
|
|
// Group by map/zone for spatial efficiency
|
|
std::unordered_map<uint32, std::vector<Player*>> zoneGroups;
|
|
|
|
for (auto* botAI : bots)
|
|
{
|
|
auto* bot = botAI->GetBot();
|
|
if (!bot)
|
|
continue;
|
|
|
|
uint32 zoneId = bot->GetZoneId();
|
|
zoneGroups[zoneId].push_back(bot);
|
|
}
|
|
|
|
// Process each zone group with shared node detection
|
|
for (auto const& [zoneId, botGroup] : zoneGroups)
|
|
{
|
|
// One spatial query for all bots in zone
|
|
GatheringManager::BatchDetectNodes(zoneId, botGroup);
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace Playerbot
|
|
```
|
|
|
|
## 4. Integration with BotAI
|
|
|
|
### Modifications to `src/modules/Playerbot/AI/BotAI.cpp`
|
|
|
|
```cpp
|
|
// REPLACE lines 1714-1798 with optimized version:
|
|
|
|
void BotAI::UpdateManagers(uint32 diff)
|
|
{
|
|
// OPTIMIZATION 1: Early exit if throttled
|
|
static uint32 s_lastBatchUpdate = 0;
|
|
uint32 currentTime = getMSTime();
|
|
|
|
// Batch updates every 50ms for non-critical managers
|
|
bool doBatchUpdate = (currentTime - s_lastBatchUpdate) >= 50;
|
|
|
|
// OPTIMIZATION 2: Use throttler to skip updates
|
|
auto& throttler = UpdateThrottler::Instance();
|
|
|
|
// Update throttler with current frame performance
|
|
static uint32 s_lastFrameTime = currentTime;
|
|
uint32 frameTime = currentTime - s_lastFrameTime;
|
|
s_lastFrameTime = currentTime;
|
|
throttler.UpdateSystemLoad(frameTime);
|
|
|
|
// OPTIMIZATION 3: Priority-based updates
|
|
// Critical managers (combat, movement) - always update via registry
|
|
if (_managerRegistry)
|
|
{
|
|
// This only updates managers marked as CRITICAL priority
|
|
uint32 managersUpdated = _managerRegistry->UpdatePriority(
|
|
diff,
|
|
UpdateThrottler::Priority::CRITICAL
|
|
);
|
|
|
|
if (managersUpdated > 0)
|
|
{
|
|
TC_LOG_TRACE("module.playerbot.managers",
|
|
"Bot {} updated {} critical managers",
|
|
_bot->GetName(), managersUpdated);
|
|
}
|
|
}
|
|
|
|
// OPTIMIZATION 4: Throttled updates for non-critical managers
|
|
if (doBatchUpdate)
|
|
{
|
|
s_lastBatchUpdate = currentTime;
|
|
|
|
// Collect bots for batch processing
|
|
static thread_local std::vector<BotAI*> s_batchBuffer;
|
|
s_batchBuffer.clear();
|
|
s_batchBuffer.push_back(this);
|
|
|
|
// Try to batch with nearby bots (same zone/group)
|
|
// This would be implemented via a bot registry
|
|
// For now, just process this bot
|
|
|
|
BatchedUpdateSystem::ProcessBotBatch(s_batchBuffer, diff);
|
|
}
|
|
|
|
// OPTIMIZATION 5: Background tasks at low frequency
|
|
if (throttler.ShouldUpdate("EquipmentManager", currentTime))
|
|
{
|
|
// Only check equipment every 10-30 seconds based on load
|
|
EquipmentManager::instance()->AutoEquipBestGear(_bot);
|
|
}
|
|
|
|
if (throttler.ShouldUpdate("ProfessionManager", currentTime))
|
|
{
|
|
// Only update professions every 15-60 seconds based on load
|
|
ProfessionManager::instance()->Update(_bot, diff);
|
|
}
|
|
}
|
|
```
|
|
|
|
## 5. Performance Monitoring
|
|
|
|
### File: `src/modules/Playerbot/Performance/UpdateProfiler.h`
|
|
```cpp
|
|
#pragma once
|
|
|
|
#include "Define.h"
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <array>
|
|
|
|
namespace Playerbot
|
|
{
|
|
|
|
/**
|
|
* @brief Lightweight profiler for bot update performance
|
|
*/
|
|
class UpdateProfiler
|
|
{
|
|
public:
|
|
struct ManagerStats
|
|
{
|
|
std::atomic<uint64> totalCalls{0};
|
|
std::atomic<uint64> totalTimeUs{0};
|
|
std::atomic<uint64> maxTimeUs{0};
|
|
std::atomic<uint64> skipCount{0};
|
|
|
|
float GetAvgTimeMs() const
|
|
{
|
|
uint64 calls = totalCalls.load(std::memory_order_relaxed);
|
|
if (calls == 0)
|
|
return 0.0f;
|
|
return static_cast<float>(totalTimeUs.load(std::memory_order_relaxed))
|
|
/ calls / 1000.0f;
|
|
}
|
|
};
|
|
|
|
private:
|
|
std::unordered_map<std::string, ManagerStats> _stats;
|
|
std::atomic<uint32> _activeBots{0};
|
|
std::atomic<uint64> _frameCount{0};
|
|
std::chrono::steady_clock::time_point _startTime;
|
|
|
|
public:
|
|
class ScopedTimer
|
|
{
|
|
UpdateProfiler& _profiler;
|
|
std::string _name;
|
|
std::chrono::steady_clock::time_point _start;
|
|
|
|
public:
|
|
ScopedTimer(UpdateProfiler& profiler, std::string const& name)
|
|
: _profiler(profiler), _name(name)
|
|
, _start(std::chrono::steady_clock::now())
|
|
{ }
|
|
|
|
~ScopedTimer()
|
|
{
|
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
|
|
std::chrono::steady_clock::now() - _start);
|
|
_profiler.RecordTime(_name, duration.count());
|
|
}
|
|
};
|
|
|
|
static UpdateProfiler& Instance()
|
|
{
|
|
static UpdateProfiler instance;
|
|
return instance;
|
|
}
|
|
|
|
void RecordTime(std::string const& manager, uint64 microseconds)
|
|
{
|
|
auto& stats = _stats[manager];
|
|
stats.totalCalls.fetch_add(1, std::memory_order_relaxed);
|
|
stats.totalTimeUs.fetch_add(microseconds, std::memory_order_relaxed);
|
|
|
|
// Update max time
|
|
uint64 currentMax = stats.maxTimeUs.load(std::memory_order_relaxed);
|
|
while (microseconds > currentMax &&
|
|
!stats.maxTimeUs.compare_exchange_weak(currentMax, microseconds))
|
|
{
|
|
// Retry
|
|
}
|
|
}
|
|
|
|
void RecordSkip(std::string const& manager)
|
|
{
|
|
_stats[manager].skipCount.fetch_add(1, std::memory_order_relaxed);
|
|
}
|
|
|
|
void PrintReport() const
|
|
{
|
|
TC_LOG_INFO("module.playerbot.perf", "=== Performance Report ===");
|
|
TC_LOG_INFO("module.playerbot.perf", "Active Bots: {}",
|
|
_activeBots.load(std::memory_order_relaxed));
|
|
|
|
for (auto const& [name, stats] : _stats)
|
|
{
|
|
TC_LOG_INFO("module.playerbot.perf",
|
|
"{}: Avg={:.2f}ms Max={:.2f}ms Calls={} Skips={}",
|
|
name,
|
|
stats.GetAvgTimeMs(),
|
|
stats.maxTimeUs.load() / 1000.0f,
|
|
stats.totalCalls.load(),
|
|
stats.skipCount.load());
|
|
}
|
|
}
|
|
};
|
|
|
|
// Macro for easy profiling
|
|
#define PROFILE_SCOPE(name) \
|
|
UpdateProfiler::ScopedTimer _timer##__LINE__(UpdateProfiler::Instance(), name)
|
|
|
|
} // namespace Playerbot
|
|
```
|
|
|
|
## Implementation Steps
|
|
|
|
1. **Add UpdateThrottler** (30 minutes)
|
|
- Create new header/source files
|
|
- Register all managers with appropriate priorities
|
|
- Integrate with ManagerRegistry
|
|
|
|
2. **Implement RCU Pattern** (2 hours)
|
|
- Modify AuctionManager::GetItemPriceData()
|
|
- Modify GatheringManager node detection
|
|
- Test lock-free reads
|
|
|
|
3. **Add Batched Updates** (2 hours)
|
|
- Create BatchedUpdateSystem
|
|
- Modify BotAI::UpdateManagers()
|
|
- Test with multiple bots
|
|
|
|
4. **Integrate Profiler** (1 hour)
|
|
- Add profiling points
|
|
- Create performance dashboard
|
|
- Set up alerts for slow operations
|
|
|
|
5. **Testing & Tuning** (2 hours)
|
|
- Load test with 100, 500, 1000 bots
|
|
- Adjust throttle thresholds
|
|
- Verify no functionality regression
|
|
|
|
## Expected Results
|
|
|
|
### Before Optimization:
|
|
- 100 bots: 50-100ms update time
|
|
- 600+ mutex operations per frame
|
|
- Linear scaling with bot count
|
|
|
|
### After Phase 1:
|
|
- 100 bots: 15-25ms update time (70% improvement)
|
|
- <200 mutex operations per frame (66% reduction)
|
|
- Sub-linear scaling with intelligent throttling
|
|
- 500 bots: 75-125ms (viable for testing)
|
|
|
|
## Configuration
|
|
|
|
Add to `worldserver.conf`:
|
|
```ini
|
|
# Playerbot Performance Optimization
|
|
Playerbot.Performance.UpdateThrottling = 1
|
|
Playerbot.Performance.BatchSize = 16
|
|
Playerbot.Performance.AdaptiveLoad = 1
|
|
Playerbot.Performance.ProfileUpdates = 1
|
|
|
|
# Manager Priority Configuration
|
|
Playerbot.Manager.Quest.Priority = HIGH
|
|
Playerbot.Manager.Trade.Priority = HIGH
|
|
Playerbot.Manager.Gathering.Priority = MEDIUM
|
|
Playerbot.Manager.Auction.Priority = LOW
|
|
Playerbot.Manager.Group.Priority = MEDIUM
|
|
Playerbot.Manager.Equipment.Priority = BACKGROUND
|
|
Playerbot.Manager.Profession.Priority = BACKGROUND
|
|
|
|
# Throttle Thresholds
|
|
Playerbot.Performance.LoadThreshold = 0.5
|
|
Playerbot.Performance.CriticalLoadThreshold = 0.9
|
|
Playerbot.Performance.MaxSkipCount = 10
|
|
```
|
|
|
|
## Monitoring
|
|
|
|
Use these commands to monitor performance:
|
|
```
|
|
.playerbot perf show - Show current performance stats
|
|
.playerbot perf reset - Reset performance counters
|
|
.playerbot throttle show - Show throttle status
|
|
.playerbot throttle set [manager] [priority] - Adjust manager priority
|
|
```
|
|
|
|
## Next Steps
|
|
|
|
After Phase 1 is stable and showing improvements, proceed to:
|
|
- **Phase 2**: Message-passing architecture
|
|
- **Phase 3**: Work-stealing task system
|
|
- **Phase 4**: Advanced SIMD optimizations
|
|
|
|
The immediate optimizations provide relief while we build the more complex lock-free architecture needed for 5000+ bots. |