cgriego/active_attr — pull request #110 by jfredett (2012-09-11 17:47:24+00:00)
Title: Typecasting performance

First, a before optimization picture:

````

hread ID: 70222187027160
Total: 19.178977
Sort by: self_time

%self total self wait child calls name
15.15 6.31 2.90 0.00 3.41 217683 ActiveAttr::Typecasting#typecaster_for
12.04 2.31 2.31 0.00 0.00 1964597 Kernel#hash
6.75 12.25 1.30 0.00 10.95 217684 ActiveAttr::TypecastedAttributes#_attribute_typecaster
5.40 2.83 1.04 0.00 1.80 217684 ActiveAttr::Typecasting#typecast_attribute
4.33 16.53 0.83 0.00 15.70 217684 ActiveAttr::TypecastedAttributes#attribute
4.10 0.79 0.79 0.00 0.00 449968 ActiveAttr::Attributes::ClassMethods#attributes
4.10 0.79 0.79 0.00 0.00 445170 ActiveAttr::AttributeDefinition#[]
4.01 1.83 0.77 0.00 1.06 217684 ActiveAttr::TypecastedAttributes::ClassMethods#_attribute_type
3.80 1.25 0.73 0.00 0.52 215279 ActiveAttr::Typecasting::StringTypecaster#call
3.39 6.96 0.65 0.00 6.31 217683 ::Base(uuid: String)#typecaster_for
3.28 2.71 0.63 0.00 2.09 217684 ActiveAttr::TypecastedAttributes#_attribute_type
3.23 0.62 0.62 0.00 0.00 217684 ActiveAttr::Attributes#attribute
3.07 0.60 0.59 0.00 0.01 449307 Kernel#respond_to?
2.99 0.57 0.57 0.00 0.00 445170 Hash#[]

````

I redacted some names because this coms from a client project, we are seeding a database which (for various reasons) lives entirely in memory. The benchmarks are done by loading a partial section of this database, consisting of roughly 590 objects and associations. We were noticing a significant performance problem (and expect that it will result in a few pull requests). After profiling, we noticed the following code was underperforming:

```` ruby

49 def typecaster_for(type)
50 typecaster = {
51 BigDecimal => BigDecimalTypecaster,
52 Boolean => BooleanTypecaster,
53 Date => DateTypecaster,
54 DateTime => DateTimeTypecaster,
55 Float => FloatTypecaster,
56 Integer => IntegerTypecaster,
57 Object => ObjectTypecaster,
58 String => StringTypecaster,
59 }[type]
60
61 typecaster.new if typecaster
62 end

````

in `lib/active_attr/typecasting.rb`. This seemed like an easy fix, so we refactored it to make the hash it creates a constant (called `TYPECASTERS`), that gave:

````
Thread ID: 70343398218460
Total: 15.374124
Sort by: self_time

%self total self wait child calls name
8.27 8.76 1.27 0.00 7.49 217684 ActiveAttr::TypecastedAttributes#_attribute_typecaster
6.60 2.79 1.02 0.00 1.77 217684 ActiveAttr::Typecasting#typecast_attribute
5.86 2.90 0.90 0.00 2.00 217683 ActiveAttr::Typecasting#typecaster_for
5.28 12.94 0.81 0.00 12.13 217684 ActiveAttr::TypecastedAttributes#attribute
5.08 0.78 0.78 0.00 0.00 449968 ActiveAttr::Attributes::ClassMethods#attributes
4.96 1.81 0.76 0.00 1.04 217684 ActiveAttr::TypecastedAttributes::ClassMethods#_attribute_type
4.96 0.76 0.76 0.00 0.00 445170 ActiveAttr::AttributeDefinition#[]
4.75 1.24 0.73 0.00 0.51 215279 ActiveAttr::Typecasting::StringTypecaster#call
4.17 3.54 0.64 0.00 2.90 217683 ::Base(uuid: String)#typecaster_for
4.06 2.68 0.62 0.00 2.06 217684 ActiveAttr::TypecastedAttributes#_attribute_type
3.78 0.58 0.58 0.00 0.00 217684 ActiveAttr::Attributes#attribute
3.73 0.59 0.57 0.00 0.01 449307 Kernel#respond_to?
3.67 0.56 0.56 0.00 0.00 445170 Hash#[]

````

refactoring further, I realized that the hash is an unnecessary overhead, a switch statement would be a bit quicker, and (IMO) more readable. (But I'm also the kind of guy who thinks curly braces are pretty)

````

Thread ID: 70252658645720
Total: 15.139436
Sort by: self_time

%self total self wait child calls name
8.77 8.08 1.33 0.00 6.75 217684 ActiveAttr::TypecastedAttributes#_attribute_typecaster
6.98 2.91 1.06 0.00 1.85 217684 ActiveAttr::Typecasting#typecast_attribute
5.58 12.45 0.85 0.00 11.60 217684 ActiveAttr::TypecastedAttributes#attribute
5.35 1.96 0.81 0.00 1.15 217683 ActiveAttr::Typecasting#typecaster_for
5.34 0.81 0.81 0.00 0.00 449968 ActiveAttr::Attributes::ClassMethods#attributes
5.31 0.80 0.80 0.00 0.00 445170 ActiveAttr::AttributeDefinition#[]
5.28 1.89 0.80 0.00 1.09 217684 ActiveAttr::TypecastedAttributes::ClassMethods#_attribute_type
5.01 1.30 0.76 0.00 0.54 215279 ActiveAttr::Typecasting::StringTypecaster#call
4.44 2.64 0.67 0.00 1.96 217683 ::Base(uuid: String)#typecaster_for
4.28 2.80 0.65 0.00 2.15 217684 ActiveAttr::TypecastedAttributes#_attribute_type
4.10 0.62 0.62 0.00 0.00 217684 ActiveAttr::Attributes#attribute
3.94 0.60 0.60 0.00 0.00 445170 Hash#[]

````

0.2 seconds difference isn't all that much, so I tossed each optimization in separate commits, in case you prefer the other one.

All tests pass on both commits.