Performance
Prerequisites: Weak tables and garbage collection
Lua is fast, but a few habits make a big difference in hot code — game
loops, request handlers, and large data processing. Measure first
with os.clock(); optimize only the parts that matter.
1. Use locals
Local variables live in registers; globals are table lookups. Cache frequently used globals and library functions in locals:
local function withGlobals(n)
local s = 0
for i = 1, n do s = s + math.sin(i) end
return s
end
local sin = math.sin
local function withLocals(n)
local s = 0
for i = 1, n do s = s + sin(i) end
return s
end
print(withGlobals(1000) == withLocals(1000))
true
local sin = math.sin at the top of a module is a common idiom for this
reason.
2. Build strings with table.concat
local function slowJoin(n)
local s = ""
for i = 1, n do s = s .. i .. "," end
return s
end
local function fastJoin(n)
local parts = {}
for i = 1, n do parts[i] = i end
return table.concat(parts, ",") .. ","
end
local t0 = os.clock(); local a = slowJoin(20000); local slow = os.clock() - t0
t0 = os.clock(); local b = fastJoin(20000); local fast = os.clock() - t0
print(a == b, slow > fast)
true true
Repeated .. copies the growing string each time (quadratic); collecting
pieces and joining once is linear.
3. Avoid creating garbage in loops
- Reuse tables instead of creating new ones every frame.
- Avoid closures created inside hot loops.
for i = 1, #tandipairsare both fast;pairsis slower and unordered.- Append with
t[#t + 1] = v— slightly faster thantable.insert.
4. Choose the right structure
Membership tests on a list are linear; on a set (t[key] = true) they're
constant time:
local list, set = {}, {}
for i = 1, 5000 do list[i] = "user" .. i; set["user" .. i] = true end
local function inList(name)
for _, v in ipairs(list) do if v == name then return true end end
return false
end
local t0 = os.clock()
for _ = 1, 2000 do inList("user4999") end
local listTime = os.clock() - t0
t0 = os.clock()
for _ = 1, 2000 do local _ = set["user4999"] end
local setTime = os.clock() - t0
print(listTime > setTime * 10)
true
5. Use LuaJIT when you can
For numeric and loop-heavy code, LuaJIT is often 10–50× faster than standard Lua, and its FFI calls C functions and uses C structs directly. It's what Neovim, LÖVE, and OpenResty run. See the next lesson for the language differences.