Skip to content

Optimised performance for attribute extraction #233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 14, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ spec/reports
test/tmp
test/version_tmp
tmp
*.swp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha, this is great, I have it in my global .gitignore as well as have vim configured to save these files elsewhere. :)

5 changes: 5 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
43 changes: 43 additions & 0 deletions bench/perf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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)

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 } }
}


27 changes: 15 additions & 12 deletions lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def attribute(attr, options={})
end

define_include_method attr

end

def associate(klass, attrs) #:nodoc:
Expand Down Expand Up @@ -281,13 +282,9 @@ def as_json(options={})
# object without the root.
def serializable_hash
return nil if @object.nil?
instrument(:serialize, :serializer => self.class.name) do
@node = attributes
instrument :associations do
include_associations! if _embed
end
@node
end
@node = attributes
include_associations! if _embed
@node
end

def include_associations!
Expand Down Expand Up @@ -378,13 +375,19 @@ def merge_association(hash, key, serializables, unique_values)
# Returns a hash representation of the serializable
# object attributes.
def attributes
hash = {}
_fast_attributes
rescue NameError
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why rescue a NameError rather than a respond_to? check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

faster, cause the general case it will not get the NameError, NameError only happens on first call.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, just pushed the fix

method = "def _fast_attributes\n"

_attributes.each do |name,key|
hash[key] = read_attribute_for_serialization(name) if include?(name)
end
method << " h = {}\n"

_attributes.each do |name,key|
method << " h[:#{key}] = read_attribute_for_serialization(:#{name}) if send #{INCLUDE_METHODS[name].inspect}\n"
end
method << " h\nend"

hash
self.class.class_eval method
_fast_attributes
end

# Returns options[:scope]
Expand Down