diff options
| author | Pete Higgins <pete@peterhiggins.org> | 2014-09-28 14:42:26 -0700 | 
|---|---|---|
| committer | Pete Higgins <pete@peterhiggins.org> | 2014-09-28 15:38:32 -0700 | 
| commit | 796cab45561fce268aa74e6587cdb9cae3bb243e (patch) | |
| tree | bb1bafe2911318c3ff061650a062ded660dccc89 /actionpack/lib/action_dispatch/testing/assertions/response.rb | |
| parent | 3a9b3ba082d50498751f6c144ef31dbefd7f0357 (diff) | |
| download | rails-796cab45561fce268aa74e6587cdb9cae3bb243e.tar.gz rails-796cab45561fce268aa74e6587cdb9cae3bb243e.tar.bz2 rails-796cab45561fce268aa74e6587cdb9cae3bb243e.zip | |
Reduce allocations when running AR callbacks.
Inspired by @tenderlove's work in
c363fff29f060e6a2effe1e4bb2c4dd4cd805d6e, this reduces the number of
strings allocated when running callbacks for ActiveRecord instances. I
measured that using this script:
```
require 'objspace'
require 'active_record'
require 'allocation_tracer'
ActiveRecord::Base.establish_connection adapter: "sqlite3",
                                        database: ":memory:"
ActiveRecord::Base.connection.instance_eval do
  create_table(:articles) { |t| t.string :name }
end
class Article < ActiveRecord::Base; end
a = Article.create name: "foo"
a = Article.find a.id
N = 10
result = ObjectSpace::AllocationTracer.trace do
  N.times { Article.find a.id }
end
result.sort.each do |k,v|
  p k => v
end
puts "total: #{result.values.map(&:first).inject(:+)}"
```
When I run this against master and this branch I get this output:
```
pete@balloon:~/projects/rails/activerecord$ git checkout master
M Gemfile
Switched to branch 'master'
pete@balloon:~/projects/rails/activerecord$ bundle exec ruby benchmark_allocation_with_callback_send.rb > allocations_before
pete@balloon:~/projects/rails/activerecord$ git checkout remove-dynamic-send-on-built-in-callbacks
M Gemfile
Switched to branch 'remove-dynamic-send-on-built-in-callbacks'
pete@balloon:~/projects/rails/activerecord$ bundle exec ruby benchmark_allocation_with_callback_send.rb > allocations_after
pete@balloon:~/projects/rails/activerecord$ diff allocations_before allocations_after
39d38
<
{["/home/pete/projects/rails/activesupport/lib/active_support/callbacks.rb",
81]=>[40, 0, 0, 0, 0, 0]}
42c41
< total: 630
---
> total: 590
```
In addition to this, there are two micro-optimizations present:
* Using `block.call if block` vs `yield if block_given?` when the block was being captured already.
```
pete@balloon:~/projects$ cat benchmark_block_call_vs_yield.rb
require 'benchmark/ips'
def block_capture_with_yield &block
  yield if block_given?
end
def block_capture_with_call &block
  block.call if block
end
def no_block_capture
  yield if block_given?
end
Benchmark.ips do |b|
  b.report("block_capture_with_yield") { block_capture_with_yield }
  b.report("block_capture_with_call") { block_capture_with_call }
  b.report("no_block_capture") { no_block_capture }
end
pete@balloon:~/projects$ ruby benchmark_block_call_vs_yield.rb
Calculating -------------------------------------
block_capture_with_yield
                        124979 i/100ms
block_capture_with_call
                        138340 i/100ms
    no_block_capture    136827 i/100ms
-------------------------------------------------
block_capture_with_yield
                      5703108.9 (±2.4%) i/s -   28495212 in   4.999368s
block_capture_with_call
                      6840730.5 (±3.6%) i/s -   34169980 in   5.002649s
    no_block_capture  5821141.4 (±2.8%) i/s -   29144151 in   5.010580s
```
* Defining and calling methods instead of using send.
```
pete@balloon:~/projects$ cat benchmark_method_call_vs_send.rb
require 'benchmark/ips'
class Foo
  def tacos
    nil
  end
end
my_foo = Foo.new
Benchmark.ips do |b|
  b.report('send') { my_foo.send('tacos') }
  b.report('call') { my_foo.tacos }
end
pete@balloon:~/projects$ ruby benchmark_method_call_vs_send.rb
Calculating -------------------------------------
                send     97736 i/100ms
                call    151142 i/100ms
-------------------------------------------------
                send  2683730.3 (±2.8%) i/s -   13487568 in   5.029763s
                call  8005963.9 (±2.7%) i/s -   40052630 in   5.006604s
```
The result of this is making typical ActiveRecord operations slightly faster:
https://gist.github.com/phiggins/e46e51dcc7edb45b5f98
Diffstat (limited to 'actionpack/lib/action_dispatch/testing/assertions/response.rb')
0 files changed, 0 insertions, 0 deletions
