aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2019-02-05 15:07:55 -0800
committerAaron Patterson <aaron.patterson@gmail.com>2019-02-05 15:24:21 -0800
commit24b068bea13e8f91397b3440494f0a5326f80882 (patch)
tree8aa1cd24df9b05ae02322020a919066725af996f
parent0fa0107d2d87f751c6768fa8a0991149134de2df (diff)
downloadrails-24b068bea13e8f91397b3440494f0a5326f80882.tar.gz
rails-24b068bea13e8f91397b3440494f0a5326f80882.tar.bz2
rails-24b068bea13e8f91397b3440494f0a5326f80882.zip
Speed up partial rendering by caching "variable" calculation
This commit speeds up rendering partials by caching the variable name calculation on the template. The variable name is based on the "virtual path" used for looking up the template. The same virtual path information lives on the template, so we can just ask the cached template object for the variable. This benchmark takes a couple files, so I'll cat them below: ``` [aaron@TC ~/g/r/actionview (speed-up-partials)]$ cat render_benchmark.rb require "benchmark/ips" require "action_view" require "action_pack" require "action_controller" class TestController < ActionController::Base end TestController.view_paths = [File.expand_path("test/benchmarks")] controller_view = TestController.new.view_context result = Benchmark.ips do |x| x.report("render") do controller_view.render("many_partials") end end [aaron@TC ~/g/r/actionview (speed-up-partials)]$ cat test/benchmarks/test/_many_partials.html.erb Looping: <ul> <% 100.times do |i| %> <%= render partial: "list_item", locals: { i: i } %> <% end %> </ul> [aaron@TC ~/g/r/actionview (speed-up-partials)]$ cat test/benchmarks/test/_list_item.html.erb <li>Number: <%= i %></li> ``` Benchmark results (master): ``` [aaron@TC ~/g/r/actionview (master)]$ be ruby render_benchmark.rb Warming up -------------------------------------- render 41.000 i/100ms Calculating ------------------------------------- render 424.269 (± 3.5%) i/s - 2.132k in 5.031455s ``` Benchmark results (this branch): ``` [aaron@TC ~/g/r/actionview (speed-up-partials)]$ be ruby render_benchmark.rb Warming up -------------------------------------- render 50.000 i/100ms Calculating ------------------------------------- render 521.862 (± 3.8%) i/s - 2.650k in 5.085885s ```
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb49
-rw-r--r--actionview/lib/action_view/template.rb9
-rw-r--r--activerecord/lib/active_record/railties/collection_cache_association_loading.rb2
3 files changed, 41 insertions, 19 deletions
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index 478400e016..801916954f 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -295,8 +295,21 @@ module ActionView
end
def render(context, options, block)
- setup(context, options, block)
- template = find_partial
+ as = as_variable(options)
+ setup(context, options, as, block)
+
+ if @path
+ if @has_object || @collection
+ @variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as)
+ @template_keys = retrieve_template_keys(@variable)
+ else
+ @template_keys = @locals.keys
+ end
+ template = find_partial(@path, @template_keys)
+ @variable ||= template.variable
+ else
+ template = nil
+ end
@lookup_context.rendered_format ||= begin
if template && template.formats.first
@@ -359,7 +372,7 @@ module ActionView
# If +options[:partial]+ is a string, then the <tt>@path</tt> instance variable is
# set to that string. Otherwise, the +options[:partial]+ object must
# respond to +to_partial_path+ in order to setup the path.
- def setup(context, options, block)
+ def setup(context, options, as, block)
@options = options
@block = block
@@ -382,25 +395,25 @@ module ActionView
if @collection
paths = @collection_data = @collection.map { |o| partial_path(o, context) }
- @path = paths.uniq.one? ? paths.first : nil
+ if paths.uniq.length == 1
+ @path = paths.first
+ else
+ paths.map! { |path| retrieve_variable(path, as).unshift(path) }
+ @path = nil
+ end
else
@path = partial_path(@object, context)
end
end
+ self
+ end
+
+ def as_variable(options)
if as = options[:as]
raise_invalid_option_as(as) unless /\A[a-z_]\w*\z/.match?(as.to_s)
- as = as.to_sym
+ as.to_sym
end
-
- if @path
- @variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as)
- @template_keys = retrieve_template_keys
- else
- paths.map! { |path| retrieve_variable(path, as).unshift(path) }
- end
-
- self
end
def collection_from_options
@@ -414,8 +427,8 @@ module ActionView
@object.to_ary if @object.respond_to?(:to_ary)
end
- def find_partial
- find_template(@path, @template_keys) if @path
+ def find_partial(path, template_keys)
+ find_template(path, template_keys)
end
def find_template(path, locals)
@@ -511,9 +524,9 @@ module ActionView
end
end
- def retrieve_template_keys
+ def retrieve_template_keys(variable)
keys = @locals.keys
- keys << @variable if @has_object || @collection
+ keys << variable
if @collection
keys << @variable_counter
keys << @variable_iteration
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index 3b2c264ed4..8a5407c622 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -128,6 +128,8 @@ module ActionView
end
end
+ attr_reader :variable
+
def initialize(source, identifier, handler, details)
format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
@@ -138,6 +140,13 @@ module ActionView
@original_encoding = nil
@locals = details[:locals] || []
@virtual_path = details[:virtual_path]
+
+ @variable = if @virtual_path
+ base = @virtual_path[-1] == "/" ? "" : File.basename(@virtual_path)
+ base =~ /\A_?(.*?)(?:\.\w+)*\z/
+ $1.to_sym
+ end
+
@updated_at = details[:updated_at] || Time.now
@formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
@variants = [details[:variant]]
diff --git a/activerecord/lib/active_record/railties/collection_cache_association_loading.rb b/activerecord/lib/active_record/railties/collection_cache_association_loading.rb
index dfaac4eefb..d57680aaaa 100644
--- a/activerecord/lib/active_record/railties/collection_cache_association_loading.rb
+++ b/activerecord/lib/active_record/railties/collection_cache_association_loading.rb
@@ -3,7 +3,7 @@
module ActiveRecord
module Railties # :nodoc:
module CollectionCacheAssociationLoading #:nodoc:
- def setup(context, options, block)
+ def setup(context, options, as, block)
@relation = relation_from_options(options)
super