I tried some benchmarks with the following code. This is a rails app. Rails v.5.1.1 / Ruby v2.4.1
Originally the purpose is just to count the number of visits in any show
actions of some controllers like so:
include SessionCount
...
def show
...
@counter = increment_session_counter
...
Then I could display the counter in the relevant views.
So I made a concern with the appropriate code with both version of the default assignment I wanted to test:
module SessionCount
private
#Counter version A
def increment_session_counter_A
session[:counter] ||= 0
session[:counter] += 1
end
#Counter version B
def increment_session_counter_B
session[:counter] += 1
rescue
session[:counter] = 1
end
end
In order to test both versions of default assignment I changed my controllers code as following:
include SessionCount
...
def show
...
t0 = Time.now
1000000.times do
session[:counter] = 0; #initialization for normalization purpose
increment_session_counter_A
end
t1 = Time.now
puts "Elapsed time: #{((end_time - beginning_time)*1000).round} ms"
@counter = increment_session_counter_A
...
Remark: In that code the initialization is here in order to enforce the "happy path" (where the value is not nil). In real scenario this will occurs only the first time for a given user.
Here are the results:
I get an average of 3100 ms with version A (||=
operator).
I get an average of 2000 ms with version B (rescue
).
But the interesting part begins now..
In the previous code the code is executed following the "happy path" where no exception occurs..
So I changed the initialization I made for normalization purpose as following in order to enforce the "exception path":
1000000.times do
session[:counter] = nil; #initialization for normalization purpose
increment_session_counter_A
end
And here is the results:
I get an average of 3500 ms with version A (||=
operator).
I get an average of around 60 000 ms with version B (rescue
). Yes I tried a couple of times only..
So here I can conclude as spickermann said that exception handling is indeed quite expensive.
But I think that there are many cases where the first time initialization happen very rarely (like a blog where there is no post at the very beginning)..
In such situations there is no reason to test for nil
every time and it could be interesting to use the exception handling.
Am I wrong?
I don't want to nitpick about some ms.. Here I just want to know if this idiom makes sense as a design pattern. I see the difference of both of these versions like the difference between while... end
and do ... while
since the intent is not the same.