8/27/2019:
A lot has changed within the past 3 years. With the introduction of the new Lua Virtual Machine, some of these optimizations are now made by Roblox and are no longer needed. This includes:
- Direct table indexing (like
table.index
)-
table[index]
is unaffected, mostly
-
- Storing of global references (like
local sqrt = math.sqrt
)
Make sure you profile your code as well since changes you make could have no affect.
This tutorial is meant to cover a more basic form of optimizing of code that can be done to code without making it hard to read. This is NOT advanced level optimization, which is also a topic I canât easily do. I will leave it up to someone like AxisAngle to do that one.
So, let us start with two basic questions. 1. âWhat is optimization?â and 2. âWhy optimize?â. To start, optimizing is defined as to âmake the best or most effective use ofâ. Simply put, to make code run faster. This runs into the second question of why. The answer is pretty much in the definition, to make code run faster. Not everyone has a high end quad core i7 CPU and a NVIDIA GTX GPU, and in those cases a little bit of optimizing can make things run a lot smoother.
1. Use Local Variables
One of the most simple things you can do is add 6 characters to the beginning of your variables, and that is âlocal â. When you put local in front of a variable name, you are declaring a value as local, meaning it can only be used in the given scope (see example), but is faster than searching the âglobalâ table because it has a lot of other things, including native Lua variables and ROBLOX functions and userdata.
So, How do I use local variables? Here is an example using global variables:
This = 1 --Declares a global value of This
for i = 1, 5 do
This = This * i --References the global value This, multiplies it by i
--i can only be referenced in this scope because it is a local value
end
Here is an example of local values:
local This = 1 --Declares a local value of This
for i = 1, 5 do
This = This * i --References the local value This and multiplies it by i
end
Is that it? Actually, no. You can localize your functions too! How about some code to explain how.
--So letâs say you have this:
function This()
--Stuff
end
--Lets re-write it so it sets This as a global value:
This = function()
--Stuff
end
--See where this is going?
local This = function()
--Stuff
end
2. Donât Re-Do The Same Math When Needed
This isnât the best subtitle, so an analogy would help. Letâs say you needed to do math starting with needing sin(0). You start by entering it into your calculator and see it is 0. Then you needed to add a number to it. If you wanted to save time, you would just input the last value rather than re-type sin(0). Not the best example, so here is code to help.
Here is what happens if you repeat the same operation for to make a part rise at a constant rate.
for i = 1, 50 do
Part.CFrame = Part.CFrame * CFrame.new(0,1,0)
wait()
end
Now letâs write down what it does:
For 50 timesâŚ
- Get the Partâs CFrame
- Find the global value for CFrame
- Create the new userdata for CFrame.new(0,1,0)
- Multiply the Partâs CFrame from 1 to the CFrame from part 3
- Set the Partâs CFrame from 4
- Wait for 1/30th of a second
So, how can you optimize this?
local UpCFrame = CFrame.new(0,1,0)
for i = 1, 50 do
Part.CFrame = Part.CFrame * UpCFrame
wait()
end
Now what does it do?
A. Find the global value for CFrame
B. Create the new userdata for CFrame.new(0,1,0)
For 50 timesâŚ
- Get the Partâs CFrame
- Multiply the Partâs CFrame from 1 to the CFrame from part B
- Set the Partâs CFrame from 2
- Wait for 1/30th of a second
And just like that, you reduced 2 (really 3) steps that is done 50 times.
3. Reduce Indexing Of Tables
A while ago, AxisAngle talked about how he could cast thousands of rays per second with no performance hit, and this is a massive helper for that. There is a more complex explanation about that involving MetaTables, but letâs keep it simple.
for i = 1, 10 do
game.Workspace:FindPartOnRay(Ray.new(Vector3.new(0,0,0),Vector3.new(0,i,0))
end
This may seem simple, but it is a lot more complex once you see all the steps it requies.
For 10 timesâŚ
- Call __index (a MetaTable function) in game to get Workspace, and calling functions like this is fast but can stack if it called a lot to be slow.
- Call __index in Workspace to get FindPartOnRay. Now to get the Ray.
- Call __index for the global value new in Ray. Now to get the Vector3s.
- Call __index for the global value new in Vector3.
- Create the first vector from the function returned from 3 to create a Vector3 with the coordinates of 0,0,0.
- Call __index for the global value new in Vector3.
- Create the second vector from the function returned from 6 to create a Vector3 with the coordinates of 0,i,0.
- Pass those arguments into the function returned in step 3 with the Vector3s from 5 and 7 to get the Ray
- Pass those arguments into the function returned in step 8 into FIndPartOnRay
Long list, right? Now to use local values to lower the amount of times everything is indexed.
local Workspace = game.Workspace
local FindPartOnRay = Workspace.FindPartOnRay --This is how you index a function without calling it.
local Raynew,Vector3new = Ray.new,Vector3.new
local Center = Vector3new(0,0,0)
for i = 1, 10 do
--For a function indexed by :, you have to pass the table it is part of first before the rest of the arguments.
FindPartOnRay(Workspace,Raynew(Center,Vector3new(0,i,0))
end
A. Call __index in game to get Workspace
B. Call __index in Workspace to get FindPartOnRay
C. Call __index for the global value new in Ray.
D. Call __index for the global value new in Vector3.
E. Create a Vector with the function returned from D to create a blank Vector
For 10 timesâŚ
- Create the second vector from the function stored in D to create a Vector3 with the coordinates of 0,i,0.
- Reference the first vector from E and create the Ray from function stored in C
- Pass the table the FindPartOnRay function is from (Workspace, so A) into the stored function in B with the Ray from 3.
How Do I Test The Speed Of My Code
This may seem complex at first, but it is simple, store the tick() of the start, run the code a given amount of times in a for loop, then subtract the new tick() from the first.
local function TestFunction()
--Insert the code to tesst
end
local Start = tick() --Start time in seconds
for i = 1, 1000 do --For 1,000 times...
TestFunction() --Call TestFunction
End
print(tick()-Start) --End time in seconds
Doing these should make your code run faster. For example, when I did the 3rd optimization tip on the CameraScript, it went from 3%-5% in script performance to 0.5%-2%, and that is on Intel Core i7 4700MQ @ 3.25 GHz with TurboBoost. Optimization. Use it. Think about the people using toasters.
Bonus: With all of my tutorials, an image of the full tutorial for public is included.