mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Optimize ActiveResource::Base.new(attributes)
* Add performance benchmark similar to ActiveRecord * Lazily find_or_create_resource_for_collection to not incur the overhead for empty arrays and arrays of primatives * #duplicable? is faster than inline rescues when the object is not duplicable * Don't constantly raise and handle NameError, raising is expensive * Even when a resource is nested inside a module, always look inside the class first for the resource definition so we don't overwrite classes all the time Before: user system total real Model.new (instantiation) 0.120000 0.000000 0.120000 ( 0.119961) Nested::Model.new (instantiation) 0.150000 0.010000 0.160000 ( 0.151183) Model.new (setting attributes) 28.540000 0.680000 29.220000 ( 29.271775) Nested::Model.new (setting attributes) 29.740000 0.580000 30.320000 ( 30.486210) After: user system total real Model.new (instantiation) 0.120000 0.000000 0.120000 ( 0.121249) Nested::Model.new (instantiation) 0.150000 0.010000 0.160000 ( 0.152429) Model.new (setting attributes) 11.480000 0.170000 11.650000 ( 11.656163) Nested::Model.new (setting attributes) 11.510000 0.210000 11.720000 ( 11.724249)
This commit is contained in:
parent
31155eeb3c
commit
a962bfe472
2 changed files with 94 additions and 15 deletions
70
activeresource/examples/performance.rb
Normal file
70
activeresource/examples/performance.rb
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
require 'rubygems'
|
||||||
|
require 'active_resource'
|
||||||
|
require 'benchmark'
|
||||||
|
|
||||||
|
TIMES = (ENV['N'] || 10_000).to_i
|
||||||
|
|
||||||
|
# deep nested resource
|
||||||
|
attrs = {
|
||||||
|
:id => 1,
|
||||||
|
:name => 'Luis',
|
||||||
|
:age => 21,
|
||||||
|
:friends => [
|
||||||
|
{
|
||||||
|
:name => 'JK',
|
||||||
|
:age => 24,
|
||||||
|
:colors => ['red', 'green', 'blue'],
|
||||||
|
:brothers => [
|
||||||
|
{
|
||||||
|
:name => 'Mateo',
|
||||||
|
:age => 35,
|
||||||
|
:children => [{ :name => 'Edith', :age => 5 }, { :name => 'Martha', :age => 4 }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
:name => 'Felipe',
|
||||||
|
:age => 33,
|
||||||
|
:children => [{ :name => 'Bryan', :age => 1 }, { :name => 'Luke', :age => 0 }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
:name => 'Eduardo',
|
||||||
|
:age => 20,
|
||||||
|
:colors => [],
|
||||||
|
:brothers => [
|
||||||
|
{
|
||||||
|
:name => 'Sebas',
|
||||||
|
:age => 23,
|
||||||
|
:children => [{ :name => 'Andres', :age => 0 }, { :name => 'Jorge', :age => 2 }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
:name => 'Elsa',
|
||||||
|
:age => 19,
|
||||||
|
:children => [{ :name => 'Natacha', :age => 1 }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
:name => 'Milena',
|
||||||
|
:age => 16,
|
||||||
|
:children => []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
class Customer < ActiveResource::Base
|
||||||
|
self.site = "http://37s.sunrise.i:3000"
|
||||||
|
end
|
||||||
|
|
||||||
|
module Nested
|
||||||
|
class Customer < ActiveResource::Base
|
||||||
|
self.site = "http://37s.sunrise.i:3000"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Benchmark.bm(40) do |x|
|
||||||
|
x.report('Model.new (instantiation)') { TIMES.times { Customer.new } }
|
||||||
|
x.report('Nested::Model.new (instantiation)') { TIMES.times { Nested::Customer.new } }
|
||||||
|
x.report('Model.new (setting attributes)') { TIMES.times { Customer.new attrs } }
|
||||||
|
x.report('Nested::Model.new (setting attributes)') { TIMES.times { Nested::Customer.new attrs } }
|
||||||
|
end
|
|
@ -1239,9 +1239,10 @@ module ActiveResource
|
||||||
@attributes[key.to_s] =
|
@attributes[key.to_s] =
|
||||||
case value
|
case value
|
||||||
when Array
|
when Array
|
||||||
resource = find_or_create_resource_for_collection(key)
|
resource = nil
|
||||||
value.map do |attrs|
|
value.map do |attrs|
|
||||||
if attrs.is_a?(Hash)
|
if attrs.is_a?(Hash)
|
||||||
|
resource ||= find_or_create_resource_for_collection(key)
|
||||||
resource.new(attrs)
|
resource.new(attrs)
|
||||||
else
|
else
|
||||||
attrs.duplicable? ? attrs.dup : attrs
|
attrs.duplicable? ? attrs.dup : attrs
|
||||||
|
@ -1251,7 +1252,7 @@ module ActiveResource
|
||||||
resource = find_or_create_resource_for(key)
|
resource = find_or_create_resource_for(key)
|
||||||
resource.new(value)
|
resource.new(value)
|
||||||
else
|
else
|
||||||
value.dup rescue value
|
value.duplicable? ? value.dup : value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self
|
self
|
||||||
|
@ -1367,36 +1368,44 @@ module ActiveResource
|
||||||
end
|
end
|
||||||
|
|
||||||
# Tries to find a resource in a non empty list of nested modules
|
# Tries to find a resource in a non empty list of nested modules
|
||||||
# Raises a NameError if it was not found in any of the given nested modules
|
# if it fails, then the resource is created
|
||||||
def find_resource_in_modules(resource_name, module_names)
|
def find_or_create_resource_in_modules(resource_name, module_names)
|
||||||
receiver = Object
|
receiver = Object
|
||||||
namespaces = module_names[0, module_names.size-1].map do |module_name|
|
namespaces = module_names[0, module_names.size-1].map do |module_name|
|
||||||
receiver = receiver.const_get(module_name)
|
receiver = receiver.const_get(module_name)
|
||||||
end
|
end
|
||||||
const_args = RUBY_VERSION < "1.9" ? [resource_name] : [resource_name, false]
|
const_args = RUBY_VERSION < "1.9" ? [resource_name] : [resource_name, false]
|
||||||
if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(*const_args) }
|
if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(*const_args) }
|
||||||
return namespace.const_get(*const_args)
|
namespace.const_get(*const_args)
|
||||||
else
|
else
|
||||||
raise NameError
|
create_resource_for(resource_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Tries to find a resource for a given name; if it fails, then the resource is created
|
# Tries to find a resource for a given name; if it fails, then the resource is created
|
||||||
def find_or_create_resource_for(name)
|
def find_or_create_resource_for(name)
|
||||||
resource_name = name.to_s.camelize
|
resource_name = name.to_s.camelize
|
||||||
ancestors = self.class.name.split("::")
|
|
||||||
if ancestors.size > 1
|
|
||||||
find_resource_in_modules(resource_name, ancestors)
|
|
||||||
else
|
|
||||||
self.class.const_get(resource_name)
|
|
||||||
end
|
|
||||||
rescue NameError
|
|
||||||
const_args = RUBY_VERSION < "1.9" ? [resource_name] : [resource_name, false]
|
const_args = RUBY_VERSION < "1.9" ? [resource_name] : [resource_name, false]
|
||||||
if self.class.const_defined?(*const_args)
|
if self.class.const_defined?(*const_args)
|
||||||
resource = self.class.const_get(*const_args)
|
self.class.const_get(*const_args)
|
||||||
else
|
else
|
||||||
resource = self.class.const_set(resource_name, Class.new(ActiveResource::Base))
|
ancestors = self.class.name.split("::")
|
||||||
|
if ancestors.size > 1
|
||||||
|
find_or_create_resource_in_modules(resource_name, ancestors)
|
||||||
|
else
|
||||||
|
if Object.const_defined?(*const_args)
|
||||||
|
Object.const_get(*const_args)
|
||||||
|
else
|
||||||
|
create_resource_for(resource_name)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create and return a class definition for a resource inside the current resource
|
||||||
|
def create_resource_for(resource_name)
|
||||||
|
resource = self.class.const_set(resource_name, Class.new(ActiveResource::Base))
|
||||||
resource.prefix = self.class.prefix
|
resource.prefix = self.class.prefix
|
||||||
resource.site = self.class.site
|
resource.site = self.class.site
|
||||||
resource
|
resource
|
||||||
|
|
Loading…
Reference in a new issue