我的一个项目中大量使用了 gem parallel 开多进程,因此对 ractor 很期待,希望能达到 cpu 性能接近多线程、内存占用小的效果。现在已经折腾了一个礼拜,实际结果非常失望。
首先,ractor 的用法让我觉得很别扭。比如,我要运行 A.xxx 方法,而它又调用了 B.yyy 方法,需要 Ractor.new(A,B) 把所有 class 全部传入才能使用,否则报错方法 undefined。而且,module 中的方法不能直接使用,也不能和 class 一样传入,我只能在 initializer 中 include module 使其能够在 ractor 中访问。
当然,只要效果能管用,这些都在其次,无非是多了点填坑时间。然而,当我最终在真实项目中把 ractor 跑起来之后,简直大跌眼镜:
ractor 开 2 线程,竟然比开 1 线程慢近 1 倍。
这里说的开 1 线程,还不是不用 ractor,只是调整 ractor 并发数而已,连代码都不变。看 cpu 占用发现,2 线程确实能跑满 2 个核心,但就是比 1 线程慢。
我的第一反应自然是:代码有问题吧?会不会 2 线程执行的总工作量比 1 线程多?于是我把 ractor 线程执行的方法改成一个计算斐波那契数列的简单函数,它的运行时间是固定的。再测,发现效果正常了:2 线程用时是 1 线程的一半左右,继续增加线程超过 cpu 核心数后,耗时不变。又改为 parallel 跑,2 线程也比 1 线程快 30% 左右,不可能出现线程数增加速度反而变慢的效果。
那么这个让 ractor 多线程变慢的方法有何神奇之处?我觉得只是数学计算和创建对象比较多而已。如果连这都没有,那也用不着跑多线程了。
ractor 目前是 experimental 状态,我也不指望现在就大面积应用到真实项目。然而在我的项目中,我竟然找不到一处可以换成 ractor 的地方:除了上述无法理解的变慢问题,还发现 1 个 gem 导致 rails 崩溃、1 个 gem 不兼容无法调用。
但愿是我自己使用的问题,而不是 ractor 本身有问题。如果 ractor 真的不成器的话,对 ruby 实在不是一个好消息,因为 ruby 也没有另一个真正意义上的多线程了。