Understanding Ruby and Rails: Benchmarking your Ruby scripts

This is article is part of my series Understanding Ruby and Rails. Please see the table of contents for the series to view the list of all posts.

RoboDomain makes intensive use of asyncronous operations. It performs more than 10.000 different kind of operations per hour and the only way to benchmark the application is to keep track of the elaboration time for every single job.

Calculating the elaboration time of code fragment is really straightforward. The most simple way is to log the initial timestamp in a temporary variable before the script starts, execute the script then log the final timestamp. If you subtract the start time from the end time you get the elaboration time.

Here's an example.

def operation
  s = Time.now

  # your script
  sleep(2)

  e = Time.now
  t = e - s
  sprintf("Elaboration Time: %f", t)
end

>> operation
=> "Elaboration Time: 2.000084"

You would probably agree with me that this code looks messy and far from being written in the Ruby way. In fact, this code would probably remember you the time you weren't a Ruby programmer…

In Ruby, we can take advantage of blocks to separate the real operation from the part of the code that actually logs elaboration time. With blocks, you would write something like this.

def operation
  # your script
  sleep(2)
end

def elaboration_time(&block)
  s = Time.now
  yield
  e = Time.now
  e - s
end

t = elaboration_time { operation }
sprintf("Elaboration Time: %f", t)

You might argue that this second solution is longer than the previous one and, in fact, it is. But it's also much more cleaner, readable and reusable. You decoupled the tracking script from the real operation and you can easily reuse the elaboration_time method to track any other operation.

But hey, the title of this post says Inside Ruby on Rails. Did you miss something?

Not really. What you probably didn't notice before is that Rails comes with a benchmark core extension built-in that extends the default benchmark Ruby library introducing the ms method. Unfortunately, the code doesn't appear to be documented in the Rails API so you need to browse the source code to read its implementation.

The Benchmark.ms method exposes the same functionality I show you before without the need of a new custom method.

def operation
  # your script
  sleep(2)
end

Benchmark.ms do
  operation
end
# => 2000.12302398682

Another benefit of using Benchmark.ms is that its implementation inherits all the compatibility constraints of Rails itself and you can be sure this method would at least be compatible with the same Ruby versions your Rails application is.