I was chatting in #ruby-pro (on Freenode) with some folks, and the topic of memoization came up. raggi expressed his sentiment that
[the rails devs] over-memoize waaaay too much. some of these methods will be faster running than looking up from memoize. in the case of certain rails code, it seems to be applied to any method that is stable, regardless of complexity or input
apeiros commented that
it’s often overmedication too since a simple def foo; @foo ||= begin; ...longcalc...; end; end would suffice
I do believe that people should not employ memoization too loosely, since I think that if you have a performance problem, you should first try to address it directly rather than blindly treat the symptom simply by slapping a method with the Memoizable mixin.
I asked raggi for an example of the problem, some code that I could run some benchmarks on. He directed my attention to request.rb of the ActionController module. Strolling through the code, I saw a memoized method which did nothing but return a hash value. apeiros and I thought that it couldn’t possibly be faster to memoize code like that, so I set out to benchmark some code.
Benchmark results
- Memoizing this method made it 14.5% slower.
- Rails memoization was 57.8% slower than classic memoization (i.e.
@cached_content_length ||= @env['CONTENT_LENGTH'].to_i). Alternatively phrased: classic memoization was more than twice as fast as Rails’. - The very act of mixing in Memoizable (without memoizing any methods) incurred an 8.3% speed penalty on method execution.
Here are the benchmarking code and the full benchmark results. The code makes use of better-benchmark, my library for statistically correct benchmarking.
So, employ Memoizable judiciously. Look over your method and consider whether memoization will really improve speed or not. It’s not guaranteed to do so in all cases.
