Quantcast About add-ons optimization - Page 3 - WoWInterface
Thread Tools Display Modes
09-05-13, 10:41 AM   #41
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,260
Originally Posted by Kagura View Post
Actually it won't be slower. But Phanx is right, I think the thread is derailing too much from "simple optimization" to getting every bit of performance you can possibly get.

p.s. If you are really curious, the test results with your suggestion is : here
Yeah but in this test you added another bunch of variable to lookup for the 1st and the 3rd version again. If you want them to test properly then you should have 1 line to set up the variables and 1 lookup line (Where you upvalue thoose variables). Which is already is in the 1st and the 3rd version.

Lua Code:
  1. function UnitAura(unitId, index)
  2. end
  3.  
  4. local time = 1000000
  5.  
  6. time = debugprofilestop()
  7. for j = 1, 1000000 do
  8.         local name, rank, icon, count, dispelType, duration, expires, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff, value1, value2, value3
  9.         for i = 1, 40 do
  10.                 name, rank, icon, count, dispelType, duration, expires, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff, value1, value2, value3 = UnitAura("player", i)
  11.         end
  12. end
  13. print(format("%.0f ms", debugprofilestop() - time))
  14.  
  15. time = debugprofilestop()
  16. for j = 1, 1000000 do
  17.         for i = 1, 40 do
  18.                 local name, rank, icon, count, dispelType, duration, expires, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff, value1, value2, value3 = UnitAura("player", i)
  19.                 name = 1
  20.                 rank = 1
  21.                 icon = 1
  22.                 count = 1
  23.                 dispelType = 1
  24.                 duration = 1
  25.                 expires = 1
  26.                 caster = 1
  27.                 isStealable = 1
  28.                 shouldConsolidate = 1
  29.                 spellID = 1
  30.                 canApplyAura = 1
  31.                 isBossDebuff = 1
  32.                 value1 = 1
  33.                 value2 = 1
  34.                 value3 = 1
  35.         end
  36. end
  37. print(format("%.0f ms", debugprofilestop() - time))
  38.  
  39. time = debugprofilestop()
  40. for j = 1, 1000000 do
  41.         for i = 1, 40 do
  42.                 local name, rank, icon, count, dispelType, duration, expires, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff, value1, value2, value3
  43.                 name, rank, icon, count, dispelType, duration, expires, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff, value1, value2, value3 = UnitAura("player", i)
  44.         end
  45. end
  46. print(format("%.0f ms", debugprofilestop() - time))

Tested it ingame:
7980 ms
8073 ms
8664 ms

Also it's pretty fast with integers only, much slower with strings/other variables. ~8500 ms

~600 ms gain on a million calls.

Last edited by Resike : 08-20-14 at 09:25 AM.
  Reply With Quote
01-09-18, 11:29 PM   #42
aallkkaa
A Cyclonian
 
aallkkaa's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2017
Posts: 46
Originally Posted by Phanx View Post
2. Upvalue any globals you're reading in frequently called functions, like OnUpdate, CLEU, UNIT_AURA, etc. Don't bother upvaluing globals you only read infrequently, such as in response to the user changing an option, the player leaving combat, etc.
Regarding the OnUpdate functions, I presume you mean globals you would call each time OnUpdate is run, right?
I mean, in the example bellow (from a non publshed little addon of mine), the function AlkaT_SpORCo_update() is called only once every 0.25 seconds, and it is in this function that I call two global functions. If I understood correctly, upvalueing these two functions would bring no benefit at all, right?
Lua Code:
  1. local AlkaT_UpdateInterval = 0.25;
  2. local AlkaT_timeSinceLastUpdate = 0;
  3. AlkaT_BMapTexts:SetScript("OnUpdate", function(self, AlkaT_elapsed)
  4.     AlkaT_timeSinceLastUpdate = AlkaT_timeSinceLastUpdate + AlkaT_elapsed;
  5.     if (AlkaT_timeSinceLastUpdate > AlkaT_UpdateInterval) then
  6.         AlkaT_SpORCo_update("player");
  7.         AlkaT_timeSinceLastUpdate = 0;
  8.     end
  9. end);
  Reply With Quote
01-10-18, 02:13 PM   #43
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,435
The point of upvaluing global functions called frequently is to reduce the amount of time it takes to get a return from those functions. A local is much faster to access than a global is.

Your script calls your AlkaT_SpORCo_update function and has to wait for it to finish before moving on to the next line in the script (AlkaT_timeSinceLastUpdate = 0). The faster it is completed, the faster it can move on. Your AlkaT_SpORCo_update function has to wait on the global functions that it calls before moving on in its code. And so on. Lua is single-threaded.
__________________
"You'd be surprised how many people violate this simple principle every day of their lives and try to fit square pegs into round holes, ignoring the clear reality that Things Are As They Are." -Benjamin Hoff, The Tao of Pooh

  Reply With Quote
01-10-18, 07:24 PM   #44
aallkkaa
A Cyclonian
 
aallkkaa's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2017
Posts: 46
Thank you for your reply, Seerah.
I was however already aware of what you said. Funny though, it made the thoughts clearer in my mind - I don't know how you did that, but thank you!

So, I guess my real question should rather be: What would be the cons, if any, of upvalueing those global functions?
I now recall that, on the thread which brought me to this one, there was a link to another thread, specifically on upvalueing, so I guess I'll have a look at that when I get some time. If I still have questions about this snippet afterwards, I'll post them back here.

P.s.: Your comment about the single-threaded nature of Lua wasn't exactly new to me, but, honestly, I had presumed it rather than properly looked into it. I'm not in the ITC industry (never was) but codeing has been a hobby of mine since I was about 15 (back then mainly in plain C), over 25 years now. And to be honest, the multi-threading technology was never something I fully understood (nor dived much into), codeing-wise.
The way I see it, even something like Lua coroutines is only a sequential pause-this-thread, then jump-to-another, then back-to-the-first (at the simplest case of only two "coroutines").
I can understand event-driven programming and have a shallow understanding of CPU IRQs as well, so I have a vague idea on how two (or more) cores CPUs work together.
But again, it's only a vague understanding of it, and as far I remember, I've never actually done any programming that wasn't, in my mind, "single-threaded".
I guess I could have started a new topic about this but I fear I would quickly loose the "thread" of it in the ensueing discussion, so I'm just dropping this here now.
  Reply With Quote
01-10-18, 10:39 PM   #45
myrroddin
A Molten Giant
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 865
This is a huge oversimplification, and is not 100% accurate, but it gets the point across:
Lua Code:
  1. local some_boy = "Harry"
  2. local some_girl = "Sally"
In Lua, since it is single threaded, it executes line 1, then line 2. A multithreaded language would execute both lines simultaneously, because at the bare minimum, multiple threads by nature start with two threads or executions per compute cycle.

If a language supported up to the new 36 threads from AMD and Intel, you could send 36 commands at the same time.

As to your question about cons, the two biggest about upvaluing are:
  • Initial startup time is a bit slower as your code has to look up the globals then store them in memory to variables. Unless you upvalue a crazy amount of globals, you won't notice this slow down on modern computers.
  • Your app, program, script, addon, whatever consumes more RAM because it is storing more things in memory. Again, with modern computers this isn't a big deal, but a player with 8GB of RAM while playing WoW doesn't have overhead to spare if he/she installs a hundred or so addons.
Not listed is a very minor con, in that unless you are looking up a global often, ie// during OnUpdate or rapid event firings, you don't gain much, if anything.
  Reply With Quote
01-11-18, 01:11 PM   #46
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,435
(Someone with 8GB of RAM would be fine )
__________________
"You'd be surprised how many people violate this simple principle every day of their lives and try to fit square pegs into round holes, ignoring the clear reality that Things Are As They Are." -Benjamin Hoff, The Tao of Pooh

  Reply With Quote
01-11-18, 02:12 PM   #47
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,026
Upvaluing globals is a micro-optimization that you shouldn't bother actively thinking about when you're coding; looking up a variable is very fast and any addon that would see an appreciable difference from storing a local copy to access is probably not going to be saved by this.

Write your program to do what you want it to do, then if something is impacting performance, clean it up. Anything that isn't an obvious performance gain should be deferred. Premature optimization can introduce unexpected behavior that can end up wasting a lot of your time tracking down and debugging.

Creating a local copy of a variable comes with the unintended (or intentional) consequence of preventing it from being replaced or hooked by other addons later in the loading process.

For example, let's say you want to monitor when another addon modifies a cvar so you know not to touch that cvar in the future, so you hook the global function SetCVar. When an addon calls SetCVar it also runs your function and everyone is happy.

But what if this addon creates a local reference to SetCVar before you hook it? Now when it calls SetCVar your function hook doesn't run, and everyone is sad because your addon ends up overwriting their addon's settings because it didn't see the change it made.

Additionally, lua actually has an internal limit of 60 upvalues. This isn't something you normally have to worry about, but you could conceivably run into it by redeclaring a large number of globals as local variables in your addon, and then trying to access them all in a function.
  Reply With Quote
01-11-18, 06:38 PM   #48
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,260
Originally Posted by semlar View Post
Upvaluing globals is a micro-optimization that you shouldn't bother actively thinking about when you're coding; looking up a variable is very fast and any addon that would see an appreciable difference from storing a local copy to access is probably not going to be saved by this.

Write your program to do what you want it to do, then if something is impacting performance, clean it up. Anything that isn't an obvious performance gain should be deferred. Premature optimization can introduce unexpected behavior that can end up wasting a lot of your time tracking down and debugging.

Creating a local copy of a variable comes with the unintended (or intentional) consequence of preventing it from being replaced or hooked by other addons later in the loading process.

For example, let's say you want to monitor when another addon modifies a cvar so you know not to touch that cvar in the future, so you hook the global function SetCVar. When an addon calls SetCVar it also runs your function and everyone is happy.

But what if this addon creates a local reference to SetCVar before you hook it? Now when it calls SetCVar your function hook doesn't run, and everyone is sad because your addon ends up overwriting their addon's settings because it didn't see the change it made.

Additionally, lua actually has an internal limit of 60 upvalues. This isn't something you normally have to worry about, but you could conceivably run into it by redeclaring a large number of globals as local variables in your addon, and then trying to access them all in a function.
I doubt upvaluing could cause this, since you are only creating a pointer to the same function that lives in a much smaller scope then the global one, nothing else.
And since both calls also goes up to another C function the time execute it would be a minuscule bit faster call, for the memory size of the function pointer.

Last edited by Resike : 01-11-18 at 06:44 PM.
  Reply With Quote
01-14-18, 12:29 AM   #49
aallkkaa
A Cyclonian
 
aallkkaa's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2017
Posts: 46
Thanks for all your replies.

@ myrroddin
Very nice explanation ons single-threaded vs multi-threaded languages. That was pretty much what I thought, it's the "huge oversimplification, and is not 100% accurate" part I'm sure would loose me...

On the cons of upvalueing, again, nice explanation, and makes perfect sense. But I was a bit surprised that 8GB would give so little leeway, having WoW running (mostly) alnoe.
Seerah seems to have a different view on this, so...


@ aemiar
Those are very interesting points.
Regarding that little addon of mine (prints player coordinates and speed on BattlefieldMinimap), it's pretty much done. I actually picked it up as example fpr its simplicity. But thanks for the hint, it is a good coding principle, I think.
And you pretty much answered it in regards to my original question: upvalueing, in my case, where the globals are called only once per 0.25 seconds isn't worth it; might be if they were called every time OnUpdate was called (only to check how long since last call presently). But, as it is, no point.
Originally Posted by semlar View Post
Additionally, lua actually has an internal limit of 60 upvalues.
I preseume this is per addon ...?


@ Resike
Originally Posted by Resike View Post
I doubt upvaluing could cause this, since you are only creating a pointer to the same function that lives in a much smaller scope then the global one, nothing else.
And since both calls also goes up to another C function the time execute it would be a minuscule bit faster call, for the memory size of the function pointer.
On this I have a couple questions.
I thought it was (NOT actual code):
Lua Code:
  1. global function DoSomething()
  2. call C function;
  3. end
and then, doing local DoSomething = _G[DoSomething] would bassically do:

A)
Lua Code:
  1. local function DoSomething()
  2. call C function;
  3. end

or...
B)

Lua Code:
  1. local function DoSomething()
  2. _G[DoSomething]();
  3. end

Which way does it work like?
  Reply With Quote
01-14-18, 07:52 AM   #50
Lombra
A Molten Giant
 
Lombra's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 554
A, I guess. The function never changes, you just get a new reference to it.
__________________
Grab your sword and fight the Horde!
  Reply With Quote
01-14-18, 08:17 AM   #51
MunkDev
An Onyxian Warder
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 380
Originally Posted by aallkkaa View Post
Which way does it work like?
It works more closely to A, but that's not the whole story. Look at this image:


Imagine that a is a reference to the function you want to upvalue. By accessing the function through a variable, you're essentially just retrieving the pointer to the memory where the function is stored (b). When you upvalue a function, you're copying the contents of a, that is to say the memory address to your function, but not the actual function. The function you end up calling is the same, whether global or local in your scope.
__________________
  Reply With Quote
01-14-18, 10:17 AM   #52
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,260
Originally Posted by MunkDev View Post
It works more closely to A, but that's not the whole story. Look at this image:


Imagine that a is a reference to the function you want to upvalue. By accessing the function through a variable, you're essentially just retrieving the pointer to the memory where the function is stored (b). When you upvalue a function, you're copying the contents of a, that is to say the memory address to your function, but not the actual function. The function you end up calling is the same, whether global or local in your scope.
Exactly, by upvalues you only save that _G function call nothing else. But you see how inefficient to call _G in an OnUpdate function, or any other function that gets triggered frequently.

Global call: Pointer to the global table _G -> lookup for the subtable _G.func -> get the table's pointer value -> call the C function from the returned pointer

(Since lua does not support multithreading calling _G and accessing it's subtable's value will take 2 cycles!)

Upvalued call: Pointer to the upvalued func function -> get the table's pointer value -> call the C function from the returned pointer
  Reply With Quote
01-14-18, 03:18 PM   #53
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,435
Originally Posted by aallkkaa View Post
Thanks for all your replies.

@ myrroddin
Very nice explanation ons single-threaded vs multi-threaded languages. That was pretty much what I thought, it's the "huge oversimplification, and is not 100% accurate" part I'm sure would loose me...

On the cons of upvalueing, again, nice explanation, and makes perfect sense. But I was a bit surprised that 8GB would give so little leeway, having WoW running (mostly) alnoe.
Seerah seems to have a different view on this, so...
https://us.battle.net/support/en/article/76459
__________________
"You'd be surprised how many people violate this simple principle every day of their lives and try to fit square pegs into round holes, ignoring the clear reality that Things Are As They Are." -Benjamin Hoff, The Tao of Pooh

  Reply With Quote
01-14-18, 04:51 PM   #54
Banknorris
A Flamescale Wyrmkin
 
Banknorris's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 146
Originally Posted by Resike View Post
I doubt upvaluing could cause this, since you are only creating a pointer to the same function that lives in a much smaller scope then the global one, nothing else.
And since both calls also goes up to another C function the time execute it would be a minuscule bit faster call, for the memory size of the function pointer.
Not sure if I am understanding correctly what you are saying but upvaluing will indeed make your hook on global functions not run if the upvalue is set before the hooking. When you hook you create another function, but the upvalued pointer will still point to the old version (pre hook). But maybe you are talking about something else.
__________________
"In this world nothing can be said to be certain, except death and taxes." - Benjamin Franklin
  Reply With Quote
01-14-18, 05:31 PM   #55
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,260
Originally Posted by Banknorris View Post
Not sure if I am understanding correctly what you are saying but upvaluing will indeed make your hook on global functions not run if the upvalue is set before the hooking. When you hook you create another function, but the upvalued pointer will still point to the old version (pre hook). But maybe you are talking about something else.
Except you are not hooking a function call, but a call for it's memory pointer's reference.

You can try it yourself:

Lua Code:
  1. hooksecurefunc("SetCVar", function(name, value)
  2.     if name == "Sound_EnableMusic" then
  3.         print(name, value)
  4.     end
  5. end)
  6.  
  7. local SetCVar = SetCVar
  8.  
  9. SetCVar("Sound_EnableMusic", 1)

You can do this in the other way around, it won't change a thing:

Lua Code:
  1. local SetCVar = SetCVar
  2.  
  3. hooksecurefunc("SetCVar", function(name, value)
  4.     if name == "Sound_EnableMusic" then
  5.         print(name, value)
  6.     end
  7. end)
  8.  
  9. _G.SetCVar("Sound_EnableMusic", 1)

Last edited by Resike : 01-14-18 at 05:43 PM.
  Reply With Quote
01-14-18, 10:10 PM   #56
lightspark
A Frostmaul Preserver
 
lightspark's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2012
Posts: 271
Originally Posted by Resike View Post
Except you are not hooking a function call, but a call for it's memory pointer's reference.
He prob meant "pre-hooking", when one pretty much re-defines a function. In that case, your upvalue will call the original.
__________________
  Reply With Quote
01-15-18, 08:07 AM   #57
Lombra
A Molten Giant
 
Lombra's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 554
Originally Posted by Resike View Post
Except you are not hooking a function call, but a call for it's memory pointer's reference.
Well of course if you explicitly do not use a pointer created before hooking it's going to work, but no, as I've recently learnt, hooksecurefunc actually replaces the function and there's no magic at all.

This does not print anything:
Code:
local SetCVar = SetCVar

hooksecurefunc("SetCVar", function(name, value)
    if name == "Sound_EnableMusic" then
        print(name, value)
    end
end)

SetCVar("Sound_EnableMusic", 1)
__________________
Grab your sword and fight the Horde!
  Reply With Quote
01-15-18, 04:09 PM   #58
jeruku
A Chromatic Dragonspawn
 
jeruku's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 159
Originally Posted by Lombra View Post
Well of course if you explicitly do not use a pointer created before hooking it's going to work, but no, as I've recently learnt, hooksecurefunc actually replaces the function and there's no magic at all.

This does not print anything:
Code:
local SetCVar = SetCVar

hooksecurefunc("SetCVar", function(name, value)
    if name == "Sound_EnableMusic" then
        print(name, value)
    end
end)

SetCVar("Sound_EnableMusic", 1)
That does work. as hooksecurefunc is more like a callback and is called immediately after the global function is called.

Lua Code:
  1. local globalIndexName = "SetCVar"
  2. hooksecurefunc(globalIndexName, function(...) end)

So globalIndexName is a _G[index] value. You can abuse this for most work that runs alongside the default WoW interface. However, of course, it only works for functions and most frame functions that are global.

You can find some more info on WoWProgramming.
__________________
"I have not failed, I simply found 10,000 ways that did not work." - Thomas Edison
  Reply With Quote
01-16-18, 05:11 AM   #59
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,260
Originally Posted by lightspark View Post
He prob meant "pre-hooking", when one pretty much re-defines a function. In that case, your upvalue will call the original.
What do you mean by pre-hooking? Of course if your hook is wrong it's not gonna work. Or if a hook does not actually hook than don't call it a hook.
  Reply With Quote
01-16-18, 05:13 AM   #60
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,260
Originally Posted by Lombra View Post
Well of course if you explicitly do not use a pointer created before hooking it's going to work, but no, as I've recently learnt, hooksecurefunc actually replaces the function and there's no magic at all.

This does not print anything:
Code:
local SetCVar = SetCVar

hooksecurefunc("SetCVar", function(name, value)
    if name == "Sound_EnableMusic" then
        print(name, value)
    end
end)

SetCVar("Sound_EnableMusic", 1)
It makes no sense, you are saying this hook actually killing the functionality of the SetCVar calls? It also works as a described and prints the value regardless how and where do you upvalue it.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » About add-ons optimization

Thread Tools
Display Modes

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