测量优于猜测

local start = os.clock()
-- 你的代码
print(os.clock() - start, "秒")

或用 LuaJIT 的 jit.v profile,luaperf 等。先测,再优化

1. local 比全局快

-- 慢
for i = 1, 1e7 do print(i) end

-- 快(消除 _G 查表)
local print = print
for i = 1, 1e7 do print(i) end

热路径里,把常用的全局 / 库函数缓存为 local

local insert = table.insert
local floor = math.floor
local format = string.format

2. 字符串拼接:用 table.concat

-- 慢:O(n²)
local s = ""
for i = 1, 10000 do s = s .. i end

-- 快:O(n)
local t = {}
for i = 1, 10000 do t[i] = tostring(i) end
local s = table.concat(t)

每次 .. 都造新字符串、复制旧的——大循环里几何级慢。

3. 表预分配(5.4+ 推荐 table.create)

-- 慢:表每次扩容
local t = {}
for i = 1, 1e6 do t[i] = i end

-- 快:知道大小先开
-- 标准 Lua 5.4 起没有 table.create;但实现内部如果 array 部分一开始就大,则不会反复 rehash
-- LuaJIT / Roblox 有 table.create(n, val):
local t = table.create(1000000)   -- LuaJIT 不一定,看版本;Roblox/Luau 有

标准 Lua 5.4 没 table.create——但对已知大小的循环,先填充就好

local t = {}
for i = 1, 1e6 do t[i] = 0 end   -- 后续覆盖更快

4. ipairs vs 数字 for

-- 慢
for i, v in ipairs(arr) do ... end

-- 快
local n = #arr
for i = 1, n do
    local v = arr[i]
    ...
end

ipairs 有函数调用 + table 查 metatable 开销。密集数值循环里for i = 1, n

5. 避免在循环里创建表

-- 慢:每次循环造新表
for i = 1, 1e6 do
    local p = {x = i, y = i*2}
    process(p)
end

-- 快:复用
local p = {}
for i = 1, 1e6 do
    p.x = i
    p.y = i*2
    process(p)
end

但只在 process 不"保留" p 时安全。

6. nil 检查

-- 慢:每次访问 obj.field
if obj.field then obj.field:do_something() end

-- 快:先存 local
local f = obj.field
if f then f:do_something() end

特别是嵌套属性 a.b.c.d——每个 . 都查表。

7. 别迷信 #table

local arr = {}
for i = 1, 1000000 do arr[i] = i end
print(#arr)    -- 标准实现是 O(log n);通常很快但不是 O(1)

如果在循环里反复读 #arr,提前存 local:

local n = #arr
for i = 1, n do ... end

8. unpack 与 select

table.unpack 在大表上慢——别用它做"列表传参":

-- 慢
process(table.unpack(args))

-- 改成接表的版本
process(args)

9. 选 LuaJIT 还是标准 Lua

标准 Lua 5.4 LuaJIT
速度 1x 3-50x(数值密集)
语法新特性 5.4 全部 卡在 5.1 + 少量借鉴
整数 / 位运算原生 ❌(用 bit 库)
FFI ✅(直接调 C 库)
嵌入二进制大小 几百 KB ~500 KB-1MB

数值密集计算 / 长跑应用 / 需要 FFI——LuaJIT。 要用 5.3+ 新语法 / 标准 Lua 一致行为——标准 Lua

10. GC 抖动

频繁创建短生命对象会触发 GC:

collectgarbage("count")    -- 当前 Lua 占用 (KB)
collectgarbage("stop")     -- 暂停 GC(在热路径前)
-- 关键计算
collectgarbage("restart")

慎用——长时间停 GC 会爆内存。一般通过减少分配(见第 5 条)来缓解 GC 压力比直接关 GC 安全。

→ 下一篇 LuaJIT 简介