A question on memory allocation/access
I'm wondering a couple of things about lua, some of them specific to WoW (I think), that have been bothering me for a while.
1. Take this example code: Code:
local function myFunction() Code:
local name 2. A similar example (assume this function is called multiple times as well): Code:
local function myFunction() Code:
local function onUpdate() Code:
local myNumber = GetSomeNumberFromServer() Code:
for i = 1, GetSomeNumberFromServer() do Thanks! |
You're lucky my browser restores the contents of form fields when I hit Back. Otherwise I'd be pretty pissed to have spent 20 minutes answering your questions only to be told that the thread did not exist after I hit Submit. :mad:
---------------------------------------------------------------------------------------------------------------------- (1) Declaring a variable inside a function vs. outside (a) Code:
local function myFunction() Code:
local name I'd recommend (a), not (b), but there is no significant difference, so do what you want. Long answer: In terms of total memory/CPU usage, these are functionally identical. Both will create a new value (returned by the GetSomeName call) and create a pointer to it named "name" each time the function is run. What is different is when that value will be garbage-collected. In version (a) the value is discarded as soon as the function's execution finishes. In version (b) the value is held in memory until the next time the function is executed, and then discarded when the "name" pointer is redirected to the new value. From a code-readability perspective, I personally think (a) is cleaner and easier to read, too. ---------------------------------------------------------------------------------------------------------------------- (2) Defining a function inside another function (a) Code:
local function myFunction() Code:
local function onUpdate() Version (a) is bad. Don't do that. Functions are expensive to create. Do (b) instead. Long answer: There are a couple of specific situations where (a) is okay. (i) You are only calling "myFunction" once the whole time your addon is loading and running. If you're calling it more than once, define the OnUpdate script handler function outside of "myFunction". If you're not sure whether you'll call it more than once, do (b) instead. (ii) You are using "myFunction" to construct a custom function based on other variables that are also defined inside "myFunction". Many addons using AceConfig options tables do this. Here's an example from Grid that creates some dynamic options: Code:
for class, name in pairs(classes) do ---------------------------------------------------------------------------------------------------------------------- (3) Function return values in for loop parameters (a) Code:
local myNumber = GetSomeNumberFromServer() Code:
for i = 1, GetSomeNumberFromServer() do In general, I'd recommend (b) since it is less code to read. The only reason to do (a) would be if you need to use the same value more than once: Code:
local n = GetNumRaidMembers() |
Thanks for those replies, a very interesting read.
I figured it'd be cleaner if I made an own topic rather than leaving a PM quote. I don't know of any browser these days that doesn't cache posts (maybe IE), but sorry. :p What do you think about (4)? |
I sometimes have to use Firefox or Chrome at work, and have lost entered form data on numerous occasions with both. It may depend partly on the type of form, I suppose.
As for #4, my lunch hour was over, and the boss was lurking. :p (4) Local copies of global variables _G is actually a special case in that it's a global variable that points to a table containing all global variables. Accessing it is a global lookup. In order from slowest to fastest:
As for the original question -- how a local copy of a global variable is faster -- consider this global table "x": Code:
x = { Code:
print(x["cat"]) Code:
local x = x You can't avoid the table lookup (unless you're only ever interested in one value in the table, in which case you should just do "local var = x["cat"]" at the top of your file and use "var" instead of looking up "x["cat"]" every time. What you can do is replace the slow global lookup with a fast local lookup. That's why you should always create local upvalues to global tables/functions if you're going to access them very often. If you're only calling the function or looking up a value in the table when the player opens an options window, or in response to events like PLAYER_ALIVE or OnClick, you don't really need to bother, because the speed increase is pointless. However, if you're calling or looking up inside an OnUpdate script, or in a function that's called very frequently such as an event handler for COMBAT_LOG_EVENT_UNFILTERED or UNIT_HEALTH, then all of those infinitesimal speed increases add up, especially in real-life situations where users are running 5, 10, 50, or 100 addons at the same time. |
I understand that global 'lookups' are slower than local ones - I'm interested as to why exactly though, considering they point to the same memory location in the case of tables. If I'm guessing correctly, are variables stored in hash tables and is the table for global variables slower to search than the one used for locals?
Thanks for the insightful post btw - will be saving this for future reference. |
The reference to the memory location is located SOMEWHERE within the global table (which also has to be looked up and found!) - you have to find that reference. Sure, it always points to the same memory location but unless you store that location in a variable you're continually looking it up (since Lua has no mechanism for saying "Oh, we already found this - I'll remember where it was!" unless you tell it to do so by using a variable.)
|
Think of the global table as a book, and you want to find your favorite passage to read it to your friend. You can either look over the shelves to find the book, and then flip through the book to find the passage -- or you can leave the book open on your coffee table open to the relevant page with the passsage highlighted.
|
Thanks Phanx for the very useful pointers
(This also made me remember the old WowAce Codings Tips page) |
Right - thanks for the replies.
One other thing I've come across - freeing up the memory allocated to a table. I've tried looking this up but I can't find anything on the actual memory usage. I need a table that can hold temporary tables for an addon I'm making. When I'm done with these tables, I want all of the memory used to be freed up again. Code:
local myTable = {} When I do this, however: Code:
local myTable = {} Am I correct in assuming that the only way to correctly 'empty' a table is to re-assign the variable to a new table? |
I could very well be wrong, and I expect to be corrected if I am, but in the first example you're setting the elements in the table to nil, but not doing anything to the actual table.
In the second table you're just getting rid of the table entirely, so my guess is that the extra 49kb you're seeing is the table itself (which at this point is just {} after setting all of those elements to nil) |
The way Lua stores tables, it reserves 2 arrays per table, one holds sequential indices and the other holds associative ones. When needed, Lua expands these arrays in powers of 2, but they never shrink back down when a value in the table is set to nil. This means when you build a table, then iterate through it to set everything to nil, the allocated space for the indices are still there. Usually, when you release a table, it stays in memory until the garbage collector comes to deallocate the memory used. You can force the garbage collector by calling collectgarbage("collect"). This will halt execution for the garbage collector to run, so it's not wise to use often or in moments of high CPU usage. Alternatively, Blizzard added the wipe() function to the API. I don't know how far it goes as far as cleaning up used memory. That might be something to test.
|
Quote:
Code:
t = {} Code:
t = { true, true, true } Code:
t = { a = true, b = true, c = true } |
What version of Lua are you talking about? As of now, WoW runs 5.1, the allocation for tables may be different in newer versions. Also, my explanation the subject wasn't memory size, but the number of elements allocated in the 2 arrays of a table.
http://www.wowpedia.org/Memory_usage. Quote:
http://www.wowpedia.org/Lua_object_memory_sizes Quote:
|
Quote:
|
All times are GMT -6. The time now is 07:02 PM. |
vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI