View Single Post
04-30-18, 03:34 PM   #8
Rainrider
A Firelord
AddOn Author - Click to view addons
Join Date: Nov 2008
Posts: 454
I used this for testing:
lua Code:
  1. local addon = ...
  2.  
  3. local UnitAuraCache = {} -- cached values are updated ten times per second and stored by unit+filter
  4. local function UnitAura(unit, ...) -- 7.x to 8.x transience 2nd return value removed and only spellID argument is supported
  5.     local retTable
  6.     local index = select(1,...)
  7.     if type(index) == 'number' then -- now only spellID is accepted
  8.         retTable = {_G.UnitAura(unit, ...)}
  9.         if retTable[1] then table.insert(retTable,2,'') end
  10.     elseif type(index) == 'string' then -- lokup by spellName must be done on own by fetching all auras and then looking up by name
  11.         local index, filter = select(1,...)
  12.         local key = string.lower(filter or 'default')
  13.         index = string.lower(index) -- lookup by spell name
  14.         unit = string.lower(unit)
  15.         local cache = UnitAuraCache[unit] -- fetch unit aura cache
  16.         if cache and cache[key] and cache[key][index] and cache[key]['stamp'] == ("%.1f"):format(GetTime()) then -- cached values updated 10 times per second
  17.             retTable = cache[key][index]
  18.         else
  19.             if not UnitAuraCache[unit] then UnitAuraCache[unit] = {} end
  20.             if not UnitAuraCache[unit][key] then UnitAuraCache[unit][key] = {} else table.wipe(UnitAuraCache[unit][key]) end
  21.             cache = UnitAuraCache[unit][key]
  22.             local n = 1
  23.             local name
  24.             repeat
  25.                 retTable = {_G.UnitAura(unit, n, filter)}
  26.                 name = retTable[1]
  27.                 if name then
  28.                     table.insert(retTable,2,'')
  29.                     cache[string.lower(name)] = retTable -- there is no table.copy
  30.                 end
  31.                 n = n + 1
  32.             until not name;
  33.             cache['stamp'] = ("%.1f"):format(GetTime())
  34.             if cache[index] then
  35.                 retTable = cache[index]
  36.             else
  37.                 retTable = {nil}
  38.             end
  39.         end
  40.     end
  41.     return unpack(retTable)
  42. end
  43.  
  44. local function GetAura(unit, ...)
  45.     local index = ...
  46.     if type(index) == 'number' then
  47.         local name, texture, count, debuffType, duration, expirationTime, caster, isStealable,
  48.             nameplateShowPersonal, spellId, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
  49.             timeMod, effect1, effect2, effect3 = _G.UnitAura(unit, ...)
  50.         return name, '', texture, count, debuffType, duration, expirationTime, caster, isStealable,
  51.             nameplateShowPersonal, spellId, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
  52.             timeMod, effect1, effect2, effect3
  53.     elseif type(index) == 'string' then
  54.         local auraName, filter = ...
  55.         auraName = string.lower(auraName)
  56.         for i = 1, 100 do
  57.             local name, texture, count, debuffType, duration, expirationTime, caster, isStealable,
  58.                 nameplateShowPersonal, spellId, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
  59.                 timeMod, effect1, effect2, effect3 = _G.UnitAura(unit, i, filter)
  60.             if not name then return end
  61.             if string.lower(name) == auraName then
  62.                 return name, '', texture, count, debuffType, duration, expirationTime, caster, isStealable,
  63.                     nameplateShowPersonal, spellId, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
  64.                     timeMod, effect1, effect2, effect3
  65.             end
  66.         end
  67.     end
  68. end
  69.  
  70. local function TestCached()
  71.     local start = debugprofilestop()
  72.  
  73.     for i = 1, 1e6 do
  74.         UnitAura('target', 'nothing')
  75.     end
  76.  
  77.     print('Execution time (cached):', debugprofilestop() - start)
  78. end
  79.  
  80. local function TestNotCached()
  81.     local start = debugprofilestop()
  82.  
  83.     for i = 1, 1e6 do
  84.         GetAura('target', 'nothing')
  85.     end
  86.  
  87.     print('Execution time (not cached):', debugprofilestop() - start)
  88. end
  89.  
  90. _G['SLASH_' .. addon .. '1'] = '/testaura'
  91. _G.SlashCmdList[addon] = function(msg)
  92.     msg = strlower(msg)
  93.     if msg == 'cached' then
  94.         TestCached()
  95.     else
  96.         TestNotCached()
  97.     end
  98. 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.
  Reply With Quote