测量优于猜测
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 简介