Efficient LUA code?
Since I am currently refactoring my old addons, I was wondering wether there are some guides out there on how to write efficient LUA code (i.e. the basic do and don'ts).
|
I don't know about guides to efficient Lua code, however if you have an IRC client (they are easy to find and you may have one without even knowing it), then go over to:
irc.freenode.net and join the channel: #wowace Then you can talk code with the various crazies over at WoWAce. Otherwise find some good examples of efficient code (Tekkub's mods are a good example, then again I am just a novice coder myself), take em apart and see how the author has put it together. Another IRC channel is: irc://irc.freenode.net/WoWUIDev |
Don't focus on efficiency as it is absolutley not important if your addon uses a few kb more memory or a little bit more processor time. It is much more important that you write clean code.
Only if some parts of your addon are called very often (e.g. OnUpdate) you should try to avoid doing unnecessary things there. |
Quote:
|
Heres a couple of links:
http://lua-users.org/wiki/OptimisationCodingTips www.lua.org/gems/sample.pdf The main sources of performance issues I've noticed are lack of table reuse, another way to help is to make your Options only load into memory when needed. |
I disagree with you, Eggi. Efficiency is one of the most important things to me. If everybody would code addons the way you say it, without looking at efficiency, then that would probably mutate into some large pile of badly used memory and processing time. I have to agree with Torhal's post, too. If you want clean code, you can as well go for efficiency, since efficient code is usually clean.
|
Efficiency and clean code go hand in hand. You can write "beautiful" code that runs like ****, or amazingly fast code that is impossible to read. The real skill is doing both :P
|
The key to efficiency is realizing that 90% of efficiency is writing the right design. No one cares how well your bubble sort avoids creating tables...
|
Thanks for all the advice and the links. Some concrete concerns I was wondering about, is the use of string indices for table lookups and string comparisons, but apparently they are not as computational expensive in LUA than I expected them to be.
To name one concrete example, I have a buff module that listens for the UNIT_AURA event and then iterates over all buffs of the respective unit to see wether certain ones are active. For each one found, the number of its occurances is counted (e.g. count the number of Renews, Rejuvenations, etc.) and for certain buffs other actions are taken. Implementing the behaviour like this for i=1, MAX_BUFFS do buff_name = UnitBuff( unit, i ); if( not buff_name ) then break; end if( watched_buffs[ buff_name ] ) then found_buffs[ buff_name ] = found_buffs[ buff_name ] + 1; end if( buff_name == BUFF1 ) then elseif( buff_name == BUFF2 ) then elseif( buff_name == BUFF3 ) then end end apparently is efficient in LUA. |
Slight problem:
Code:
if( not buff_name ) then |
Quote:
|
if not UnitBuff("player", "Mark of the Wild") then....
|
Some days I'm partial to the Rowne Programming Elegance By Fiat method: "I touched that, therefore there is no more beautiful or efficient piece of code ever created. Behold! The laws of space and time warp to increase your fps despite my once-a-frame SetPoints! You may begin your praise now, I am ready to receive it."
Don't forget the many profiling tools Blizzard provides for us: http://www.wowwiki.com/World_of_Warc...ging_Functions http://www.wowwiki.com/World_of_Warc...ling_Functions If you're unsure whether one way is more efficient than another, try doing it 100,000 times and seeing how long it took. |
Ah Rowne... no one thought they wrote more perfect code than he did...
|
Now I have to see this code.
|
Concerning string/hash indexes vs. numerical indexes
Lua puts any numerically indexed table entries into an array as opposed to a hashed map (every lua table can have both array data and hashed data). So in theory, accessing numbered entries should be faster (adding items however could be another thing) Now, to contradict my previous paragraph... Some time ago I did some testing of numeric vs. hash indexes (in plain lua.exe) and I was not able to determine any difference at all. Any time differences (at some 100k iterations) were well in threshold margins. Some day I might also read into Lua's hashing function, but until then I'd say you try to have shorter indexes so the function has less characters to parse. Also Lua might fall back to hashing when you have a table like {[1] = ..., [10000] = ...} One thing to also note is: Avoid global tables at all costs. WoWs current global table is so overpopulated that you're almost always faster when you first do local x = sometableinglobal; and then do your operations on x. You're even faster if you completely skip the global table and stay within your addon's scope (have a table where everything is inside that you can access using self, or use local-to-file variables if possible). Also note that accessing anytable.number is always slower than accessing number without a table (100-1000 times or so) also note that your line Code:
buff_name = UnitBuff( unit, i ); Code:
local buff_name = UnitBuff( unit, i ); Assuming that you have more than three "elseif( buff_name == BUFF3 )"-like branches, you can further reduce your code - if you use a metatable that returns an empty function when an empty index is accessed, and do the stuff you'd now do inside your "if buff_name == xyz then" constructs in functions defined in that table - to: Code:
local i=1; Code:
local i=1; What both options have in common is that they're slower when the buff is not in the table and really fast when it is, because then the metatable methods are not used. The reason to do this is, because in an if/elseif/...../else construct lua has to parse every single branch in the worst case - half of them in the average case - and if you call the table it just does a hash of the string, goes there in the table (with native speed!) and calls that code (with some overhead due to function header). |
Quote:
Many things in my UI is my own code though, and I try to pollute the global namespace as little as possible, only adding one global, or in some cases none, and I know many other addon authors will do the same. You learn a lot of good coding guidelines from this community, this thread is a good example. I just think it is funny that the Blizzard coders aren't learning from this too, their use of globals is frightening, when we know how slow it is compared to locals. To test how many globals your interface has, use this code: Code:
/run c = 1; for _ in next, _G do c = c +1 end print(c) |
Quote:
The performance difference you observe is caused by only looking up "sometableinglobal" once, rather than every time you access it. This applies to *all* nested tables: if you're performing a large number of operations on some sub-table, use a local reference rather than looking it up in its parent table every time. |
Quote:
Yes, size does matter:banana: O(1) (for lookup) is only average case for your typical hash table implementation, and that only if the key-hashes follow the perfect distribution that the hash-function designer thought to be universal. Worst case is O(n). Reality will be somewhere in between, especially since best case O(1) = average case O(1) should give you a hint on why the O-notation is not always the perfect method to judge the performance of an algorithm. Quote:
I was telling from old data, the difference used to be much bigger back in 2.x. I reran my bench when writing this post and found that the performance of the global table has significantly improved (it's still not completely the same as an empty table though, about 2-5% off *cough*), I apologize for this. I just should have rerun the bench when writing my previous post. @Aezay: It used to be much bigger - so big actually that my client locked up and disconnected before finishing counting. After that I stopped trying... I think I read something about blizzard removing global variables in some patch notes though. Your count is one off btw. you need to start at zero ;) |
****, someone had to bring up O notation
*glazes over* |
All times are GMT -6. The time now is 10:32 PM. |
vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI