« MongoDB 的 Lua Driver | 返回首页 | 写了一个 lua bson 库 »

用栈方式管理 Lua 中的 C 对象

最近思考了给 Lua 写 C 扩展的另一个问题。

我曾经总结过几种 Lua C 库中 C/C++ 对象的生命期管理问题 。最近想到另一个方案,虽然实现后并没有用到项目里,但值得记录一下。

Lua 没有 RAII ,一切对象的回收是依赖 GC 的。封装 C/C++ 对象则一般用 userdata 。userdata 比较重,作为临时对象使用总觉得有点别扭。比如封装 matrix 对象,如果我们为每个 matrix 对象都生成一个 userdata ,那么一些临时的 matrix 对象就会一直推迟到 GC 发生的时候才回收。而在 C/C++ 这样的语言中,临时对象通常是在离开调用层次时自动释放的。

对于某些 C 和 Lua 混合的业务也有这样的问题。某些较长的业务流程,一部分环节由于性能原因使用 C 来实现,另一部分更适合直接用 Lua 。我们必须用 userdata 来交换中间状态。比如处理一个 C 层次上产生的数据包或 C 结构数据,交由 Lua 处理后,C 对象就没有必要再存在了。但处理过程中,Lua 代码则需要反复引用和处理它。

多数情况下,我们不用太考虑这两者间的差别。但这并不妨碍我去考虑有没有可能在 Lua 中模拟一套栈对象的管理机制。它可能是 GC 系统之外的一种对象生命期管理的选择。


我试着用 C 实现了一个简单的栈结构,每个堆栈用一个数组来保存一系列相同的 C 对象(或指针)。由于 Lua 的 coroutine 的存在,没有 lua thread 都应该有一个独立的栈,所以我把这个结构封装成 userdata 放在一张表里,用当前的 lua thread (即 lua State 对象)索引。

用户可以主动的调用入栈或出栈,这有一点繁琐,可以考虑放在 debug hook 中实现,但我觉得手工调用更好。如果入栈和出栈调用不匹配的话,栈深度就很容易变成负数或超过设置的最大值。所以一旦写错,很容易被发现。

索引栈上的对象可以用一个组合起来的 id ,高位使用 stack frame 的编号,低位使用序号。这个 id 可以对应到唯一的对象。我采用单调递增的 stack frame 编号,也就是说,及时在同一层次的 stack frame 上,多次函数调用在同一位置产生的对象也有不同的 id 。

这个 id 就可以以 lightuserdata 的形式保存在 Lua 中的。Lua API 引用 lightuerdata 的时候,可以校验 id 是否还在当前栈上,并得到真正的对象。当离开调用层次时,C 库去释放掉那些过期的对象。

Comments

@middleware 主要是因为 lua/C 混合开发,某些 C/C++ 库在设计上, 性能依赖于 RAII. 把这种 C 库封装成 lua 库使用时, 固然可以用 gc 替代 on stack object 的内存管理. 但这不是那些 C 库的设计初衷. 结果会把性能压力加在 gc 模块上. 我见过一个 lua vector 的实现是通过修改 lua vm, 把 vector 变成值对象实现的, 也是为了减轻 gc 的开销.
C++ 因为缺少 GC,所以大量采用 value-copy semantic,大量使用 on-stack 对象。对于依赖 GC 的语言,应该多使用 pass-by-ref 语义。
不错的研究
您好像被盗版了。http://9.douban.com/subject/9011340/
如果c和lua的交互出现性能问题,那多半是设计出了问题,应该重新设计交互的方式,或是把更多的代码移到c模块中,而不是通过这种tricky的方式来打补丁。

Post a comment

非这个主题相关的留言请到:留言本