perf: reduce CPU hotspots identified in profiler
- HBD.lua: Cache GetPlayerWorldPosition/GetPlayerZonePosition at 50ms
intervals instead of hammering GetPlayerMapPosition() every frame.
Invalidate cache on PLAYER_ENTERING_WORLD and ZONE_CHANGED_* events.
Expected ~97% reduction in C API position calls (45,570 -> ~1,200 / 10min).
- zoneDB.lua: Replace O(n) linear scan in GetAreaIdByUiMapId with an
O(1) reverse lookup cache (uiMapIdToAreaIdCache) built at Initialize().
Cache is kept in sync by ApplyCustomZones and name-match fallback now
caches its result so subsequent calls are also O(1).
- Tooltip.lua: Throttle GameTooltip OnUpdate hook to 100ms intervals
(was firing every frame at 60-144 Hz). Added _tooltipLastText cache
to avoid redundant GetText() + CountTooltip() calls when nothing changed.
- QuestieArrow.lua: Hoist _HasMissingCompletedFlag, _GetCompleteIconType,
_CollectFinisherSpawns, and _CollectObjective out of _CollectQuestTargets
to module-level functions. These were re-created as closures on every
UpdateNearestTargets call (1 Hz). Shared per-cycle context is published
via _arrow_* module upvalues to avoid closure capture overhead.
- QuestieLearnerComms.lua: Four micro-optimizations:
(1) Reduce ProcessQueues ticker 0.2s -> 0.5s (still 7x faster than
minChatInterval of 3.5s).
(2) Cache hidden channel ID at init; lazy refresh on disconnect.
(3) Drop LibDeflate compress level 9 -> 1 (fraction of CPU cost).
(4) O(1) messageCacheCount counter replaces O(n) pairs() size scan.
This commit is contained in:
@@ -65,7 +65,10 @@ local rateLimitQueue = {}
|
||||
|
||||
-- Deduplication & Quarantine
|
||||
local messageCache = {}
|
||||
local messageCacheCount = 0 -- O(1) counter; avoids pairs() scan on every message
|
||||
local incomingMessageQueue = {}
|
||||
-- Cached hidden channel ID (avoids GetChannelName every ProcessQueues tick)
|
||||
local _hiddenChannelId = 0
|
||||
|
||||
-- Sender Trust System
|
||||
local senderTrust = {}
|
||||
@@ -124,10 +127,11 @@ local function IsDuplicateMessage(serializedData)
|
||||
|
||||
if messageCache[hash] then return true end
|
||||
|
||||
local count = 0
|
||||
for _ in pairs(messageCache) do count = count + 1 end
|
||||
if count > 500 then
|
||||
-- O(1) size tracking via explicit counter
|
||||
messageCacheCount = messageCacheCount + 1
|
||||
if messageCacheCount > 500 then
|
||||
messageCache = {}
|
||||
messageCacheCount = 0
|
||||
end
|
||||
|
||||
messageCache[hash] = true
|
||||
@@ -149,9 +153,11 @@ function QuestieLearnerComms:Initialize()
|
||||
ChatFrame_RemoveChannel(DEFAULT_CHAT_FRAME, hiddenChannelName)
|
||||
DebugLog("CRITICAL", "Joined hidden data-sharing channel: " .. hiddenChannelName)
|
||||
end
|
||||
-- Cache for use in ProcessQueues (avoids GetChannelName every tick)
|
||||
_hiddenChannelId = GetChannelName(hiddenChannelName) or 0
|
||||
|
||||
-- Process incoming/outgoing queues
|
||||
QuestieCompat.C_Timer.NewTicker(0.2, function() _QuestieLearnerComms:ProcessQueues() end)
|
||||
QuestieCompat.C_Timer.NewTicker(0.5, function() _QuestieLearnerComms:ProcessQueues() end)
|
||||
|
||||
-- Start Reinforcement Loop (every 60 seconds)
|
||||
QuestieCompat.C_Timer.NewTicker(60, function() _QuestieLearnerComms:ProcessReinforcement() end)
|
||||
@@ -209,7 +215,7 @@ function QuestieLearnerComms:BroadcastLearnedData(op, entityType, entityId, data
|
||||
return
|
||||
end
|
||||
serialized = err
|
||||
local compressed = LibDeflate:CompressDeflate(serialized, {level = 9})
|
||||
local compressed = LibDeflate:CompressDeflate(serialized, {level = 1})
|
||||
local encoded = LibDeflate:EncodeForPrint(compressed)
|
||||
|
||||
-- 3. Broadcast (Token Bucket logic handled in QueueMessage)
|
||||
@@ -224,28 +230,30 @@ function _QuestieLearnerComms:ProcessQueues()
|
||||
-- 1. Refill Tokens
|
||||
local now = GetTime()
|
||||
local elapsed = now - lastTokenUpdate
|
||||
currentTokens = math.min(bucketCapacity, currentTokens + (elapsed * tokenRefillRate))
|
||||
currentTokens = math_min(bucketCapacity, currentTokens + (elapsed * tokenRefillRate))
|
||||
lastTokenUpdate = now
|
||||
|
||||
-- 2. Drain Outgoing Queue
|
||||
if table.getn(rateLimitQueue) > 0 and currentTokens >= 1 and (now - lastChatMessageTime) >= minChatInterval then
|
||||
local msg = table.remove(rateLimitQueue, 1)
|
||||
if table_getn(rateLimitQueue) > 0 and currentTokens >= 1 and (now - lastChatMessageTime) >= minChatInterval then
|
||||
local msg = table_remove(rateLimitQueue, 1)
|
||||
currentTokens = currentTokens - 1
|
||||
lastChatMessageTime = now
|
||||
|
||||
-- Send via Hidden Channel (Global reach)
|
||||
local channelId = GetChannelName(hiddenChannelName)
|
||||
if channelId > 0 then
|
||||
SendChatMessage(msg, "CHANNEL", nil, channelId)
|
||||
-- Use cached channel ID; refresh lazily if 0 (e.g. after disconnect)
|
||||
if _hiddenChannelId == 0 then
|
||||
_hiddenChannelId = GetChannelName(hiddenChannelName) or 0
|
||||
end
|
||||
DebugLog("DEVELOP", "Broadcasted message. Tokens left: " .. math.floor(currentTokens))
|
||||
if _hiddenChannelId > 0 then
|
||||
SendChatMessage(msg, "CHANNEL", nil, _hiddenChannelId)
|
||||
end
|
||||
DebugLog("DEVELOP", "Broadcasted message. Tokens left: " .. math_floor(currentTokens))
|
||||
end
|
||||
|
||||
-- 3. Process Incoming Queue (Combat Aware)
|
||||
local processCount = InCombatLockdown() and 2 or 6
|
||||
for i = 1, processCount do
|
||||
if table.getn(incomingMessageQueue) == 0 then break end
|
||||
local rawMsg = table.remove(incomingMessageQueue, 1)
|
||||
if table_getn(incomingMessageQueue) == 0 then break end
|
||||
local rawMsg = table_remove(incomingMessageQueue, 1)
|
||||
_QuestieLearnerComms:ProcessRawMessage(rawMsg.text, rawMsg.sender)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user