Reputation: 12412
This question is mostly about Ruby internals, the speed can be gauged with a simple benchmark.
What's the most efficient way to memoize a return value in ruby?
I've always memoized values with:
def method
@value ||= calculate_value
end
But since it technically expands to:
@value = @value || calculate_value
I wonder about the efficiency of re-executing the same assignment each time.
Would this be better?
def method
@value ? @value : (@value = calculate_value)
end
Also, does it change in different interpreters? MRI, Rubinius, etc.
Upvotes: 7
Views: 495
Reputation: 168121
I think any differences between ||=
and ... = ... || ...
should be subtle.
Perhaps, a much more efficient way is to switch the method when the value is set. This switching happens only once, and from there on, the method call becomes just a reference to the variable, so it should be fast, and the advantage increases as the number of times the method is called increases.
def method
alias method memoized_value
@value ||= calculate_value
end
def memoized_value; @value end
This assumes that calculate_value
always returns a truthy value, and there is no other part in the code that modifies the value so that once method
is called, the variable's value remains truthy.
Upvotes: 0
Reputation: 389
@spickermann, @tompave, sorry for posting it as an answer but I need code formatting to post benchmark results for different usecases. It looks like explicit way is the fastest @a || @a = calculated_value
> n = 10000000;
> a = 1;
> puts Benchmark.measure { n.times { a ||= 1 } }
0.570000 0.000000 0.570000 ( 0.569977)
> puts Benchmark.measure { n.times { a ? a : a = 1 } }
0.560000 0.000000 0.560000 ( 0.562855)
> puts Benchmark.measure { n.times { a || a = 1 } }
0.530000 0.000000 0.530000 ( 0.532715)
> @a = 1;
> puts Benchmark.measure { n.times { @a ||= 1 } }
0.870000 0.000000 0.870000 ( 0.869489)
> puts Benchmark.measure { n.times { @a ? @a : @a = 1 } }
0.670000 0.000000 0.670000 ( 0.668910)
> puts Benchmark.measure { n.times { @a || @a = 1 } }
0.610000 0.000000 0.610000 ( 0.613978)
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]
All 3 notations behave the same from result point of view, however last one is the fasted one from performance point of view. Unfortunately it is all empirical and I can't link any ruby source code here.
Upvotes: 0
Reputation: 106932
Your example
@value ||= calculate_value
is equivalent to
@value || @value = calculate_value
and is not equivalent to
@value = @value || calculate_value
Therefore the answer is: It is very efficient. It is not re-assigned each time.
Upvotes: 9