Skip to content

Instantly share code, notes, and snippets.

@lovitt
Last active May 18, 2017 22:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lovitt/b5edf05d11ef2571582c32d7547c1243 to your computer and use it in GitHub Desktop.
Save lovitt/b5edf05d11ef2571582c32d7547c1243 to your computer and use it in GitHub Desktop.
Benchmark tests for three patches designed to speed up ActiveRecord instantiation
# Set up Rails environment
RAILS_VERSION = ENV["RAILS_VERSION"] || "5.1.1"
APPLY_PATCHES = RAILS_VERSION == "5.1.1"
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", RAILS_VERSION
gem "sqlite3"
gem "benchmark-ips"
end
require "active_support"
require "active_record"
# Monkey patch ActiveRecord to use optimizations when $patch == true
if APPLY_PATCHES
ActiveRecord::ModelSchema::ClassMethods.module_eval do
prepend(Module.new do
def column_defaults
if $patch
load_schema
@column_defaults ||= _default_attributes.to_hash
else
super
end
end
private
def reload_schema_from_cache
if $patch
@column_defaults = nil
end
super
end
end)
end
ActiveRecord::Inheritance::ClassMethods.module_eval do
prepend(Module.new do
def subclass_from_attributes(attrs)
if $patch
attrs = attrs.to_h if attrs.respond_to?(:permitted?)
if attrs.is_a?(Hash)
subclass_name = attrs[inheritance_column] || attrs[inheritance_column.to_sym]
if subclass_name.present?
find_sti_class(subclass_name)
end
end
else
super(attrs)
end
end
end)
end
ActiveRecord::AttributeSet.class_eval do
prepend(Module.new do
def deep_dup
if $patch
self.class.new(attributes.deep_dup)
else
super
end
end
end)
end
end
# Set up ActiveRecord models for benchmarking
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
def mass_create_attributes(t)
10.times do |i|
t.string "str_attribute_#{i}", :default => "default #{i}"
t.integer "int_attribute_#{i}", :default => i
end
end
ActiveRecord::Schema.define do
create_table :animals, force: true do |t|
t.string "name", :default => "Some Animal"
t.string "type"
mass_create_attributes(t)
end
create_table :entries, force: true do |t|
t.string "name", :default => "Some Entry"
mass_create_attributes(t)
end
end
class Animal < ActiveRecord::Base; end
class Dog < Animal; end
class Entry < ActiveRecord::Base; end
# For each benchmarking scenario, run compare normal behavior to patched
def scenario(name)
puts
puts " #{name} ".center(80, "=")
puts
Benchmark.ips do |x|
x.report("v#{RAILS_VERSION}") { $patch = false; yield }
if APPLY_PATCHES
x.report("patched") { $patch = true; yield }
x.compare!
end
end
end
# Benchmark various ActiveRecord instantiation scenarios
scenario("Instantiate STI base class with attributes") { Animal.new :name => "Some Animal" }
scenario("Instantiate STI base class without attributes") { Animal.new }
scenario("Instantiate STI subclass via type attribute") { Animal.new :type => "Dog" }
scenario("Instantiate STI subclass with attributes") { Dog.new :name => "Spot" }
scenario("Instantiate STI subclass without attributes") { Dog.new }
scenario("Instantiate Non-STI class with attributes") { Entry.new :name => "Some Entry" }
scenario("Instantiate Non-STI class without attributes") { Entry.new }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment