Rubinius 2.2.3版本的详细指南

190 阅读6分钟

Ruby 2.1在今年圣诞节发布了,这是个好消息。它采用了更好的GC(RGenGC - gerational GC),层次化的方法缓存,一些小的语法变化和非实验性的Rfinements。总而言之,我们可以期待5%到15%的性能提升,这是相当棒的。

当我在阅读与发布有关的黑客新闻线程中的评论时,其中一条引起了我的注意--我们在MRI Ruby VM中需要JIT。好吧,但看起来每个人都忘记了Rubinius,它有一段时间是基于LLVM的JIT,本地线程,低暂停生成的垃圾收集器和几乎完美的C扩展支持。

是的,我们还有JRuby--它可能是最快的实现,当与TorqueBox这样的服务器结合时甚至更快,主要问题是:一些C语言库需要被替换,但正如人们在文章后面所看到的,这只是旧思维在起作用,显然现在情况要好得多,因为大多数宝石都支持JRuby,没有任何问题。

因此,我们在Ruby虚拟机之间有一个很好的中间地带:它支持两种C语言扩展,没有任何问题(然而,至少从经验上看,一些更奇特的宝石可能无法安装),但不知为何它被忽略了?

计划很简单,把一个生产中的Rails 4.0.2应用程序及其所有的依赖关系转换为Rubinius,安装Puma并做一些基准测试,然后如果一切正常就部署到staging。

设置

Rubinius将大部分标准库提取为宝石,所以为了正确启动任何使用这些宝石的Ruby脚本,需要在宝石文件中加入这个:

gem 'racc'
gem 'rubysl'
gem 'puma'

备注:

  • rubysl - 是Ruby标准库gem的一个相当隐蔽的名字。
  • racc 是 tenderlove 编写的 LALR(1) 解析器生成器 - 也是 Rubinius 的硬性要求,否则无法启动 Rails。
  • puma - 这是Rubinius的最佳服务器选择,因为它支持本地线程。

不能与Rubinius一起使用的宝石(如果我找到更多的宝石,将会更新):

# gem 'oj'

记得把它们注释掉,否则Rails将无法启动。

对于虚拟机的安装和切换,我使用的是古老的RVM和最新版本的Rubinius 2.2.3以及Ruby 2.1.0。

现在来看看基准测试!

好吧,由于明显的原因,我不能分享应用程序的源代码,在某些时候,我可能会创建一个公共资源库,用于测试的东西。这些基准只针对一个特殊情况,也是为了好玩,所以在根据这些基准做出任何决定之前,请先在你自己身上测试。

Rails应用程序实际上是一个API,所以大部分重要的部分都被禁用了(例如,流式数据流 Rendering RequestForgeryProtection),还有链轮轨道。

ApacheBench配置

我使用了简单的ApacheBench,2.3版 - 显然不是理想的基准测试工具(应该使用siege或类似的工具),但对于像这个测试这样的快速浏览,它很适合工作。

启动它的命令: ab -n400 -c16 -T'application/json' http://localhost:3000/entries

unicorn.rb

# config/unicorn.rb
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 15
preload_app true

before_fork do |server, worker|
  # et cetera
end

启动它的命令: The command to start it up:
unicorn_rails -c config/unicorn.rb -p 3000

运行一段时间后的结果:

Concurrency Level:      16
Time taken for tests:   6.769 seconds
Complete requests:      400
Failed requests:        0
Write errors:           0
Total transferred:      3611600 bytes
HTML transferred:       3346800 bytes
Requests per second:    59.09 [#/sec] (mean)
Time per request:       270.766 [ms] (mean)
Time per request:       16.923 [ms] (mean, across all concurrent requests)
Transfer rate:          521.03 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   1.0      0       5
Processing:    49  267  36.8    270     331
Waiting:       44  266  36.8    269     330
Total:         49  267  36.3    270     331

Percentage of the requests served within a certain time (ms)
  50%    270
  66%    283
  75%    288
  80%    293
  90%    308
  95%    315
  98%    324
  99%    327
 100%    331 (longest request)

puma.rb on Rubinius 2.2.3

# config/puma.rb
threads 8,32
workers 1

preload_app!

on_worker_boot do
  # et cetera
end

启动它的命令:启动它的命令:启动它的命令。
puma -C config/puma.rb -b tcp://localhost:3000

多次运行后的结果(以便JIT能够发挥其魔力):

Concurrency Level:      16
Time taken for tests:   9.383 seconds
Complete requests:      400
Failed requests:        0
Write errors:           0
Total transferred:      3590400 bytes
HTML transferred:       3346800 bytes
Requests per second:    42.63 [#/sec] (mean)
Time per request:       375.311 [ms] (mean)
Time per request:       23.457 [ms] (mean, across all concurrent requests)
Transfer rate:          373.69 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:    84  371 105.4    348     731
Waiting:       83  363 104.1    338     728
Total:         84  371 105.3    348     732

Percentage of the requests served within a certain time (ms)
  50%    348
  66%    390
  75%    431
  80%    458
  90%    526
  95%    571
  98%    640
  99%    683
 100%    732 (longest request)

puma.rb on JRuby 1.7.9

# config/puma.rb
threads 8,32

preload_app!

on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end

启动它的命令:启动它的命令:启动它的命令。
puma -C config/puma.rb -b tcp://localhost:3000

需要替换的宝石:

# gem 'pg'
gem 'activerecord-jdbcpostgresql-adapter'

多次运行后的结果(以便JIT能够施展它的魔法):

Concurrency Level:      16
Time taken for tests:   4.019 seconds
Complete requests:      400
Failed requests:        0
Write errors:           0
Total transferred:      3590400 bytes
HTML transferred:       3346800 bytes
Requests per second:    99.53 [#/sec] (mean)
Time per request:       160.760 [ms] (mean)
Time per request:       10.048 [ms] (mean, across all concurrent requests)
Transfer rate:          872.42 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:    35  158  31.5    157     389
Waiting:       34  151  27.1    149     261
Total:         36  158  31.5    157     389

Percentage of the requests served within a certain time (ms)
  50%    157
  66%    166
  75%    173
  80%    177
  90%    189
  95%    204
  98%    232
  99%    260
 100%    389 (longest request)

结论

Rubinius的性能不佳可能与racc gem有关,因为它可能真的很慢,在这个Github线程中详细说明了这一点

14 req/s vs 60 req/s(我禁用了缓存,而且该应用产生了大量的ActiveRecord对象,这就是为什么数字相当低的原因)使得Rubinius目前不是这个特定Rails应用的好选择。

更新

感谢headius,我修改了基准:
- 显然,我的虚拟机只访问了一个核心(因此Rubinius和JRuby最初的性能很差) - 撞到了四个
- 更新了所有的基准,还加入了JRuby 1.7.9

   jruby 99.53 #################################
   cruby 59.09 ###################
rubinius 42.63 ##############

从上面的图表中可以清楚地看到,JRuby以令人印象深刻的优势胜出,而且只有一个gem的变化,我认为它应该被推到staging。