Railsのviewで、eachを回してる中で繰り返しrenderしているところがあって、体感的に特に遅かったわけでは無いのですが、ファイルの読み込みや検索が繰り返し行われてるんじゃ無いかと気になってベンチを取ってみました。

<% Benchmark.bm 10 do |r|%>
  <% r.report "inline" do %>
    <% 1000.times do%>
    <div><%= Time.zone.now.strftime('%Y-%m-%d %H:%M:%S') %></div>
    <%end%>
  <% end %>

  <% r.report "render" do %>
    <% 1000.times do%>
    <%= render 'root/foobar' %>
    <%end%>
  <% end %>

  <% r.report "read" do %>
    <% erb = File.read(Rails.root.join('app', 'views', 'root', '_foobar.html.erb')) %>
    <% 1000.times do%>
    <%== ERB.new(erb).result(binding) %>
    <%end%>
  <% end %>
<% end %>
# app/views/root/_foobar.html.erb
<div><%= Time.zone.now.strftime('%Y-%m-%d %H:%M:%S') %></div>

結果はこんな感じです。

                 user     system      total        real
inline       0.018214   0.000176   0.018390 (  0.021384)
render       0.709417   0.749956   1.459373 (  1.526002)
read         0.095806   0.000444   0.096250 (  0.097051)

renderはsystemに非常に時間がかかってるので、毎回ファイルを読み込んでいそうです。試しにキャッシュして読み込むメソッドを作ってみた。

# app/helpers/application_helper.rb
  def render_erb_with_cache(path, locals = {})
    erb = Rails.cache.fetch "#{path}@render_erb_with_cache" do
      # ActionView::LookupContextでテンプレートを見つける。
      lookup_context = ActionView::LookupContext.new('app/views')

      # 3番目の引数は`partial`でこれをtrueにするとファイル名に`_`を付与して探します。
      # 返り値はActionView::Template
      template_path = lookup_context.find_template(path, [], true, [], {})
      # sourceアトリビュートで内容が取得可能。
      template_path.source
    end

    view_context = ActionView::Base.new('app/views', {}, controller)

    # ApplicationHelperのメソッドを使えるようにする。他のヘルパーを使いたい場合も読み込む必要あり。
    view_context.extend(ApplicationHelper)

    # URLヘルパーを使えるようにする。
    view_context.extend(Rails.application.routes.url_helpers)
    view_context.extend(ActionView::RoutingUrlFor)

    # renderする
    view_context.render(inline: erb, locals: locals)
  end
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.