观察者:Lua JIT详解(观察者the one)
cac55 2024-10-13 01:16 29 浏览 0 评论
自从 OpenResty 1.5.8.1 版本之后,默认捆绑的 Lua 解释器就被替换成了 LuaJIT,而不再是标准 Lua。单从名字上,我们就可以直接看到这个新的解释器多了一个 JIT ,接下来我们就一起来聊聊 JIT 。
先看一下 LuaJIT 官方的解释:LuaJIT is a Just-In-Time Compilerfor the Lua programming language。
LuaJIT 的运行时环境包括一个用手写汇编实现的 Lua 解释器和一个可以直接生成机器代码的JIT 编译器。
Lua 代码在被执行之前总是会先被 lfn 成 LuaJIT 自己定义的字节码(Byte Code) 。
一开始的时候,Lua 字节码总是被 LuaJIT 的解释器解释执行。LuaJIT 的解释器会在执行字节码时同时记录一些运行时的统计信息,比如每个 Lua 函数调用入口的实际运行次数,还有每个 Lua 循环的实际执行次数。当这些次数超过某个预设的阈值时,便认为对应的 Lua 函数入口或者对应的 Lua 循环足够的“热”,这时便会触发 JIT 编译器开始工作。
JIT 编译器会从热函数的入口或者热循环的某个位置开始尝试编译对应的 Lua 代码路径。编译的过程是把 LuaJIT 字节码先转换成LuaJIT 自己定义的中间码(IR) ,然后再生成针对目标体系结构的机器码(比如 x86_64 指令组成的机器码) 。
如果当前 Lua 代码路径上的所有的操作都可以被 JIT 编译器顺利编译,则这条编译过的代码路径便被称为一个“trace”,在物理上对应一个 trace 类型的 GC 对象(即参与 Lua GC 的对象) 。
你可以通过 ngx-lj-gc-objs 工具看到指定的 Nginx worker 进程里所有 trace 对象的一些基本的统计信息,
比如下面这一行 ngx-lj-gc-objs 工具的输出
102 trace objects: max=928, avg=337, min=160, sum=34468 (in bytes)
则表明当前进程内的 LuaJIT VM 里一共有 102 个 trace 类型的 GC 对 象,其中最小的 trace占用 160 个字节,最大的占用 928 个字节,平均大小是 337 字节,而所有 trace 的总大小是34468 个字节。
LuaJIT 的 JIT 编译器的实现目前还不完整,有一些基本原语它还无法编译,比如 pairs() 函数、unpack() 函数、string.match() 函数、基于 lua_CFunction 实现的 Lua C 模块、FNEW字节码,等等。所以当 JIT 编译器在当前代码路径上遇到了它不支持的操作,便会立即终止当前的 trace 编译过程(这被称为 trace abort) ,而重新退回到解释器模式。
JIT 编译器不支持的原语被称为 NYI(Not Yet Implemented) 原语。
所谓“让更多的 Lua 代码被 JIT 编译”,其实就是帮助更多的 Lua 代码路径能为 JIT 编译器所接受。这一般通过两种途径来实现:
1. 调整对应的 Lua 代码,避免使用 NYI 原语。
2. 增强 JIT 编译器,让越来越多的 NYI 原语能够被编译。
对于第 2 种方式,春哥一直在推动公司(CloudFlare) 赞助 Mike Pall 的开发工作。不过有些原语因为本身的代价过高,而永远不会被编译,比如基于经典的 lua_CFunction 方式实现的Lua C 模块(所以需要尽量通过 LuaJIT 的 FFI 来调用 C) 。
而对于第 1 种方法,我们如何才能知道具体是哪一行 Lua 代码上的哪一个 NYI 原语终止了trace 编译呢?答案很简单。就是使用 LuaJIT 安装自带的 jit.v 和 jit.dump 这两个 Lua 模块。
这两个 Lua 模块会打印出 JIT 编译器工作的细节过程。
在 Nginx 的上下文中,我们可以在 nginx.conf 文件中的 http {} 配置块中添加下面这一段:
init_by_lua_block {
local verbose = false
if verbose then
local dump = require "jit.dump"
dump.on(nil, "/tmp/jit.log")
else
local v = require "jit.v"
v.on("/tmp/jit.log")
end
require "resty.core"
}
那一行 require "resty.core" 倒并不是必需的,放在那里的主要目的是为了尽量避免使用ngx_lua 模块自己的基于 lua_CFunction 的 Lua API,减少 NYI 原语。
在上面这段 Lua 代码中,当 verbose 变量为 false 时(默认就为 false 哈) ,我们使用 jit.v 模块打印出比较简略的流水信息到 /tmp/jit.log 文件中;而当 verbose 变量为 true 时,我们则使用 jit.dump 模块打印所有的细节信息,包括每个 trace 内部的字节码、IR 码和最终生成的机器指令。
这里我们主要以 jit.v 模块为例。在启动 Nginx 之后,应当使用 ab 和 weighttp 这样的工具对相应的服务接口进行预热,以触发 LuaJIT 的 JIT 编译器开始工作(还记得刚才我们说的“热函数”和“热循环”吗?) 。预热过程一般不用太久,跑个二三百个请求足矣。当然,压更多的请求也没关系。完事后,我们就可以检查 /tmp/jit.log 文件里面的输出了。
jit.v 模块的输出里如果有类似下面这种带编号的 TRACE 行,则指示成功编译了的 trace 对象,例如
[TRACE 6 shdict.lua:126 return]
这个 trace 对象编号为 6,对应的 Lua 代码路径是从 shdict.lua 文件的第 126 行开始的。
下面这样的也是成功编译了的 trace:
[TRACE 16 (15/1) waf-core.lua:419 -> 15]
这个 trace 编号为 16,是从 waf-core.lua 文件的第 419 行开始的,同时它和编号为 15 的trace 联接了起来。
而下面这个例子则是被中断的 trace:
[TRACE --- waf-core.lua:455 -- NYI: FastFunc pairs at waf-core.lua:458]
上面这一行是说,这个 trace 是从 waf-core.lua 文件的第 455 行开始编译的,但当编译到waf-core.lua 文件的第 458 行时,遇到了一个 NYI 原语编译不了,即 pairs() 这个内建函数,于是当前的 trace 编译过程被迫终止了。
类似的例子还有下面这些:
[TRACE --- exit.lua:27 -- NYI: FastFunc coroutine.yield at waf-core.lua:439]
[TRACE --- waf.lua:321 -- NYI: bytecode 51 at raven.lua:107]
上面第二行是因为操作码 51 的 LuaJIT 字节码也是 NYI 原语,编译不了。
那么我们如何知道 51 字节码究竟是啥呢?我们可以用 nginx-devel-utils 项目中的 ljbc.lua 脚本来取得 51 号字节码的名字:
$ /usr/local/openresty/luajit/bin/luajit-2.1.0-alpha ljbc.lua 51
opcode 51:
FNEW
我们看到原来是用来(动态) 创建 Lua 函数的 FNEW 字节码。ljbc.lua 脚本的位置是https://github.com/agentzh/nginx-devel-utils/blob/master/ljbc.lua
非常简单的一个脚本,就几行 Lua 代码。
这里需要提醒的是,不同版本的 LuaJIT 的字节码可能是不相同的,所以一定要使用和你的Nginx 链接的同一个 LuaJIT 来运行这个 ljbc.lua 工具,否则有可能会得到错误的结果。
我们实际做个对比实验,看看 JIT 带来的好处:
? cat test.lua
local s = [[aaaaaabbbbbbbcccccccccccddddddddddddeeeeeeeeeeeee
fffffffffffffffffggggggggggggggaaaaaaaaaaabbbbbbbbbbbbbb
ccccccccccclllll]]
for i=1,10000 do
for j=1,10000 do
string.find(s, "ll", 1, true)
end
end
? time luajit test.lua
5.19s user
0.03s system
96% cpu
5.392 total
? time lua test.lua
9.20s user
0.02s system
99% cpu
9.270 total
本例子可以看到效率相差大约 9.2/5.19 ≈ 1.77 倍,换句话说标准 Lua 需要 177% 的时间才能完成同样的工作。估计大家觉得这个还不过瘾,再看下面示例代码:
文件 test.lua:
local loop_count = tonumber(arg[1])
local fun_pair = "ipairs" == arg[2] and ipairs or pairs
local t = {}
for i=1,100 do
t[i] = i
end
for i=1,loop_count do
for j=1,1000 do
for k,v in fun_pair(t) do
--
end
end
end
执行参数执行结果
time lua test.lua 1000 ipairs 3.96s user 0.02s system 98% cpu 4.039 total
time lua test.lua 1000 pairs 3.97s user 0.01s system 99% cpu 3.992 total
time luajit test.lua 1000 ipairs 0.10s user 0.00s system 95% cpu 0.113 total
time luajit test.lua 10000 ipairs 0.98s user 0.00s system 99% cpu 0.991 total
time luajit test.lua 1000 pairs 1.54s user 0.01s system 99% cpu 1.559 total
从这个执行结果中,大致可以总结出下面几个观点:
在标准 Lua 解释器中,使用 ipairs 或 pairs 没有区别;
对于 pairs 方式,LuaJIT 的性能大约是标准 Lua 的 4 倍;
对于 ipairs 方式,LuaJIT 的性能大约是标准 Lua 的 40 倍。
可以被 JIT 编译的元操作
下面给大家列一下截止到目前已经可以被 JIT 编译的元操作。 其他还有 IO、Bit、FFI、Coroutine、OS、Package、Debug、JIT 等分类,使用频率相对较低,这里就不罗列了。
基础库的支持情况
函数 编译? 备注
assert yes
collectgarbage no
dofile never
error never
getfenv 2.1 partial 只有 getfenv(0) 能编译
getmetatable yes
ipairs yes
load never
loadfile never
loadstring never
next no
pairs no
pcall yes
print no
rawequal yes
rawget yes
rawlen (5.2) yes
rawset yes
select partial 第一个参数是静态变量的时候可以编译
setfenv no
setmetatable yes
tonumber partial 不能编译非10进制,非预期的异常输入
tostring partial 只能编译:字符串、数字、布尔、nil 以及支持 __tostring元方法的类型
type yes
unpack no
xpcall yes
字符串库
函数 编译? 备注
string.byte yes
string.char 2.1
string.dump never
string.find 2.1 partial 只有字符串样式查找(没有样式)
string.format 2.1 partial 不支持 %p 或 非字符串参数的 %s
string.gmatch no
string.gsub no
string.len yes
string.lower 2.1
string.match no
string.rep 2.1
string.reverse 2.1
string.sub yes
string.upper 2.1
表
函数 编译? 备注
table.concat 2.1
table.foreach no 2.1: 内部编译,但还没有外放
table.foreachi 2.1
table.getn yes
table.insert partial 只有 push 操作
table.maxn no
table.pack (5.2) no
table.remove 2.1 部分,只有 pop 操作
table.sort no
table.unpack (5.2) no
math 库
函数 编译? 备注
math.abs yes
math.acos yes
math.asin yes
math.atan yes
math.atan2 yes
math.ceil yes
math.cos yes
math.cosh yes
math.deg yes
math.exp yes
math.floor yes
math.fmod no
math.frexp no
math.ldexp yes
math.log yes
math.log10 yes
math.max yes
math.min yes
math.modf yes
math.pow yes
math.rad yes
math.random yes
math.randomseed no
math.sin yes
math.sinh yes
math.sqrt yes
math.tan yes
math.tanh yes
相关推荐
- 无力吐槽的自动续费(你被自动续费困扰过吗?)
-
今天因为工作需要,需要在百度文库上下载一篇文章。没办法,确实需要也有必要,只能老老实实的按要求买了个VIP。过去在百度文库上有过类似经历,当时为了写论文买了一个月的VIP,后面也没有太注意,直到第二个...
- 百度文库推出“文源计划”创作者可一键认领文档
-
11月7日,百度文库发布了旨在保护创作者权益的“文源计划”。所谓“文源计划”,即为每一篇文档找到源头,让创作者享受更多的权益。据百度文库总经理李小婉介绍,文源计划分为三部分,分别是版权认证、版权扶持和...
- 有开放大学学号的同学,百度文库高校版可以用了。
-
还在网上找百度文库的下载方式,只要从身边的朋友在读开放大学的,那他(她)的学号就可以登陆到国家开放大学图书馆,还使用百度文库高校版来下载。与百度文库稍有不同,但足够使用了。现转国图链接如下:htt...
- 搜索资源方法推荐(搜索资源的方法)
-
今天msgbox就要教大家如何又快又准的搜到各类资源,第一点,排除干扰百度搜索出来啊经常前排展示它的产品以及百度文库,如何去除呢?很简单,后面输入空格减号百度文库,比如你搜高等数学百度文库很多,只要后...
- 一行代码搞定百度文库VIP功能(2021百度文库vip账号密码共享)
-
百度文库作为大家常用查资料找文档的平台,大多数文档我们都可以直接在百度文库找到,然而百度文库也有让人头痛的时候。好不容易找到一篇合适的文档,当你准备复制的时候他却提示你需要开通VIP才能复制~~~下载...
- 百度文库文档批量上传工具用户说明书
-
百度文库文档批量上传工具用户说明书1、软件主要功能1、批量上传文档到百度文库,支持上传到收费、VIP专享、优享以及共享。2、支持自动分类和自动获取标签3、支持多用户切换,一个账户传满可以切换到...
- 百度文库现在都看不到文档是否上传成功,要凉了吗?
-
打开知识店铺,百度文库文档里显示都是下载这一按键,上传的文档也看不到是否成功?咋情况,要取消了吗?没通过审核的也不让你删除,是几个意思,想通吃吗?现在百度上传文档也很费劲,有时弄了半天的资料上传审核过...
- 微信推广引流108式:利用百度文库长期分享软文引流
-
百度文库相对于百度知道、百度百科来说,操作上没那么多条条框框,规则上也相对好把握些。做一条百度知道所花费的精力一般都会比做一条百度文库的要多些,老马个人操作下来觉得百度文库更好把握。但见仁见智吧,今天...
- 职场“避雷”指南 百度文库推出标准化劳动合同范本
-
轰轰烈烈的毕业季结束了,众多应届生在经过了“职场海选”后,已正式成为职场生力军的一员。这一阶段,除了熟悉业务,签订劳动合同、了解职场福利也迅速被提上日程。而随着国人法律意识的增强,百度文库内《劳动合同...
- 《百度文库》:素材精选宝库(百度文库官网首页)
-
《百度文库》:独特功能助力选择高质量素材在当今信息爆炸的时代,如何高效地获取并利用有价值的素材成为了许多人面临的挑战。而《百度文库》作为百度公司推出的一款在线文档分享平台,凭借其丰富的资源、强大的功能...
- 深度整合和开放AI能力 百度文库和网盘推出内容操作系统「沧舟OS」
-
【TechWeb】4月25日消息,Create2025百度AI开发者大会上,百度文库和百度网盘推出全球首个内容操作系统——沧舟OS。基于沧舟OS,百度文库APP全新上线「GenFlow超能搭子」...
- 女子发现大二作业被百度文库要求付费下载,律师:平台侵权,应赔偿
-
近日,28岁的黎女士在百度百科搜索家乡的小地名时,发现了自己在大二完成的课题作业。她继续搜索,发现多个平台收录了该文,比如豆丁网和文档之家等,有的还设置了付费或积分下载。2月15日,九派新闻记者以用户...
- 2016杀入百度文库的新捷径,只有少数人才知道的喔
-
百度的产品在SEO优化中的分量真不用多说,其实很多人都像我一样一直在找捷径。但是我经常发现很多人都是在用死方法。比如发贴吧发帖而不知道去申请一个吧主,知道自问自答而不知道去申请一个合作资格。口碑和贴吧...
- 百度文库付费文档搜索方法(百度文库付费文档搜索方法有哪些)
-
一直以来,百度文库中无论是个人中心还是个人主页,都没有像淘宝一样的店内搜索功能,连最近新开的知识店铺也没有设计店内搜索功能,这无论是对上传用户还是下载用户都不方便,上传用户想要搜索自己的文档无法办到...
- 供读者免费使用!泰达图书馆机构版百度文库新年上新啦
-
在泰达图书馆读者使用百度文库数字资源不需要VIP,免-费-用!惊不惊喜?快来了解一下吧……新年伊始,为满足区域企业、高校、科研院所以及居民群众在教学、科研及学习过程中,对各类文献资源的需求,泰达图书馆...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 如何绘制折线图 (52)
- javaabstract (48)
- 新浪微博头像 (53)
- grub4dos (66)
- s扫描器 (51)
- httpfile dll (48)
- ps实例教程 (55)
- taskmgr (51)
- s spline (61)
- vnc远程控制 (47)
- 数据丢失 (47)
- wbem (57)
- flac文件 (72)
- 网页制作基础教程 (53)
- 镜像文件刻录 (61)
- ug5 0软件免费下载 (78)
- debian下载 (53)
- ubuntu10 04 (60)
- web qq登录 (59)
- 笔记本变成无线路由 (52)
- flash player 11 4 (50)
- 右键菜单清理 (78)
- cuteftp 注册码 (57)
- ospf协议 (53)
- ms17 010 下载 (60)