Introduction
This is a compiled list of multiple performance quirks, suggestions and gotchas in programming that I have accumulated throughout the usage of the engine. This list ranges from small style choices to entire anti-patterns.
Use Table Literals
Table Literals are when you explicitly describe your keys/values inside the brackets as opposed to outside. This allows LuaU to activate some optimization.
-- Bad
local Table = {}
Table.Pizza = true
-- Good
local Table = {
Pizza = true
}
Optimize Unknown-Sized Arrays with table.create
If a Table Literal isn’t possible and you need to manually insert values into it, you should use table.create to pre-allocate memory so you only need to pay for allocation once.
-- Bad
local Numbers = {1, 2, 3, 4}
local Evens = {}
for _, v in Numbers do
if 2 % v == 0 then
table.insert(Evens, v)
end
end
-- Good
local Numbers = {1, 2, 3, 4}
local Evens = table.create(#Numbers)
Use Vector3 Constants inside CFrame.new when possible
This one should speak for itself, but it is actually faster to use Vector3 constants for the constructor instead of just writing the XYZ out.
-- Bad
local CF = CFrame.new(0, 10, 0)
-- Good
local CF = CFrame.new(Vector3.one * 10)
Avoid Lambda Functions when working with Callbacks
Sometimes a single lambda function won’t make any difference in terms of performance, however if you are constantly connecting and disconnecting functions to a callback or have two callbacks to an identical function, you are running the risk of creating duplicate functions in memory if it contains UpValues that are not constant (As seen the example below).
-- Bad
local UpValue = 1
EventA:Connect(function(...)
UpValue = 2
end)
EventB:Connect(function(...)
UpValue = 2
end)
-- Good
local UpValue = 1
local function OnEvent(...)
UpValue = 2
end
EventA:Connect(OnEvent)
EventB:Connect(OnEvent)
This can also be extended to other fields such as coroutines and the task library, where you have the freedom to actually pass arguments into the pre-defined function.
local function Add(A, B)
print(A + B)
end
task.spawn(Add, 1, 2)
Cache Re-Usable Objects
Too often do I see amateur programmers create new Tweens
, AnimationTracks
and other objects meant to be reused in coding. You should keep these objects as constants instead of repeatedly creating and destroying them.
-- Bad
local function Disappear()
local Tween = TweenService:Create(Part, TweenInfo.new(1), {Transparency = 1})
Tween:Play()
end
-- Good
local TWEEN_INFO = TweenInfo.new(1)
local TWEEN_DISAPPEAR = TweenService:Create(Part, TWEEN_INFO, {Transparency = 1})
local function Disappear(Size)
TWEEN_DISAPPEAR:Play()
end
Avoid unnecessary interaction with the DataModel
The impact of this can range from small performance to colossal change. You should try to keep references to instances from the DataModel instead of continuously traversing for them.
-- Bad
local Model = ...
Model.Part.BrickColor = BrickColor.Red()
Model.Part.Anchored = false
-- Good
local Model = ...
local Part = Model.Part
Part.BrickColor = BrickColor.Red()
Part.Anchored = false