I used this for testing:
lua Code:
local addon = ...
local UnitAuraCache = {} -- cached values are updated ten times per second and stored by unit+filter
local function UnitAura(unit, ...) -- 7.x to 8.x transience 2nd return value removed and only spellID argument is supported
local retTable
local index = select(1,...)
if type(index) == 'number' then -- now only spellID is accepted
retTable = {_G.UnitAura(unit, ...)}
if retTable[1] then table.insert(retTable,2,'') end
elseif type(index) == 'string' then -- lokup by spellName must be done on own by fetching all auras and then looking up by name
local index, filter = select(1,...)
local key = string.lower(filter or 'default')
index = string.lower(index) -- lookup by spell name
unit = string.lower(unit)
local cache = UnitAuraCache[unit] -- fetch unit aura cache
if cache and cache[key] and cache[key][index] and cache[key]['stamp'] == ("%.1f"):format(GetTime()) then -- cached values updated 10 times per second
retTable = cache[key][index]
else
if not UnitAuraCache[unit] then UnitAuraCache[unit] = {} end
if not UnitAuraCache[unit][key] then UnitAuraCache[unit][key] = {} else table.wipe(UnitAuraCache[unit][key]) end
cache = UnitAuraCache[unit][key]
local n = 1
local name
repeat
retTable = {_G.UnitAura(unit, n, filter)}
name = retTable[1]
if name then
table.insert(retTable,2,'')
cache[string.lower(name)] = retTable -- there is no table.copy
end
n = n + 1
until not name;
cache['stamp'] = ("%.1f"):format(GetTime())
if cache[index] then
retTable = cache[index]
else
retTable = {nil}
end
end
end
return unpack(retTable)
end
local function GetAura(unit, ...)
local index = ...
if type(index) == 'number' then
local name, texture, count, debuffType, duration, expirationTime, caster, isStealable,
nameplateShowPersonal, spellId, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
timeMod, effect1, effect2, effect3 = _G.UnitAura(unit, ...)
return name, '', texture, count, debuffType, duration, expirationTime, caster, isStealable,
nameplateShowPersonal, spellId, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
timeMod, effect1, effect2, effect3
elseif type(index) == 'string' then
local auraName, filter = ...
auraName = string.lower(auraName)
for i = 1, 100 do
local name, texture, count, debuffType, duration, expirationTime, caster, isStealable,
nameplateShowPersonal, spellId, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
timeMod, effect1, effect2, effect3 = _G.UnitAura(unit, i, filter)
if not name then return end
if string.lower(name) == auraName then
return name, '', texture, count, debuffType, duration, expirationTime, caster, isStealable,
nameplateShowPersonal, spellId, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
timeMod, effect1, effect2, effect3
end
end
end
end
local function TestCached()
local start = debugprofilestop()
for i = 1, 1e6 do
UnitAura('target', 'nothing')
end
print('Execution time (cached):', debugprofilestop() - start)
end
local function TestNotCached()
local start = debugprofilestop()
for i = 1, 1e6 do
GetAura('target', 'nothing')
end
print('Execution time (not cached):', debugprofilestop() - start)
end
_G['SLASH_' .. addon .. '1'] = '/testaura'
_G.SlashCmdList[addon] = function(msg)
msg = strlower(msg)
if msg == 'cached' then
TestCached()
else
TestNotCached()
end
end
The changes I made to Edik's code were:
1. removed the toc check (tested on beta only)
2. local index, _, filter = select(1,...) - > local index, filter = select(1,...)
3. cache[string.lower(name)] = table.copy(retTable) -> cache[string.lower(name)] = retTable
On average the cached version is 4 times slower than with no caching.
The test is not fair at all as it does not compare only the caching! The aim here is just to provide a rough estimation.
Beware that GetTime() is cached by the client. This has side effects on Edik's implementation as well.
The GetAura function can be optimized further.
Please do not take the results as a contra cache statement. Caching may prove useful, but this depends a lot on the use case and the implementation.