使用Rails一段时间之后,你可能就会开始吹毛求疵的想要提高它性能。这是一系列文章中第一次考虑如何提高(即使微不足道的)Rails的性能。
我将会关注在一些gem的提速上面,在某些情况下,可能是一小部分的Rails,如html转义,String.blank?和JSON工具类。
基准原则
原则,对于仅仅在控制台wrk运行几次来讲,是一个与其过强的词语,但是我这里不是来寻找“圣杯”的,而是提供一些初始的想法。
我将从旧的apache ab切换到wrk。
wrk是现代的 HTTP 基准工具,当在一个单一的多核 CPU 上运行时,能够产生巨大的负载。
- wrk -t10 -c10 -d10s http://localhost:3000
这条指令运行基准问题10s,使用10个线程,并且保持打开50个HTTP链接,也就是说,这样就足够了。记得将这些基准测试在你实际的应用中跑一下,看一下实际上的性能提高有多少。
escape_utils gem
通过可爱的escape_utils gem可以加快HTML的转义。为了使其能够在Rails中使用,需要添加一个初始值设定来解决:
- begin
- require 'escape_utils/html/rack' # to patch Rack::Utils
- require 'escape_utils/html/erb' # to patch ERB::Util
- require 'escape_utils/html/cgi' # to patch CGI
- require 'escape_utils/html/haml' # to patch Haml::Helpers
- rescue LoadError
- Rails.logger.info 'Escape_utils is not in the gemfile'
- end
对该逻辑进行测试的用例:
- def escape_utils
- @escape_me = <<-HTML
- <body class="application articles_show">
- <!-- Responsive navigation
- ==================================================== -->
- <div class="container">
- <nav id="nav">
- <ul>
- <li><a href="/"><i class="ss-standard ss-home"></i>home</a></li>
- <li><a href="/home/about"><i class="ss-standard ss-info"></i>about</a></li>
- <li><a href="/contact"><i class="ss-standard ss-ellipsischat"></i>contact</a></li>
- <li><a href="/home/projects"><i class="ss-standard ss-fork"></i>projects</a></li>
- <li><a href="/tags"><i class="ss-standard ss-tag"></i>tags</a></li>
- <li><a href="/articles?query=code"><i class="ss-standard ss-search"></i>search</a></li>
- </ul>
- </nav>
- <a href="#" class="ss-standard ss-list" id="nav-toggle" aria-hidden="true"></a>
- HTML
- render inline: "Hello world <%= @escape_me %>"
- end
使用标准Rails:
- Running 10s test @ http://localhost:3000/sidechannels/bench
- 2 threads and 10 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 35.40ms 3.55ms 64.70ms 91.98%
- Req/Sec 142.19 11.68 164.00 83.12%
- 2837 requests in 10.00s, 4.92MB read
- Requests/sec: 283.61
- Transfer/sec: 503.34KB
使用escape_utils gem:
- Running 10s test @ http://localhost:3000/sidechannels/bench
- 2 threads and 10 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 34.06ms 3.89ms 63.92ms 89.10%
- Req/Sec 148.65 13.36 180.00 75.94%
- 2960 requests in 10.00s, 5.46MB read
- Requests/sec: 295.98
- Transfer/sec: 558.72KB
fast_blank gem
是否在印象里,blank?方法太慢?不用多说,试一下fast_blank gem!
仅需要在你的Gemfile中添加gem 'fast_blank',这应该就可以非常漂亮的提高像这篇文章中提到的String.black?方法的速度。为了测试,我仅添加下俩代码:
fast_blank是一个简单的扩展,提供了一个支持String.blank?功能的快速实现。
- def fast_blank_test
- n = 1000
- strings = [
- "",
- "\r\n\r\n ",
- "this is a test",
- " this is a longer test",
- " this is a longer test
- this is a longer test
- this is a longer test
- this is a longer test
- this is a longer test"
- ]
- Benchmark.bmbm do |x|
- strings.each do |s|
- x.report("Fast Blank #{s.length} :") do
- n.times { s.blank? }
- end
- end
- end
- render nothing: true
- end
#p#
使用标准Rails:
- Running 10s test @ http://localhost:3000/sidechannels/bench
- 2 threads and 10 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 1.40s 207.72ms 1.58s 92.68%
- Req/Sec 3.10 2.11 6.00 53.66%
- 69 requests in 10.01s, 33.08KB read
- Requests/sec: 6.90
- Transfer/sec: 3.31KB
使用fast_blank gem:
- Running 10s test @ http://localhost:3000/sidechannels/bench
- 2 threads and 10 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 1.33s 179.56ms 1.41s 93.33%
- Req/Sec 3.07 0.80 4.00 40.00%
- 72 requests in 10.00s, 34.52KB read
- Requests/sec: 7.20
- Transfer/sec: 3.45KB
oj gem
- # oj gem
- gem 'oj'
- gem 'oj_mimic_json' # we need this for Rails 4.1.x
这个测试用例非常简单,仅仅将所有的article序列化为JSON格式:
- class SidechannelsController < ApplicationController
- def oj
- render json: Article.all
- end
- end
使用标准Rails序列化器:
- Running 10s test @ http://localhost:3000/sidechannels/bench
- 2 threads and 10 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 108.37ms 5.12ms 134.90ms 83.33%
- Req/Sec 45.76 3.60 55.00 57.69%
- 922 requests in 10.00s, 57.41MB read
- Requests/sec: 92.17
- Transfer/sec: 5.74MB
使用oj gem:
- Running 10s test @ http://localhost:3000/sidechannels/bench
- 2 threads and 10 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 78.06ms 4.43ms 92.83ms 81.31%
- Req/Sec 63.64 5.33 71.00 64.49%
- 1277 requests in 10.00s, 79.83MB read
- Requests/sec: 127.65
- Transfer/sec: 7.98MB
使用jemalloc
好吧,这其实不是一个真正的gem,如果你想深入探究它,可以看我的这篇文章。在初始测试时,jemalloc并没有产生太多性能的提升,至少对我使用的测试用例是这样的。
提示:某些情况下,可能会默认包含在Ruby中。
更新:请一定尝试一下kzk的jemalloc gem:
- gem install jemalloc
- je -v rails s
深入探究你的Rails应用
不要担心,去用一下Sam Saffron的带有非常棒的FlameGraphs的MiniProfiler吧!
结语
鉴于你的应用要做什么,你可能想为你的Gemfile添加上述的一些gem。通常我会把他们都添加上,当然是出于一个好的估量(你可能会想检查你的RAM利用率,然后在添加之前,进行一个完整的测试)。
oj gem基于JSON API,对Rails来说是非常不错的,使用oj gem,你可以删除视图并仅使用代言人或者你选择的模式进行序列化。