cntkillme
|
  |
| Joined: 07 Apr 2008 |
| Total Posts: 44956 |
|
|
| 01 Jan 2016 05:55 AM |
Efficiency Misconceptions In order of most common to least common (from what I've seen, at least)
1) Declaring a variable outside a loop is more efficient than declaring it inside a loop because you don't have to create a variable. -> This is wrong. In terms of speed, they're the exact same because of how variables work internally in Lua. However declaring a variable outside the loop actually lets the last value leak which can hinder the GC from ever collecting garbage (depending on what you are doing) and may cause tiny memory leaks (which can build up, depending on what you are doing). Also, in my opinion, it looks cleaner to declare the variable inside the loop instead of outside the loop.
2) Using "next" is more efficient than using "pairs" in generic for loops. -> While this is technically true, the speed difference is insignificant (unless you have a loop which has a loop like this in it, and so on). Realistically, "pairs" is actually "next in disguise" and you are actually using "next" implicitly when you use "pairs" explicitly. For each of your generic for loops, using "pairs" only costs a single extra function call as opposed to using "next" and something this small should not be considered an optimization in a language like Lua. In addition, it would be better to use a numerical for loop if you can instead of a generic for loop in general if you can (and if you do it properly). In a common example, say iterating through an array (a table that has no non-integer keys and all keys are ordered from 1, and has no holes) like those returned from the GetChildren function and whatnot, it seems to be 2x more efficient to use a numerical for loop.
3) Using a loop is more efficient than explicitly doing something over and over in Lua. -> Loops make for shorter and better organized code, but that doesn't make it faster. The misconception in itself explains what is wrong with it in the first place; whether you use a loop or do something over and over, whatever you are repeatedly doing is done from Lua either way. This technically makes loops (and functions) slower, but the different is practically insignificant. Programming is really a balance of efficiency, readability, and maintainability; focusing on a single aspect is detrimental. Use functions and loops because reusing code makes for less required memory and typically makes your code more readable.
4) Shorter code is better code (this includes the misconception that 'semicolons' making your code slower). -> This is obviously false, anyone with a basic knowledge of any programming language can tell you this. This implies that "local myVar = 10" should be changed to "m=10" which actually yields less efficient code. In a more rational sense, some say that shorter variable names (this includes function names, parameter names, etc.) are more efficient. While technically they are faster during compile-time, the benefit is far more than just insignificant considering your code is going to be compiled once, not many many times and that a simple "read next character" call from the compiler is very very efficient. As I said before, readability and maintainability is important and the duration at which your code compiles is not important in any way considering the Lua compiler is quick and your code is going to be compiled before you can even notice anything has happened. Technically your global variable names are actually stored meaning they will have to be utilized at runtime, but in that case it'd be far, far more efficient to just make it a local variable (if you can). Computers are fast, the time at which you read a character from a buffer is virtually 0.
5) Using the "_G" table is inefficient. -> Using the "_G" table is just as efficient as indexing/writing to any other table (that is referenced to by a global variable). _G is just a regular Lua table, and in Roblox: all scripts on that same machine can access it (as opposed to it just referencing the global environment as it does in "Vanilla Lua"). Saying that accessing "_G" is inefficient is saying that accessing the "print" function or the "Instance" table (where Instance.new exists) is inefficient. It can actually be really efficient and save tons of memory if you reuse functions in many different scripts. Instead of creating many identical functions across separate scripts, you can create it in a single script and store a reference to it in "_G" in which you can read it from any script that wants to utilize it. Obviously ModuleScripts would be "better" in some cases (in that it's a much better way to be organized and keep "_G" clean if you need it for something else) but accessing "_G" is not "magically less efficient" than accessing any other global variable.
6) Testing a value instead of comparing a value is more efficient ("if not x" as opposed to "if x == false" or "if x == nil"). -> While technically it is more efficient, the benefit is more than negligible. In my opinion the first one is more readable but if the second one looks better to you, switching to the first one "because it's more efficient" is not something you should do. Also, these both do different things; in some cases you have to use the latter (checking a value to see if it's exactly nil).
7) This one is pretty Roblox specific: "while wait() do" is faster than "while true do" and calling "wait" somewhere within that loop. -> False, technically the second one is actually more efficient (but of course, almost negligible). In my opinion the second one can actually be less misleading as not everyone knows that the "wait" function returns something that is non-falsey and can make the first look like "magic" to beginners. Not only that, but if you want to yield after everything happens as the first method forces it to yield before, it's much easier to do that with the second method. Of course if the first one looks better to you, use it.
I may expand on this later on, also please feel free to ask questions and contribute if you can. And if you find any error (whether it's a typo or an actual inaccuracy) please let me know as soon as you can. You'll be given credit regardless of how you contribute. |
|
|
| Report Abuse |
|
|
tyzone
|
  |
| Joined: 16 Aug 2008 |
| Total Posts: 1726 |
|
|
| 01 Jan 2016 06:01 AM |
Great post overall. I don't have any misconceptions to offer, but do you know if there exists a similar document that gives you little tips and tricks about small syntax modifications that can change the performance significantly?
eww no pink shaggy |
|
|
| Report Abuse |
|
|
cntkillme
|
  |
| Joined: 07 Apr 2008 |
| Total Posts: 44956 |
|
|
| 01 Jan 2016 06:05 AM |
| Well things like that are very situation-specific (by syntax I assume you just mean in general) but typically the most known tips are: use local variables, don't "hog the resources" (do too many things at a given time) if you can avoid it (the "using wait wisely" Roblox blog post will help you understand what I mean), and planning out your code can help you out big time. |
|
|
| Report Abuse |
|
|
|
| 01 Jan 2016 06:20 AM |
| I never knew that people thought that was efficient. |
|
|
| Report Abuse |
|
|
DrHaximus
|
  |
| Joined: 22 Nov 2011 |
| Total Posts: 8410 |
|
|
| 01 Jan 2016 06:27 AM |
"technically the second one is actually more efficient (but of course, almost negligible)"
i don't know if 'negligible' is the right word here. the lack of sleeping after every iteration can massively improve performance (in terms of time) in the circumstances of: 1) heavy calculation, lots of iteration 2) the loop definitely breaks eventually |
|
|
| Report Abuse |
|
|
DrHaximus
|
  |
| Joined: 22 Nov 2011 |
| Total Posts: 8410 |
|
|
| 01 Jan 2016 06:27 AM |
you know what? i misread that. please ignore.
good post. |
|
|
| Report Abuse |
|
|
|
| 01 Jan 2016 06:59 AM |
'this includes the misconception that 'semicolons' making your code slower'
I've heard this as 'semicolons make your code faster because the compiler doesn't add them in'
I've never heard of semicolons making it slower.
Also isn't there some misconception on if methods are faster or not? |
|
|
| Report Abuse |
|
|
|
| 01 Jan 2016 07:07 AM |
| Lua is an interpreted language, not a compiled language. However the language it is made in(C) is a compiled language. |
|
|
| Report Abuse |
|
|
|
| 01 Jan 2016 07:28 AM |
Shorter code is better in terms of quantity of lines (Lines like that of python, which we use the template of in Lua, but serve no actual purpose other than it's easier to look at). Shorter code is better as it is executed faster (Again, in terms of lines), often but not always easier to understand, easier to edit, and quicker to type.
To clarify, what I mean by lines
while wait() do print("This is 3 lines") end
while wait() do print("This is 1 line, but the same as 3.") end |
|
|
| Report Abuse |
|
|
cntkillme
|
  |
| Joined: 07 Apr 2008 |
| Total Posts: 44956 |
|
|
| 01 Jan 2016 02:01 PM |
"I've heard this as 'semicolons make your code faster because the compiler doesn't add them in'" The compiler won't add in semi-colons, it uses whitespace to denote an end of a statement.
"Also isn't there some misconception on if methods are faster or not?" Well methods in themselves are supposed to be more efficient, but typically a method call requires an object lookup meaning it can become much less efficient if you have to do a lot of lookups.
"Lua is an interpreted language, not a compiled language. However the language it is made in(C) is a compiled language." Lua is interpreted, but it's compiled into an intermediate form called Lua bytecode because bytecode is far easier to interpret.
"Shorter code is better as it is executed faster" I don't understand what you mean "executed faster", because literally that means _faster to execute_ which is false. |
|
|
| Report Abuse |
|
|
|
| 01 Jan 2016 02:09 PM |
"Well methods in themselves are supposed to be more efficient, but typically a method call requires an object lookup meaning it can become much less efficient if you have to do a lot of lookups."
Could you give an example? I've been confused for quite some time on this concept. |
|
|
| Report Abuse |
|
|
cntkillme
|
  |
| Joined: 07 Apr 2008 |
| Total Posts: 44956 |
|
|
| 01 Jan 2016 02:17 PM |
Here's a simple example.
This: str:sub(x, y); Will be slightly less efficient than storing a reference to string.sub over the long run because you won't need to actually "get the sub function from the string" (a couple of implicit table lookups in this case). |
|
|
| Report Abuse |
|
|
cntkillme
|
  |
| Joined: 07 Apr 2008 |
| Total Posts: 44956 |
|
|
| 01 Jan 2016 02:19 PM |
Of course in this case, the speed difference will only really be visible if you do it PLENTY of times. In a case like this it would be better to use a method since it is easier to read (at least for me). But in a case of something like: a.b.c.d:e(f)
You will see a significant difference over the long run (you can test this by having a loop doing it a bunch of times and see how much time has passed). |
|
|
| Report Abuse |
|
|
|
| 01 Jan 2016 02:29 PM |
| What I meant by faster is that it can read 5 lines faster than 20 assuming the content is the same time length to execute |
|
|
| Report Abuse |
|
|
chimmihc
|
  |
| Joined: 01 Sep 2014 |
| Total Posts: 17143 |
|
|
| 01 Jan 2016 02:38 PM |
| All that is handled at compile time, the speed it actually executes is not effected. |
|
|
| Report Abuse |
|
|
rvox
|
  |
| Joined: 18 Feb 2011 |
| Total Posts: 5380 |
|
|
| 01 Jan 2016 02:42 PM |
line breaks count as one character, so the difference is negligible
also, a lot of the time, you need some character separator in between
what i said is probably right, dont quote me |
|
|
| Report Abuse |
|
|
cntkillme
|
  |
| Joined: 07 Apr 2008 |
| Total Posts: 44956 |
|
|
| 01 Jan 2016 02:45 PM |
"What I meant by faster is that it can read 5 lines faster than 20 assuming the content is the same time length to execute" If you read what I posted, you should have read that compile-time is not important, because it's done once and reading a single character is stupid fast. And as chimmihc said, all this affects is the length at which the code is compiled, but "affects" is actually not the correct term because of how insignificant it is. |
|
|
| Report Abuse |
|
|
cntkillme
|
  |
| Joined: 07 Apr 2008 |
| Total Posts: 44956 |
|
|
| 01 Jan 2016 02:45 PM |
And also what rvox said. A newline and a space are both a single character, so putting things on a single line will actually not affect it in any way. |
|
|
| Report Abuse |
|
|
|
| 02 Jan 2016 01:51 PM |
| Bump. This was interesting |
|
|
| Report Abuse |
|
|
mudkip99
|
  |
| Joined: 17 Jun 2008 |
| Total Posts: 3362 |
|
|
| 02 Jan 2016 01:58 PM |
| Even with excess whitespace, it's all removed at compiletime, anyway. With an interpreted language like Lua, it makes a bit more difference (almost 0, completely imperceptible to a human, but still some since it has to recompile it every time it runs), but you could have a ten line script and a ten million line script whose only differences are newlines/spaces, and you would see no difference in performance at runtime whatsoever. |
|
|
| Report Abuse |
|
|
|
| 02 Jan 2016 09:22 PM |
| Nice post, cntkillme! I really like and agree with most of the points, so please forgive that I have to point one thing out: while you made an explicit implication that semicolons don't make code slower, logic is to the contrary. String parsing functions are among the slowest, so it only makes sense that given more length it takes longer. Additionally, semicolons end statements, so Lua can get a bit confused (I won't go too much into detail but imagine putting two 'end's after one if without an error.) Other than that, not too much to complain about! I hope the community takes these into consideration before embarking upon meaningless quests for efficiency while sacrificing all other aspects of their code. |
|
|
| Report Abuse |
|
|
cntkillme
|
  |
| Joined: 07 Apr 2008 |
| Total Posts: 44956 |
|
|
| 02 Jan 2016 09:26 PM |
Even though I know you are trolling, others may not. "String parsing functions are among the slowest, so it only makes sense that given more length it takes longer. Additionally, semicolons end statements, so Lua can get a bit confused (I won't go too much into detail but imagine putting two 'end's after one if without an error.)" Obviously string parsing functions in C are not slow considering they are simple array lookups. Semicolons are optional, and 'putting two ends after one if' is going to result with an error regardless of semicolons. |
|
|
| Report Abuse |
|
|
|
| 02 Jan 2016 09:38 PM |
| Do the tips from "Lua Performance Tips" by Roberto Ierusalimschy help? |
|
|
| Report Abuse |
|
|
cntkillme
|
  |
| Joined: 07 Apr 2008 |
| Total Posts: 44956 |
|
|
| 02 Jan 2016 09:39 PM |
| Yes, that's a really good PDF. This thread is merely just to clear up some misinformation, not really inform how to write more efficient code so I'd recommend everyone to take a look at that if they care. |
|
|
| Report Abuse |
|
|
|
| 02 Jan 2016 09:46 PM |
I need your expert opinion. We know the following does show some performance improvement:
local mytable = {} local table_insert = table.insert
for i = 1, 100000 do table_insert(mytable, i) end
But what if we were to call it from a deeper scope?
local mytable = {} local table_insert = table.insert
for a = 1, 2 do for b = 1, 2 do ... table_insert(mytable, f) ... end end
It doesn't necessarily have to be loops; it could be enclosing functions. Does this slow down the lookup for the function on the stack? |
|
|
| Report Abuse |
|
|