Thread Tools Display Modes
01-11-18, 02:12 PM   #1
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
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   #2
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,290
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, 04:51 PM   #3
Banknorris
A Chromatic Dragonspawn
 
Banknorris's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 153
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 that fractional reserve banking is a Ponzi scheme and that you won't believe it." - Mandrill
  Reply With Quote
01-14-18, 05:31 PM   #4
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,290
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   #5
lightspark
A Rage Talon Dragon Guard
 
lightspark's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2012
Posts: 341
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-16-18, 05:11 AM   #6
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,290
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, 06:04 AM   #7
lightspark
A Rage Talon Dragon Guard
 
lightspark's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2012
Posts: 341
Originally Posted by Resike View Post
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.
Lua Code:
  1. local oldBlizzFunc = BlizzFunc
  2.  
  3. function BlizzFunc(...)
  4.     -- your stuff here
  5.  
  6.     oldBlizzFunc(...)
  7. end

People either do this thingy or replace Blizz function entirely to prevent it from doing something, quite common for bag addons.

In this case, if you create an upvalue for a function before some other addon redefines it, you'll be calling original function.

It's not a proper hook, that's why I wrote it in quotes.
__________________

Last edited by lightspark : 01-16-18 at 06:09 AM.
  Reply With Quote
01-15-18, 08:07 AM   #8
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   #9
jeruku
A Cobalt Mageweaver
 
jeruku's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 223
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:13 AM   #10
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,290
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
01-16-18, 02:28 PM   #11
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
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.
No, I'm saying a new function is defined. You may verify using this:

Code:
/run local a = SetCVar hooksecurefunc("SetCVar", function(name, value) if name == "Sound_EnableMusic" then print(name, value) end end) print(a == SetCVar)
My code does not print anything on login for me, nor does this macro: (don't use together with the addon code as it will upvalue the already hooked function)

Code:
/run local a = SetCVar hooksecurefunc("SetCVar", function(name, value) if name == "Sound_EnableMusic" then print(name, value) end end) a("Sound_EnableMusic", 1)
And regardless, the point still stands for insecure hooks, and whether those are a good idea or not wasn't the point.
__________________
Grab your sword and fight the Horde!
  Reply With Quote
01-16-18, 06:52 PM   #12
Banknorris
A Chromatic Dragonspawn
 
Banknorris's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 153
Originally Posted by Resike View Post
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)
What would happen if in the last line of the second code you have used SetCVar("Sound_EnableMusic", 1) instead of _G.SetCVar("Sound_EnableMusic", 1)? Would it still print anything? If not then that is exactly the point: calling a local reference to a global function when the attribution of the local is made BEFORE you hook the global function will result in the hook (by that I mean the function with print(name,value)) not being called. But of course the hooked function (SetCVar) will run normally.

That was exactly what Semlar said. If in my addon I don't call _G.SetCVar but a local version of it and your addon (which wants to track SetCVar calls by hooksecurefunc'ing it) loads after mine, then you will not be able to see my SetCVar calls. Hence the advice to use _G.SetCVar (implicitally, by just not defining a local version of it) that is just a little bit slower but don't interfere with hooks.
__________________
"In this world nothing can be said to be certain, except that fractional reserve banking is a Ponzi scheme and that you won't believe it." - Mandrill

Last edited by Banknorris : 01-17-18 at 03:06 PM.
  Reply With Quote
01-17-18, 02:42 PM   #13
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,290
Originally Posted by Banknorris View Post
What would happen if in the last line of the second code you have used SetCVar("Sound_EnableMusic", 1) instead of _G.SetCVar("Sound_EnableMusic", 1)? Would it still print anything? If not then that is exactly the point, calling a local reference to a global function when the attribution of the local is made BEFORE you hook the global function will result in the hook (by that I mean the function with print(name,value) not being called. But of course the hooked function (SetCVar) will run normally.

That was exactly what Semlar said. If in my addon I don't call _G.SetCVar but a local version of it and your addon (which wants to track SetCVar calls by hooksecurefunc'ing it) loads after mine, then you will not be able to see my SetCVar calls. Hence the advice to use _G.SetCVar (implicitally, by just not defining a local version of it) that is just a little bit slower but don't interfere with hooks.
Still works. Actually it only works with the _G.

So this indeed does not seems to work, i might have been wrong about this, since i doubt it would be the post-patch changes:

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. SetCVar("Sound_EnableMusic", 1)

Last edited by Resike : 01-17-18 at 02:57 PM.
  Reply With Quote

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


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