Lua 字节码与字符串的共享
我们的系统的应用场合比较特殊,在同一个进程内存在数千个 lua_State
。
Lua 的虚拟机占用的内存已经足够小了,但还是抗不住数量多啊。所以我希望有版本节约一些内存。
最想做的一件事情是把不同 lua_State
中相同的函数字节码合并起来共用一块内存。要做到这一点并不复杂。而且可以提高一些内存访问的效率。(因为大部分 lua 程序在并行执行相同的逻辑)
首先我们需要准备一个用来共享数据块的模块,它必须是线程安全的。因为既然分到了不同的 lua_State
就是想利用并发的优势。针对这个特定需求定制这样一个模块可以做到 lock-free 。
进程开始时初始化一个足够大的 hash 表,不要管 hash 冲突的问题,每个节点只需要初始化一次,这样可以最大可能的简化逻辑。就不必用锁了。
对每个可能需要共享的内存块,我们可以先计算 hash 值,然后比对 hash 表中是否有相同 hash 值的节点,然后做一次全文比较。如果相同,就可以直接引用以前的数据块。
如果已经存在相同 hash 值但内容不同的数据块,那么不要替换掉旧数据块(用 CAS 指令可以做到这点)。
在数据块上做一个标记区分这两种数据块,当释放数据块的时候,在 hash 表中的永远不要释放,不在 hash 表中的没有多个引用就可以释放了。
由于 hash 表中的值只可能为空,或是一个有唯一意义的值。且一旦被赋值后,就不可能更改(引用的内存也永不释放)。这个机制就不需要锁来保证并发安全了。由于 hash 表的大小有限,这套机制所占用的总内存也不可能无限增加,所以是可控的。
剩下个工作就是修改 lua 的虚拟机实现。我在 lobject.h 中给 Proto 结构增加了一个域标记其 opcode 是否被共享化。然后在 lparser.c 中,找到 close_func
这个函数,再构造完 opcode 的 buffer 后(可以同样处理的还有 lineinfo 这组调试信息),把指针传入前面的模块,并从 Lua 管理的内存中释放掉。最后设置标记表示 opcode 已经被复制到全局共享区。
在 lfunc.c 中的 luaF_freeproto
可以找到释放代码。当检查到共享标记时,改为通知共享模块去处理。
如果 lua 代码不是通过 parser 加载,那么还需要修改 lundump.c
可以被共享的数据必须符合的原则是,一旦够构造出来,就不会被修改。符合这个原则的数据还有 lua 中的字符串 TString 。
但我们需要先的 TString 做一点小改造。lua 直接把字符串内存附加在 TString 这样一个 GCObject 的内存后面。这会使得不同 lua_State
中的字符串继续完全相同,结构也会有差异。
简单的改法是给 TString 加一个指针,指向独立的内存块存放字符串内容。只需要修改 lobject.h 中的 getstr(ts) 宏就可以改变 lua 代码中所有访问字符串体的方式。注:这会损失一丁点性能。
然后修改 lstring.c 中的 createstrobj 函数,lstring.h 中的sizestring(s) 宏,以及 lgc.c 中的 freeobj 函数就大功告成了。
我在我们的系统上做了对比测试,平均每个 lua_State
可以节省下 500K 左右的内存,大部分是在 opcode 上节约下来的。这也和我们每个 lua 虚拟机中加载的代码量接近,证明这个方法是有效且被正确实现了。
我们每个 'lua_State` 占用 5 到 10 M 内存。所以大约节约了 10% 左右的内存,不算特别可观。以后还要在实际环境中评测。
考虑到日后一台主机上会有至少 2000 个 lua 虚拟机在运行,我认为省下 1G 的内存应该还是有意义的。尤其是这些是运行 lua 字节码最常访问到的部分,对 CPU cache 的利用很有好处。当然在现阶段只是臆测,过段时间再做细致的测试比较。
对有耐心读完的读者,这里放一个彩蛋 :)
最近有一点闲情,所以继续写了那本书两章。对,就是那本传说中的《Lua 源码欣赏》。上次提到它都是今年一月份的事情了。
依旧不保证它不会太监掉。我写起来才发现工程巨大啊,感觉至少还有 80% 的内容要写。为了让书中的细节不出错,需要反复校对源代码,比之前几次阅读 lua 的代码累多了。
开始动笔时,lua 还是 5.2.0 版,现在已经是 5.2.1 了,改动的位置不少。相比最初完整阅读 Lua 代码时的 Lua 5.0 以及后来再读时的 Lua 5.1 ,每次阅读都是新体验。
希望在 Lua 5.3 发布前能够完成。
Comments
Posted by: PigFly | (18) March 19, 2014 06:03 PM
Posted by: aozima | (17) November 28, 2013 12:13 AM
Posted by: Anonymous | (16) December 3, 2012 11:01 AM
Posted by: Anonymous | (15) November 22, 2012 03:53 PM
Posted by: 五金配件 | (14) November 21, 2012 10:08 AM
Posted by: lpk | (13) November 16, 2012 12:11 AM
Posted by: ctx | (12) November 15, 2012 04:12 PM
Posted by: ctx | (11) November 15, 2012 04:06 PM
Posted by: Jack | (10) November 14, 2012 11:13 AM
Posted by: shuax | (9) November 11, 2012 09:27 AM
Posted by: 五金配件 | (8) November 10, 2012 02:31 PM
Posted by: Cloud | (7) November 10, 2012 02:17 PM
Posted by: sw | (6) November 10, 2012 09:30 AM
Posted by: sw | (5) November 10, 2012 07:42 AM
Posted by: xishvai | (4) November 10, 2012 12:46 AM
Posted by: Genius | (3) November 9, 2012 08:40 PM
Posted by: CatIsFlying | (2) November 9, 2012 07:24 PM
Posted by: rongekuta | (1) November 9, 2012 05:45 PM