我正在寻找一个关于如何优化LuaJIT 2的 Lua代码的好指南.它应该关注LJ2细节,比如如何检测正在编译哪些迹线,哪些不是,等等.
有什么指针吗?收集Lua ML帖子的链接可以作为答案(这里总结这些链接的奖励积分.)
更新:我已将标题文本从"性能分析"更改为"优化"指南,因为这更有意义.
Nec*_*lis 31
Mike最近为LuaJIT创建并发布了一个精彩的轻量级探测器,你可以在这里找到它.
维基已经在这个领域获得了更多的页面,尤其是这一页,其中详细介绍了原始答案中未提及的一些额外内容,并基于Mike 的邮件列表帖子.
LuaJIT最近推出了自己的wiki和邮件列表,并且有很多很多关于加速LuaJIT代码的宝石.
现在维基很薄(但总是寻找人们添加它),但是,最近添加的一个很棒的页面是NYI函数列表.NYI函数导致JIT挽救并回退到解释器,所以很明显应该尽可能避免在热路上使用NYI函数,尤其是在循环中.
邮件列表中感兴趣的一些主题:
并且重复说再说下来(因为它只是有帮助),-jv是性能调优的最佳工具,它也应该是您排除故障时的第一站.
我怀疑你实际上会发现很多,主要是因为LJ2仍处于测试阶段,因此大多数配置文件都是天真的,因为LJ2特定的东西,如跟踪记录器没有调试钩子.
从好的方面来说,新的FFI模块允许直接调用高分辨率的定时器(或者像VTune/CodeAnalyst这样的分析API),你可以用这种方式进行分析,但是更需要LJ2 JIT核心的扩展,这不应该太难,因为代码清晰和评论.
一个跟踪记录器命令行参数(取自此处):
-jv和-jdump命令是用Lua编写的扩展模块.它们主要用于调试JIT编译器本身.有关其选项和输出格式的说明,请阅读其源代码开头的注释块.它们可以在源代码发布的lib目录中找到,也可以安装在jit目录下.默认情况下,这是POSIX系统上的/usr/local/share/luajit-2.0.0-beta8/jit.
这意味着您可以使用命令中的模块代码构成LuaJIT 2的分析模块.
随着问题的更新,这变得更容易回答.所以让我们从源头开始,LuaJIT.org:
在手动优化代码之前,检查JIT的优化调优资源总是一个好主意:
汇编
在Running页面中,我们可以看到设置JIT参数的所有选项,为了优化,我们专注于-O选项.Mike立即告诉我们,启用所有优化对性能影响最小,因此请确保运行-O3(现在是默认值),因此这里唯一对我们有实际价值的选项是JIT和Trace阈值.
这些选项非常特定于您编写的代码,因此除了默认值之外没有通用的"最佳设置",但不用说,如果您的代码有很多循环,请尝试循环展开并计算执行时间(但如果您正在寻找冷启动性能,请在每次运行之间刷新缓存).
-jv在帮助避免导致JIT纾困的已知问题/"后备"方面也很有用.
除了FFI教程中的一些小花絮之外,该网站本身并没有提供更多关于如何编写更好或更优化的代码的信息:
功能缓存
在Lua中缓存函数是一个很好的性能助推器,但是在LuaJIT中关注不太重要,因为JIT本身就完成了大部分优化,重要的是要注意FFI C函数的缓存很糟糕,最好缓存它们所在的命名空间.
该页面的一个示例:
坏:
local funca, funcb = ffi.C.funcb, ffi.C.funcb -- Not helpful!
local function foo(x, n)
for i=1,n do funcb(funca(x, i), 1) end
end
Run Code Online (Sandbox Code Playgroud)
好:
local C = ffi.C -- Instead use this!
local function foo(x, n)
for i=1,n do C.funcb(C.funca(x, i), 1) end
end
Run Code Online (Sandbox Code Playgroud)
FFI性能问题
" 状态"部分详细说明了降低代码性能的各种构造和操作(主要是因为它们未编译,而是使用VM回退).
现在我们转到所有LuaJIT宝石的源头,Lua邮件列表:
避免C调用和NYI Lua调用循环:如果您希望LJ2跟踪器启动并提供有用的反馈,则需要避免使用跟踪编译器无法执行的NYI(尚未实现)函数或C调用.因此,如果您有任何可以导入到lua并且在循环中使用的小C调用,那么导入它们,最坏的情况是它们可能比C编译器实现慢6%,最好速度更快.
在ipairs上使用线性数组:根据Mike的说法,与其他方法相比,pair/next总是会慢一些(对于跟踪器,还有一个关于符号缓存的小问题).
避免使用嵌套循环:每个嵌套级别都需要额外的跟踪来进行跟踪,并且稍微不那么优化,特别是避免使用较低迭代的内部循环.
您可以使用0基阵列:Mike在这里说LuaJIT对基于0的阵列没有性能损失,这与标准Lua不同.
尽可能在最内部范围内声明本地人:没有真正的解释原因,但IIRC这与SSA生动性分析有关.还包含一些有关如何避免太多本地人的有趣信息(打破了活力分析).
避免许多微小的循环:这会弄乱展开的启发式方法,并会减慢代码速度.
较小的花絮:
分析工具可用于普通的Lua,但是,有一个新的项目与LuaJIT正式兼容(我怀疑它会考虑任何LuaJIT功能),luatrace.Lua wiki还有一个关于普通Lua的优化技巧的页面,这些需要在LuaJIT下测试它们的有效性(大多数这些优化可能已经在内部执行),但是,LuaJIT仍然使用默认的GC,这使它成为手动优化增益仍然很大的一个领域(直到Mike添加他提到的自定义GC在这里和那里做).
LuaJIT的源码包含一些摆弄JIT内部的设置,但是,这些需要进行大量测试才能根据特定代码进行调整,事实上,完全避免它们可能更好,特别是对于那些不是熟悉JIT的内部.
小智 7
不是你想要的,但我已经管理了一些jit.*跟踪设施的逆向工程.以下内容有点粗糙,不准确,可能会发生变化并且非常不完整.我很快就会开始在luatrace中使用它.这些函数用于几个-j库文件中.dump.lua可能是一个很好的起点.
jit.attach
您可以使用回调将回调附加到许多编译器事件jit.attach.可以调用回调:
使用jit.attach(callback, "event")和设置回调并清除相同的回调jit.attach(callback)
传递给回调的参数取决于报告的事件:
callback(func).func是刚刚录制的功能.callback(what, tr, func, pc, otr, oex)
what是跟踪事件的描述:"flush","start","stop","abort".适用于所有活动.tr是跟踪号码.不适用于同花顺.func是被追踪的功能.可用于启动和中止.pc是程序计数器 - 正在记录的函数的字节码编号(如果这是一个Lua函数).可用于启动和中止.otr start:父跟踪号,如果这是一个副跟踪,abort:abort code(integer)?oex start:父跟踪的退出编号,abort:abort reason(string) callback(tr, func, pc, depth).第一个参数与trace start相同.depth是当前字节码内联的深度.callback(tr, ex, ngpr, nfpr).
tr 是以前的跟踪号码ex 是退出号码ngpr并且nfpr是在出口处处于活动状态的通用和浮点寄存器的数量.jit.util.funcinfo(func,pc)
传递时func,pc从jit.attach回调中
jit.util.funcinfo返回一个有关函数的信息表,就像debug.getinfo.
表格的字段是:
linedefined:至于 debug.getinfolastlinedefined:至于 debug.getinfoparams:函数所需的参数数量stackslots:函数的局部变量使用的堆栈槽数upvalues:函数使用的upvalues数bytecodes:编译函数的字节码数gcconsts:??nconsts:??currentline:至于 debug.getinfoisvararg:如果函数是vararg函数`source:至于 debug.getinfoloc:描述源和当前行的字符串,如"<source>:<line>"ffid:函数的快速函数id(如果是1).在这种情况下,只有upvalues上方和addr下方有效addr:函数的地址(如果它不是Lua函数).如果它是C函数而不是快速函数,则只有upvalues上面有效| 归档时间: |
|
| 查看次数: |
12608 次 |
| 最近记录: |