coroutine 的回收利用
这几天在 lua 和 luajit 的邮件列表上有人讨论 coroutine 的再利用问题。
前几天有个用 skynet 的同学给我写了封邮件,说他的 skynet 服务在产生了 6 万次 timeout 后,内存上升到了 50M 直到 gc 才下降。
这些让我重新考虑 skynet 的消息处理模块。skynet 对每条消息的相应都产生了一个新的 coroutine ,这样才能在消息处理流程中,可以方便的切换出去让调度器调度。诸如 RPC/ socket 读写这些 api 才能在用起来看成是同步调用,却在实现上不阻塞线程。
读源码可知,lua 的 coroutine 非常轻量(luajit 的略重)。但依旧有一些代价。频繁的动态生成 coroutine 对象也会对 gc 造成一定的负担。所以我今天花了一点时间优化了这个问题。
简单说,就是用自己写的 co_create
函数替换掉 coroutine.create 来构建 coroutine 。在原来的主函数上包裹一层。主函数运行完后,抛出一个 EXIT 消息表示主函数运行完毕。并把自己放到池中。如果池中有可利用的旧 coroutine ,则可以传入新的主函数重新利用之。
为了简化设计,如果 coroutine 中抛出异常,就废弃掉这个 coroutine 不再重复利用。为了防止 coroutine 池引用了死对象,需要在主函数运行完后,把主函数引用清空,等待替换。
具体实现参见这个 patch 。
ps. coroutine poll 故意没实现成弱表,而是在相应 debug GC 消息时再主动清空。
Comments
我也试过这种复用coroutine的做法,发现有两个问题:
1. 性能反而更差,我的实现稍有不同,但本质上是一样的,更差的原因估计是如何保留yield的变长参数,有兴趣可以看:https://gist.github.com/kingluo/64396435f7ed55af1b5b
2. 重用的coroutine的旧全局表会否有信息泄露的嫌疑?
Posted by: kingluo | (3) June 18, 2015 05:01 PM
@lpk
加上超时后会增加业务层特别多的复杂度,得不偿失.
如果必须要, 可以不用 call 而用 send 自己模拟. skynet 已经提供了足够的机制把超时做出来.
Posted by: Cloud | (2) August 9, 2013 07:31 PM
能否给coroutine加上超时限制呢,比如skynet.call,跨机器调用别一个服务,而被调用的服务没有响应的话,如果调用次数足够多,这会引起大量coroutine挂起.
Posted by: lpk | (1) August 9, 2013 04:01 PM