Introduce
Benchmarking in ruby is an essential tool for you to evaluate the hair of a code, a certain block of code in Ruby, from which can give other solutions to solve problems to increase the speed of the program. . In Ruby’s Standard Library, we provide a Benchmark module that can be used to measure the runtime of any block code. To use it, we just need:
1 2 3 | <span class="token keyword">require</span> <span class="token string">'benchmark'</span> <span class="token comment"># => true</span> |
Benchmarking a block code
To benchmarking a simple block code, we use the #measure
method
1 2 3 4 | puts <span class="token constant">Benchmark</span> <span class="token punctuation">.</span> measure <span class="token punctuation">{</span> <span class="token number">10</span> _000_000 <span class="token punctuation">.</span> times <span class="token punctuation">{</span> <span class="token builtin">Object</span> <span class="token punctuation">.</span> <span class="token keyword">new</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment"># user system total real</span> <span class="token comment"># => 1.280000 0.000000 1.280000 ( 1.283235)</span> |
We can see the result is returned in seconds, so the initialization of 10 million Objects takes just over a second.
If we want to print the result with a message, we can do the following:
1 2 3 4 5 | result <span class="token operator">=</span> <span class="token constant">Benchmark</span> <span class="token punctuation">.</span> realtime <span class="token punctuation">{</span> <span class="token number">10</span> _000_000 <span class="token punctuation">.</span> times <span class="token punctuation">{</span> <span class="token builtin">Object</span> <span class="token punctuation">.</span> <span class="token keyword">new</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> puts <span class="token string">"Creating ten million objects: <span class="token interpolation"><span class="token delimiter tag">#{</span> realtime <span class="token punctuation">.</span> <span class="token function">round</span> <span class="token punctuation">(</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token delimiter tag">}</span></span> s"</span> <span class="token comment"># => Creating ten million objects: 1.29s</span> |
Very simple is not it.
Comparison of code blocks
A problem is posed, there are always many different solutions, as is the code. So choosing the best, fast and accurate way is really a very difficult thing, especially for complex logic-processing blocks, then Benchmark is the tool used to compare the blockcode. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token comment"># DP version</span> <span class="token keyword">def</span> <span class="token function">fib_dp</span> <span class="token punctuation">(</span> n <span class="token punctuation">)</span> <span class="token punctuation">(</span> <span class="token number">2.</span> <span class="token punctuation">.</span> n <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">reduce</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token number">0</span> <span class="token punctuation">,</span> <span class="token number">1</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">|</span> m <span class="token operator">|</span> m <span class="token operator"><</span> <span class="token operator"><</span> m <span class="token punctuation">.</span> <span class="token function">last</span> <span class="token punctuation">(</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">reduce</span> <span class="token punctuation">(</span> <span class="token punctuation">:</span> <span class="token operator">+</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">[</span> n <span class="token punctuation">]</span> <span class="token keyword">end</span> <span class="token comment"># Recursive version</span> <span class="token keyword">def</span> <span class="token function">fib_rec</span> <span class="token punctuation">(</span> n <span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">0</span> <span class="token keyword">if</span> n <span class="token operator">==</span> <span class="token number">0</span> <span class="token keyword">return</span> <span class="token number">1</span> <span class="token keyword">if</span> n <span class="token operator">==</span> <span class="token number">1</span> <span class="token function">fib_rec</span> <span class="token punctuation">(</span> n <span class="token operator">-</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token function">fib_rec</span> <span class="token punctuation">(</span> n <span class="token operator">-</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token keyword">end</span> |
In the above two methods, for the second method, using recursive speed, it will be very slow if n is a big number, use the #bm
method to see the difference:
1 2 3 4 5 6 7 8 9 | <span class="token constant">Benchmark</span> <span class="token punctuation">.</span> <span class="token function">bm</span> <span class="token punctuation">(</span> <span class="token number">10</span> <span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span> x <span class="token operator">|</span> x <span class="token punctuation">.</span> <span class="token function">report</span> <span class="token punctuation">(</span> <span class="token string">'dp:'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fib_dp</span> <span class="token punctuation">(</span> <span class="token number">35</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> x <span class="token punctuation">.</span> <span class="token function">report</span> <span class="token punctuation">(</span> <span class="token string">'recursive:'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fib_rec</span> <span class="token punctuation">(</span> <span class="token number">35</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token comment"># user system total real</span> <span class="token comment"># dp: 0.000000 0.000000 0.000000 ( 0.000035)</span> <span class="token comment"># recursive: 1.680000 0.000000 1.680000 ( 1.671631)</span> |
The first argument of the #bm
method specifies the width of the label (dp, recursive), to align the displayed value to the left.
We can see that, for the #fib_rec
function to finish running, it takes more than 1.5 seconds, and for the #fib_dp
function, the execution time is insignificant, in fact, the #fib_dp
function can run very fast when Large n, we can clearly see the following:
1 2 3 4 | puts <span class="token constant">Benchmark</span> <span class="token punctuation">.</span> measure <span class="token punctuation">{</span> <span class="token function">fib_dp</span> <span class="token punctuation">(</span> <span class="token number">100</span> _000 <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment"># user system total real</span> <span class="token comment"># => 0.350000 0.000000 0.350000 ( 0.350249)</span> |
with n=100_000
, the #fib_dp
function ran out of 0.35 seconds while with n=35
, the #fib_rec
function had to run for more than 1.5 seconds.
Use benchmark-ips
benchmark-ips is a gem that gives us more features than the default Benchmark module. To use it, you need to install it and require into your program.
1 2 | <span class="token keyword">require</span> <span class="token string">'benchmark/ips'</span> |
Use the gem above to test the following methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token constant">Benchmark</span> <span class="token punctuation">.</span> ips <span class="token keyword">do</span> <span class="token operator">|</span> x <span class="token operator">|</span> x <span class="token punctuation">.</span> <span class="token function">report</span> <span class="token punctuation">(</span> <span class="token string">'dp: '</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fib_dp</span> <span class="token punctuation">(</span> <span class="token number">35</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> x <span class="token punctuation">.</span> <span class="token function">report</span> <span class="token punctuation">(</span> <span class="token string">'recursive: '</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fib_rec</span> <span class="token punctuation">(</span> <span class="token number">35</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> x <span class="token punctuation">.</span> compare <span class="token operator">!</span> <span class="token keyword">end</span> <span class="token comment"># Calculating -------------------------------------</span> <span class="token comment"># dp: 5.600k i/100ms</span> <span class="token comment"># recursive: 1.000 i/100ms</span> <span class="token comment"># -------------------------------------------------</span> <span class="token comment"># dp: 60.299k (± 2.0%) i/s - 302.400k</span> <span class="token comment"># recursive: 0.517 (± 0.0%) i/s - 3.000 in 5.800686s</span> <span class="token comment">#</span> <span class="token comment"># Comparison:</span> <span class="token comment"># dp: : 60299.5 i/s</span> <span class="token comment"># recursive: : 0.5 i/s - 116590.15x slower</span> |
The only difference is x.compare!
call x.compare!
at the end of the block. We can see from the gem benchmark / ips that many other information of the two methods are printed to the screen such as the number of iterations per second, the standard deviation and finally the comparison shows that the #fib_rec
method is not optimized. 100 000 times slower than the #fib_dp
method
Conclude
If you’ve ever wondered why Enumerable#each
faster than the for
loop or [].map.flatten
slower than [].flat_map
, you can go to fast-ruby there with the correct answers to the problem. above, we can see the application of Benchmark in comparing the speed of methods available in ruby, thereby considering the use of methods in a logical and scientific manner.
Finally, it can be said that the Benchmark module and benchmark-ips gem are really easy to use and are essential for a Ruby developer program to increase performance.