通過添加一些 gems 來提升 Rails 應用的性能
使用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> HTMLrender inline: "Hello world <%= @escape_me %>" end</pre>
使用標準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.72KBfast_blank gem
是否在印象里,blank?方法太慢?不用多說,試一下fast_blank gem!
僅需要在你的Gemfile中添加gem 'fast_blank',這應該就可以非常漂亮的提高像這篇文章中提到的String.black?方法的速度。為了測試,我僅添加下倆代碼:
fast_blank是一個簡單的擴展,提供了一個支持String.blank?功能的快速實現。def fast_blank_test n = 1000strings = [ "", "\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</pre>
使用標準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.45KBoj 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 jemallocje -v rails s</pre>
深入探究你的Rails應用
不要擔心,去用一下Sam Saffron的帶有非常棒的FlameGraphs的MiniProfiler吧!
結語
鑒于你的應用要做什么,你可能想為你的Gemfile添加上述的一些gem。通常我會把他們都添加上,當然是出于一個好的估量(你可能會想檢查你的RAM利用率,然后在添加之前,進行一個完整的測試)。
oj gem基于JSON API,對Rails來說是非常不錯的,使用oj gem,你可以刪除視圖并僅使用代言人或者你選擇的模式進行序列化。