When coming to a speed of Ruby applications, the first thing we going to do is to identify application’s bottlenecks. Usually, such a task is not imaginable without a benchmarking tool. In this article, I intend to make an overview of typical tools for simplest ruby benchmarking tasks.
The first and most obvious tool is
benchmark available from within Ruby’s standard library. Let’s try it to see performance
shuffle methods to shuffle the elements of an array of 1_000_000 elements.
As we can see, with this amount of data shuffling via
Array#shuffle is about 30 times more efficient than the using
Another example will involve the performance comparison between
Regexp#match to assert whether the given
string includes the provided substring or not.
This example shows that String#include? is about 2.6 times more performant than Regexp.match in this particular case. But that is not very precise measurement. If we try to wrap our code blocks into loops, we would get different results everytime we run the benchmark:
You can try this yourself,
include? would always win, but the numbers will differ significantly between the benchmarks.
So, to make precise measurements, we have to choose the number of iterations.
Fortunately, the Ruby ecosystem has another tool, that makes this choice for us - it is the evanphx/benchmark-ips, which is the
enhanced version of benchmark.
Let’s try our previous example with
This kind of benchmark operates with
iterations per second measurement and therefore its output shows the difference
between compared code blocks in a more clear and understandable way. Obviously, this tool is more popular for these reasons.
Let’s compare the performance difference between Struct, OpenStruct, PORO, and a Hash using
benchmark/ips, which now will
help us to build a really fancy benchmark report.
This script produces a very descriptive report about the performance of each data structure in this simple scenario. As you may see from its output(obviously, truncated), the OpenStruct is really a performance destroyer, and Struct is as performant as the Plain Old Ruby Object, when it comes to reading or initialization.
Initialization Comparison: Struct: 5758806.7 i/s PORO: 5598489.7 i/s - same-ish: difference falls within error Hash: 3143382.6 i/s - 1.83x slower ostruct: 1150810.4 i/s - 5.00x slower Reading Comparison: PORO: 13365686.7 i/s Hash#: 12988414.6 i/s - same-ish: difference falls within error Struct: 12721531.5 i/s - same-ish: difference falls within error Hash#fetch: 10978007.0 i/s - 1.22x slower ostruct: 7877572.2 i/s - 1.70x slower Writing Comparison: PORO: 12676271.4 i/s Hash#: 10976062.0 i/s - 1.15x slower Struct: 10609637.3 i/s - 1.19x slower ostruct: 5380540.4 i/s - 2.36x slower