Thread Tools Display Modes
06-11-10, 01:25 AM   #1
Crepusculu
A Deviate Faerie Dragon
 
Crepusculu's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2006
Posts: 16
How to improve bottleneck functions?

I am trying to improve the performance of my bottleneck functions that (ab)use the OnUpdate event. Since this event is called so frequently, I would like to optimize the functions.

My idea is to move my temporary variables outside of the function.

Here's a silly example:
Code:
myFrame:SetScript("OnUpdate",function(self,elapsedTime)
  for i=1,#array do
    local name = UnitName("raid"..i.."target")
    -- do important stuff
  end
end)
The concept is that moving the temporary variables on the outer scope of the function will still encapsulate the variables, but would recycle the existing variables when the function gets called again. This should reduce the garbage collection.

The fix would be:
Code:
do
  local i, name
  myFrame:SetScript("OnUpdate",function(self,elapsedTime)
    for i=1,#array do
      name = UnitName("raid"..i.."target")
      -- do important stuff
    end
  end)
end


Does this extended variable scope have any benefit, or should I be letting the garbage collector pick up every local variable? Do for-loop variables override preexisting variables and build new localized ones?

Thanks to any Lua-gurus out there!
  Reply With Quote
06-11-10, 02:02 AM   #2
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Variables created within the for loop are done so on the stack and are freed immediately after the end of execution without even being on the garbage collection facility's radar. You can create your variable at the top of the function so it's declared exactly once per run or leave it in the do block as you're doing now, but either way isn't going to significantly improve your CPU usage.

The only real optimization you can do in your OnUpdate script is to throttle how often it occurs - at the moment, if your FPS is 120 your function is executed 120 times in one second.

Here, I'm limiting the runs to once every quarter of a second.

Code:
do
  local last_update = 0

  myFrame:SetScript("OnUpdate", function(self, elapsedTime)
        last_update = last_update + elapsedTime

        if last_update < 0.25 then
                return
        end
        local name

        last_update = 0

        for i = 1, #array do
                name = UnitName("raid"..i.."target")
                -- do important stuff
        end
  end)
end
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
06-11-10, 04:59 AM   #3
Foxlit
A Warpwood Thunder Caller
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 91
Originally Posted by Crepusculu View Post
Do for-loop variables override preexisting variables and build new localized ones?
Yes, they do. Variables that appear in for i=... or for i,v in ... loop syntax are new and local to the loop. You cannot bypass this behavior.
__________________
... and you do get used to it, after a while.
  Reply With Quote
06-11-10, 09:49 AM   #4
Vrul
A Scalebane Royal Guard
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 404
I would make a local reference to UnitName as well.
  Reply With Quote
06-11-10, 10:34 AM   #5
lilsparky
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2007
Posts: 117
yeah, stuff the unitname into the array you're using (or have another array that contains that info). if you absolutely can't do that, at least prebuild your unitid instead of running that pointless concat function. ie, unitid[1] = "raid1target"
  Reply With Quote
06-11-10, 02:26 PM   #6
Crepusculu
A Deviate Faerie Dragon
 
Crepusculu's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2006
Posts: 16
Thanks for the replies!

Normally I would be imposing time restrictions as Torhal has suggested. This particular situation I am referencing is an exception, as i am dealing with time critical status changes that lack any triggered event.

Pre-concatenated strings is an excellent idea, since I am dealing with a small finite set. The return value on the unit is actually the focus of the bottleneck.


I'm not exactly sure how localizing UnitName would work. Does that mean doing something like
Code:
local UnitNameShortcut = UnitName
and using the local reference to bypass the lookup time on the global function? That sounds like it might help a bit.
  Reply With Quote
06-11-10, 03:23 PM   #7
dr_AllCOM3
A Cyclonian
 
dr_AllCOM3's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 40
local UnitName = UnitName
is fine.
  Reply With Quote
06-12-10, 03:25 PM   #8
Waverian
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Dec 2006
Posts: 188
Originally Posted by Crepusculu View Post
and using the local reference to bypass the lookup time on the global function? That sounds like it might help a bit.
It won't. The difference in execution time of local vs global functions is trivial. The difference between them for me, over a million iterations, is .077 second. If a user is running at 60 FPS with a completely unthrottled OnUpdate running constantly, you're saving .077 second of execution time every 4.6 hours.

There's no serious flaw in localizing globals that you're going to use a lot, but there's really no tangible benefit unless you're trying to prevent the function from being tampered with outside of your addon.
  Reply With Quote
06-12-10, 05:22 PM   #9
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Having a quick glance of the code the best optimizations your gonna get is by either keeping tab of the size of array so you don't have to iterate over the entire thing with #array or by keeping a copy of the concatenation "raid"..i.."target".
i.e.
lua Code:
  1. local raidtargets = setmetatable({}, {__index = function(t,i)
  2.     t[i] = "raid"..i.."target"
  3.     return t[i]
  4. end})
  5. myFrame:SetScript("OnUpdate",function(self,elapsedTime)
  6.     for i=1,array.size do --keep a tab of array.size
  7.         local name = UnitName(raidtargets[i])
  8.         -- do important stuff
  9.     end
  10. end)

These are probably the best optimizations your gonna get if your nutty about that stuff.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » How to improve bottleneck functions?


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off