* Concurrent::Map usage was removed from this file in 3239ed48d28f3c0baf4445e6c279107e892b7cab
* Monitor usage was removed in f233598d2da773c2024cbe62a199ddc70d9fd7a1
Or it would raise if the argument was frozen.
And even with this change, it would still reduce String allocations together with 9276ea89d2b0be9fdd1ad6590857f8d45a38c267
because `escape` should be `true` in most cases
* Since #18411, we started to inform about extracted gem (record_tag_helper)
to developers who use `ActionView::Helpers::RecordTagHelper` 's methods.
* Currently, it seems no problem that we don't have to support no longer.
Since we introduced default option for `class_attribute` and
`mattr_accessor` family of methods and changed all occurrences of setting
default values by using of `:default` option I think it would be fine to use
`:default` option in order to set default value of `finalize_compiled_template_methods`
since it expresses itself very well.
Related to #29294, #32418
ActionView::Template instances compile their source to methods on the
ActionView::CompiledTemplates module. To prevent leaks in development
mode, where templates can frequently change, a finalizer is added that
undefines these methods[1] when the templates are garbage-collected.
This is undesirable in the test environment, however, as templates don't
change during the life of the test. Moreover, the cost of undefining a
method is proportional to the number of descendants a class or module
has, since the method cache must be cleared for all descendant classes.
As ActionView::CompiledTemplates is mixed into every
ActionView::TestCase (or in RSpec suites, every view spec example
group), it can end up with a very large number of descendants, and
undefining its methods can become very expensive.
In large test suites, this results in a long delay at the end of the
test suite as all template finalizers are run, only for the process to
then exit.
To avoid this unnecessary cost, this change adds a config option,
`action_view.finalize_compiled_template_methods`, defaulting to true,
and sets it to false in the test environment only.
[1] 09b2348f7f/actionview/lib/action_view/template.rb (L118-L126)
The spacing in these comments is fairly inconsistent. Array argument
contents are often separated with a space from the array literal
brackets but in several cases the Hash literal curly braces are tangent
to their contents which makes the documentation harder to read in some
cases.
If the options passed in don't have a default key, there's no point in
creating an array from those empty results when we can just go straight
to creating an empty array.
Benchmarks:
```ruby
master_version with false
{:FREE=>-2497, :T_STRING=>52, :T_ARRAY=>2000, :T_HASH=>1000, :T_IMEMO=>1}
master_version with true
{:FREE=>-3001, :T_ARRAY=>2000, :T_HASH=>1000}
fast_version with false
{:FREE=>-1001, :T_ARRAY=>1000}
fast_version with true
{:FREE=>-3001, :T_ARRAY=>2000, :T_HASH=>1000}
Warming up --------------------------------------
master_version with false
104.985k i/100ms
master_version with true
118.737k i/100ms
fast_version with false
206.013k i/100ms
fast_version with true
107.005k i/100ms
Calculating -------------------------------------
master_version with false
1.970M (±24.6%) i/s - 8.924M in 5.010302s
master_version with true
2.152M (±12.4%) i/s - 10.686M in 5.051588s
fast_version with false
5.613M (±19.6%) i/s - 26.782M in 5.003740s
fast_version with true
2.027M (±15.8%) i/s - 9.951M in 5.065670s
Comparison:
fast_version with false: 5613159.2 i/s
master_version with true: 2152354.4 i/s - 2.61x slower
fast_version with true: 2027296.0 i/s - 2.77x slower
master_version with false: 1969824.9 i/s - 2.85x slower
```
Benchmark code:
```ruby
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update
your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem "benchmark-ips"
gem "rails"
end
def allocate_count
GC.disable
before = ObjectSpace.count_objects
yield
after = ObjectSpace.count_objects
after.each { |k,v| after[k] = v - before[k] }
after[:T_HASH] -= 1 # probe effect - we created the before hash.
GC.enable
result = after.reject { |k,v| v == 0 }
GC.start
result
end
def master_version(key)
Array({}.delete(:default)).compact
end
def fast_version(key)
if key
Array({}.delete(:default)).compact
else
[]
end
end
def test
puts "master_version with false"
puts allocate_count { 1000.times { master_version(false) } }
puts "master_version with true"
puts allocate_count { 1000.times { master_version(true) } }
puts "fast_version with false"
puts allocate_count { 1000.times { fast_version(false) } }
puts "fast_version with true"
puts allocate_count { 1000.times { fast_version(true) } }
Benchmark.ips do |x|
x.report("master_version with false") { master_version(false) }
x.report("master_version with true") { master_version(true) }
x.report("fast_version with false") { fast_version(false) }
x.report("fast_version with true") { fast_version(true) }
x.compare!
end
end
test
```
This gets called many times for each virtual_path, creating a new string
each time that `translate` is called. We can memoize this so that it
only happens once per virtual_path instead.