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:
+35
-26
@@ -30,9 +30,12 @@ ZoneDB.private.dungeons = ZoneDB.private.dungeons or {}
|
||||
ZoneDB.private.dungeonLocations = ZoneDB.private.dungeonLocations or {}
|
||||
ZoneDB.private.dungeonParentZones = ZoneDB.private.dungeonParentZones or {}
|
||||
ZoneDB.private.subZoneToParentZone = ZoneDB.private.subZoneToParentZone or {}
|
||||
-- O(1) reverse lookup: uiMapId -> areaId, built from uiMapIdToAreaId at init.
|
||||
ZoneDB.private.uiMapIdToAreaIdCache = ZoneDB.private.uiMapIdToAreaIdCache or {}
|
||||
|
||||
local areaIdToUiMapId = ZoneDB.private.areaIdToUiMapId
|
||||
local uiMapIdToAreaId = ZoneDB.private.uiMapIdToAreaId
|
||||
local uiMapIdToAreaIdCache = ZoneDB.private.uiMapIdToAreaIdCache
|
||||
local dungeons = ZoneDB.private.dungeons
|
||||
local dungeonLocations = ZoneDB.private.dungeonLocations
|
||||
local dungeonParentZones = ZoneDB.private.dungeonParentZones
|
||||
@@ -59,6 +62,7 @@ function ZoneDB:Initialize()
|
||||
end
|
||||
|
||||
ZoneDB:ApplyCustomZones()
|
||||
_ZoneDB:BuildUiMapIdToAreaIdCache()
|
||||
_ZoneDB:GenerateParentZoneToStartingZoneTable()
|
||||
|
||||
-- Run tests if debug enabled
|
||||
@@ -75,6 +79,10 @@ function ZoneDB:ApplyCustomZones()
|
||||
-- skip non-numeric keys
|
||||
elseif _ZoneDB.areaIdToUiMapId[uiMapId] == nil then
|
||||
_ZoneDB.areaIdToUiMapId[uiMapId] = uiMapId
|
||||
-- Keep reverse cache in sync
|
||||
if uiMapIdToAreaIdCache[uiMapId] == nil then
|
||||
uiMapIdToAreaIdCache[uiMapId] = uiMapId
|
||||
end
|
||||
end
|
||||
|
||||
if data and type(data.parentMapID) == "number" and _ZoneDB.areaIdToUiMapId[data.parentMapID] == nil then
|
||||
@@ -83,6 +91,17 @@ function ZoneDB:ApplyCustomZones()
|
||||
end
|
||||
end
|
||||
|
||||
-- Builds the O(1) uiMapId -> areaId reverse cache from the uiMapIdToAreaId table.
|
||||
-- Called once at Initialize() after all zone data is loaded.
|
||||
function _ZoneDB:BuildUiMapIdToAreaIdCache()
|
||||
for areaUiMapId, areaId in next, uiMapIdToAreaId do
|
||||
-- First entry wins (matches the original scan behaviour)
|
||||
if uiMapIdToAreaIdCache[areaUiMapId] == nil then
|
||||
uiMapIdToAreaIdCache[areaUiMapId] = areaId
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _ZoneDB:GenerateParentZoneToStartingZoneTable()
|
||||
for startingZone, parentZone in next, subZoneToParentZone do
|
||||
parentZoneToSubZone[parentZone] = startingZone
|
||||
@@ -110,46 +129,36 @@ function ZoneDB:GetUiMapIdByAreaId(areaId)
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Use with care, kind of slow.
|
||||
---@param uiMapId UiMapId
|
||||
---@return AreaId
|
||||
function ZoneDB:GetAreaIdByUiMapId(uiMapId)
|
||||
--? Some areas have multiple areaIds, so we return the correct AreaId
|
||||
-- Fast path: override table
|
||||
if UiMapIdOverrides[uiMapId] then
|
||||
return UiMapIdOverrides[uiMapId]
|
||||
end
|
||||
|
||||
local foundId
|
||||
-- First we look for a direct match
|
||||
for AreaUiMapId, lAreaId in next, uiMapIdToAreaId do
|
||||
local areaId = lAreaId
|
||||
if (AreaUiMapId == uiMapId and not foundId) then
|
||||
foundId = areaId
|
||||
elseif AreaUiMapId == uiMapId and foundId ~= AreaUiMapId then
|
||||
-- If we find a second match that does not match the first
|
||||
-- Only print if debug is enabled.
|
||||
if Questie.db.profile.debugEnabled then
|
||||
Questie:Error("[ZoneDB:GetAreaIdByUiMapId] : ", "UiMapId", uiMapId, "has multiple AreaIds:", foundId, areaId)
|
||||
end
|
||||
end
|
||||
end
|
||||
if foundId then
|
||||
return foundId
|
||||
else
|
||||
-- As a last resort we try to match AreaId and UiMapId by name
|
||||
-- Fast path: O(1) pre-built reverse cache
|
||||
local cached = uiMapIdToAreaIdCache[uiMapId]
|
||||
if cached then return cached end
|
||||
|
||||
-- Slow fallback: name-based lookup (only for unmapped IDs, result is cached for next time)
|
||||
local mapInfo = C_Map.GetMapInfo(uiMapId)
|
||||
if mapInfo then
|
||||
for areaId in next, areaIdToUiMapId do
|
||||
local mapInfo = C_Map.GetMapInfo(uiMapId)
|
||||
local areaName = C_Map.GetAreaInfo(areaId)
|
||||
if mapInfo and mapInfo.name == areaName then
|
||||
if mapInfo.name == areaName then
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[ZoneDB:GetAreaIdByUiMapId] : ", "Found AreaId", areaName, ":", areaId, "for UiMapId", mapInfo.name, ":", uiMapId, "by name")
|
||||
-- Cache the result so we don't scan again
|
||||
uiMapIdToAreaIdCache[uiMapId] = areaId
|
||||
return areaId
|
||||
end
|
||||
end
|
||||
if Questie.db.profile.debugEnabled then
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "No AreaId found for UiMapId: " .. uiMapId .. ":" .. (C_Map.GetMapInfo(uiMapId) and C_Map.GetMapInfo(uiMapId).name or "nil"))
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
if Questie.db.profile.debugEnabled then
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "No AreaId found for UiMapId: " .. uiMapId .. ":" .. (mapInfo and mapInfo.name or "nil"))
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param areaId AreaId
|
||||
|
||||
Reference in New Issue
Block a user