提交 beb07fbf 编写于 作者: G Guo Xiang Tan

Revert "Revert "Reduce allocations when running AR callbacks.""

This reverts commit bdc1d329.

Before:
Calculating -------------------------------------
                        22.000  i/100ms
-------------------------------------------------
                        229.700  (± 0.4%) i/s -      1.166k
Total Allocated Object: 9939

After:
Calculating -------------------------------------
                        24.000  i/100ms
-------------------------------------------------
                        246.443  (± 0.8%) i/s -      1.248k
Total Allocated Object: 7939

```
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 'rails', github: 'rails/rails', ref: 'bdc1d329'
  gem 'rails', github: 'rails/rails', ref: 'd2876141'
  gem 'arel', github: 'rails/arel'
  gem 'sqlite3'
  gem 'benchmark-ips'
end

require 'active_record'
require 'benchmark/ips'

ActiveRecord::Base.establish_connection('sqlite3::memory:')

ActiveRecord::Migration.verbose = false

ActiveRecord::Schema.define do
  create_table :users, force: true do |t|
    t.string :name, :email
    t.boolean :admin
    t.timestamps null: false
  end
end

class User < ActiveRecord::Base
  default_scope { where(admin: true) }
end

admin = true

1000.times do
  attributes = {
    name: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
    email: "foobar@email.com",
    admin: admin
  }

  User.create!(attributes)

  admin = !admin
end

GC.disable

Benchmark.ips(5, 3) do |x|
  x.report { User.all.to_a }
end

key =
  if RUBY_VERSION < '2.2'
    :total_allocated_object
  else
    :total_allocated_objects
  end

before = GC.stat[key]
User.all.to_a
after = GC.stat[key]
puts "Total Allocated Object: #{after - before}"
```
上级 794252d7
......@@ -404,7 +404,7 @@ def validate!(context = nil)
protected
def run_validations! #:nodoc:
run_callbacks :validate
_run_validate_callbacks
errors.empty?
end
......
......@@ -109,7 +109,7 @@ def after_validation(*args, &block)
# Overwrite run validations to include callbacks.
def run_validations! #:nodoc:
run_callbacks(:validation) { super }
_run_validation_callbacks { super }
end
end
end
......
......@@ -133,7 +133,7 @@ def delete_records(records, method)
if scope.klass.primary_key
count = scope.destroy_all.length
else
scope.each { |record| record.run_callbacks :destroy }
scope.each(&:_run_destroy_callbacks)
arel = scope.arel
......
......@@ -289,24 +289,25 @@ module ClassMethods
end
def destroy #:nodoc:
run_callbacks(:destroy) { super }
_run_destroy_callbacks { super }
end
def touch(*) #:nodoc:
run_callbacks(:touch) { super }
_run_touch_callbacks { super }
end
private
def create_or_update(*) #:nodoc:
run_callbacks(:save) { super }
_run_save_callbacks { super }
end
def _create_record #:nodoc:
run_callbacks(:create) { super }
_run_create_callbacks { super }
end
def _update_record(*) #:nodoc:
run_callbacks(:update) { super }
_run_update_callbacks { super }
end
end
end
......@@ -508,7 +508,7 @@ def checkin(conn)
synchronize do
remove_connection_from_thread_cache conn
conn.run_callbacks :checkin do
conn._run_checkin_callbacks do
conn.expire
end
......@@ -764,7 +764,7 @@ def checkout_new_connection
end
def checkout_and_verify(c)
c.run_callbacks :checkout do
c._run_checkout_callbacks do
c.verify!
end
c
......
......@@ -303,7 +303,7 @@ def initialize(attributes = nil)
assign_attributes(attributes) if attributes
yield self if block_given?
run_callbacks :initialize
_run_initialize_callbacks
end
# Initialize an empty model object from +coder+. +coder+ should be
......@@ -330,8 +330,8 @@ def init_with(coder)
self.class.define_attribute_methods
run_callbacks :find
run_callbacks :initialize
_run_find_callbacks
_run_initialize_callbacks
self
end
......@@ -367,7 +367,7 @@ def initialize_dup(other) # :nodoc:
@attributes = @attributes.dup
@attributes.reset(self.class.primary_key)
run_callbacks(:initialize)
_run_initialize_callbacks
@new_record = true
@destroyed = false
......
......@@ -319,8 +319,8 @@ def rollback_active_record_state!
end
def before_committed! # :nodoc:
run_callbacks :before_commit_without_transaction_enrollment
run_callbacks :before_commit
_run_before_commit_without_transaction_enrollment_callbacks
_run_before_commit_callbacks
end
# Call the +after_commit+ callbacks.
......@@ -329,8 +329,8 @@ def before_committed! # :nodoc:
# but call it after the commit of a destroyed object.
def committed!(should_run_callbacks: true) #:nodoc:
if should_run_callbacks && destroyed? || persisted?
run_callbacks :commit_without_transaction_enrollment
run_callbacks :commit
_run_commit_without_transaction_enrollment_callbacks
_run_commit_callbacks
end
ensure
force_clear_transaction_record_state
......@@ -340,8 +340,8 @@ def committed!(should_run_callbacks: true) #:nodoc:
# state should be rolled back to the beginning or just to the last savepoint.
def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc:
if should_run_callbacks
run_callbacks :rollback
run_callbacks :rollback_without_transaction_enrollment
_run_rollback_callbacks
_run_rollback_without_transaction_enrollment_callbacks
end
ensure
restore_transaction_record_state(force_restore_state)
......
......@@ -80,8 +80,12 @@ module Callbacks
# save
# end
def run_callbacks(kind, &block)
callbacks = send("_#{kind}_callbacks")
send "_run_#{kind}_callbacks", &block
end
private
def __run_callbacks__(callbacks, &block)
if callbacks.empty?
yield if block_given?
else
......@@ -91,8 +95,6 @@ def run_callbacks(kind, &block)
end
end
private
# A hook invoked every time a before callback is halted.
# This can be overridden in AS::Callback implementors in order
# to provide better debugging/logging.
......@@ -806,6 +808,12 @@ def define_callbacks(*names)
names.each do |name|
class_attribute "_#{name}_callbacks"
set_callbacks name, CallbackChain.new(name, options)
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def _run_#{name}_callbacks(&block)
__run_callbacks__(_#{name}_callbacks, &block)
end
RUBY
end
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册