CHUAN2 DEV ENGINE
996 正版授权研发中心 · 360 授权合作教学中心 · 抖音传奇直播合作授权 · 快手推广运营商授权
OFFICIAL LICENSED ACADEMY 查验官方授权证书 →
// 威海旷世互娱教学基地 · 技术文章
性能调优LuaJIT

【性能调优】LuaJIT trace abort 排查:-jv 输出解读与循环提速

2026-09-20 08:30 作者:996 技术组 3 阅读 996引擎Lua教程传奇脚本
完整课程入口:996 全套课程体系Lua 学习路径幂尔框架 mirs.cn

trace 编译为什么会放弃

LuaJIT 2.1 的 JIT 按"trace"编译:从热点循环回边出发,把线性执行的指令录制成机器码。录制途中遇到无法编译的操作,这条 trace 就被放弃(trace abort),退回解释执行——代码看起来一样,速度可能差 8 倍。排查 trace abort 是 LuaJIT 优化的一半工作量,另一半是数据结构布局。

用 -jv 看编译过程

LuaJIT 自带详细输出模式,直接观察每条 trace 的命运:

bash
luajit -jv hot_loop.lua

输出里每一行是一个事件:TRACE start 1 开始录制,TRACE flush 回收失效 trace,关键的是这一类:

lua
TRACE abort 1: NYI: FastFunc pairs

abort 后面跟着放弃原因。高频出现的原因按项目实测频率排序:NYI: FastFunc pairs(pairs 遍历不支持编译)、NYI: bytecode CALLSF(调用未编译的 C 函数或 FFI 边界)、loop unroll too large(循环体过大)、symbol not found on trace(FFI 符号问题)。

常见 abort 的改法

pairs 换数字 for。 实测同一份 10 万次键值读取,pairs 版本 41ms(全程解释执行),换成数组 + 数字 for 后 5ms(trace 编译成功),提速约 8 倍:

lua
-- abort 源:NYI pairs
for k, v in pairs(cfgList) do process(v) end

-- 可编译版本:数组 + 数字 for
for i = 1, #cfgList do process(cfgList[i]) end

NYI 函数出循环。 string.format、os.date 这类 NYI 函数放在循环外预算,循环内只做算术与表访问。减少分支发散。 trace 录制沿单一执行路径,循环内 if 分支两侧来回走会导致反复 abort,把罕见分支挪出热路径(或拆函数)即可稳定编译。

验证与量化

改完再跑 -jv,确认目标循环出现 TRACE 1 mcode 203K(编译成功并注明机器码大小)而不是 abort。性能用对照组量化:修复前后各跑 100 次取平均,用 os.clock() 打点。某 996 战斗预演模块按此流程处理后,单次预演从 118ms 降到 15ms。

两个补充工具:jit.off(fn) 可以对确认无法编译的函数显式关 JIT,省去反复尝试录制的开销;jit.flush() 在热更替换代码后清空全部 trace,避免旧机器码继续执行。优化顺序保持测量先行——先看 -jv 确认 abort,再改代码,改完复测,每一步都有数据支撑。

← 返回文章地图返回研学路径