Optimised performance for attribute extraction

Disabled all instrumentation unless enabled explicitly
This commit is contained in:
Sam 2013-03-13 18:26:47 -07:00
parent ad886495a1
commit 31ba6fbb08
4 changed files with 95 additions and 6 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ spec/reports
test/tmp
test/version_tmp
tmp
*.swp

View File

@ -10,4 +10,9 @@ Rake::TestTask.new(:test) do |t|
t.verbose = true
end
desc 'Benchmark'
task :bench do
load 'bench/perf.rb'
end
task :default => :test

52
bench/perf.rb Normal file
View File

@ -0,0 +1,52 @@
require "rubygems"
require "bundler/setup"
require "active_model_serializers"
require "active_support/json"
require "benchmark"
class User < Struct.new(:id,:name,:age,:about)
include ActiveModel::SerializerSupport
def fast_hash
h = {
id: read_attribute_for_serialization(:id),
name: read_attribute_for_serialization(:name),
about: read_attribute_for_serialization(:about)
}
h[:age] = read_attribute_for_serialization(:age) if age > 18
h
end
end
class UserSerializer < ActiveModel::Serializer
attributes :id, :name, :age, :about
def include_age?
object.age > 18
end
end
u = User.new(1, "sam", 10, "about")
s = UserSerializer.new(u)
u2 = User.new(2, "sam", 20, "about")
s2 = UserSerializer.new(u2)
p s2.attributes
p s.attributes
n = 100000
Benchmark.bmbm {|x|
x.report("init") { n.times { UserSerializer.new(u) } }
x.report("fast_hash") { n.times { u.fast_hash } }
x.report("attributes") { n.times { UserSerializer.new(u).attributes } }
x.report("serializable_hash") { n.times { UserSerializer.new(u).serializable_hash } }
x.report("serializable_hash_with_instrumentation") { n.times { UserSerializer.new(u).serializable_hash_with_instrumentation } }
}

View File

@ -93,6 +93,11 @@ module ActiveModel
end
define_include_method attr
if self.method_defined? :_fast_attributes
undef :_fast_attributes
end
end
def associate(klass, attrs) #:nodoc:
@ -279,7 +284,15 @@ module ActiveModel
# Returns a hash representation of the serializable
# object without the root.
def serializable_hash
def serializable_hash_without_instrumentation
return nil if @object.nil?
@node = attributes
include_associations! if _embed
@node
end
def serializable_hash_with_instrumentation
return nil if @object.nil?
instrument(:serialize, :serializer => self.class.name) do
@node = attributes
@ -290,6 +303,18 @@ module ActiveModel
end
end
# disable all instrumentation on serializable_hash (performance will be better)
def self.disable_instrumentation!
alias_method :serializable_hash, :serializable_hash_without_instrumentation
end
# enable instrumentation for serializable_hash (performance may be impacted)
def self.enable_instrumentation!
alias_method :serializable_hash, :serializable_hash_with_instrumentation
end
disable_instrumentation!
def include_associations!
_associations.each_key do |name|
include!(name) if include?(name)
@ -378,13 +403,19 @@ module ActiveModel
# Returns a hash representation of the serializable
# object attributes.
def attributes
hash = {}
_fast_attributes
rescue NameError
method = "def _fast_attributes\n"
_attributes.each do |name,key|
hash[key] = read_attribute_for_serialization(name) if include?(name)
end
method << " h = {}\n"
hash
_attributes.each do |name,key|
method << " h[:#{key}] = read_attribute_for_serialization(:#{name}) if send #{INCLUDE_METHODS[name].inspect}\n"
end
method << " h\nend"
self.class.class_eval method
_fast_attributes
end
# Returns options[:scope]