Rubyで特定の箇所の処理に要する時間を計測したい場合、これまでは
1
2
3
4
5
start_time = Time.now
# ・・・
# (計測対象の処理)
# ・・・
puts Time.now - start_time
なんてコードを書いたりしていたのですが、Rubyには標準モジュールとして benchmark が用意されており、これを使えば簡単に計測が可能なのでした。
複数の処理を一度に計測でき、余計な代入処理が無いのでコード的にも心理的にも(?)良い感じです。
複数の結果もそれぞれ任意のラベルを付けて、見やすい形で出力可能です。
検証環境
Ruby 2.7.1 / 2.1.1
テストコード
Array(配列)への要素の追加を1億回行った場合のメソッドによる処理時間の差を計測してみます。
- 処理a:
[].push(1)
を1億回 - 処理b:
[] << 1
を1億回 - 処理c:
[] = [] + [1]
を1億回
複数処理の比較も工夫するとすっきり書くことができます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# benchmark.rb
require 'benchmark'
n = 100000000
# "10" は結果出力のラベル幅を指定している
Benchmark.bm 10 do |r|
# 処理a(do-end)
## 結果出力の「ラベル」を文字列で指定する
r.report "Array#push" do
ary = []
n.times do
ary.push(1)
end
end
# 処理b(ブロック)
r.report("Array#<<") {
ary = []
n.times do
ary << 1
end
}
# 処理c(ブロック1行)
r.report("Array#+[]") { n.times do; ary = []; ary += [1]; end }
end
出力
実行時にラベル(”Array#push” 等の見出し部分)の幅を指定すると、結果出力の桁が揃うので見やすいです。
1
2
3
4
5
6
7
8
$ ruby -v
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]
$ ruby benchmark.rb
user system total real
Array#push 5.078125 1.000000 6.078125 ( 6.077756)
Array#<< 3.796875 1.046875 4.843750 ( 4.848366)
Array#+[] 10.171875 0.078125 10.250000 ( 10.241070)
旧いバージョンのRubyではパフォーマンスが低下するのも一目瞭然。
1
2
3
4
5
6
7
8
$ ruby -v
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ ruby benchmark.rb
user system total real
Array#push 6.640000 0.000000 6.640000 ( 6.629305)
Array#<< 5.300000 0.000000 5.300000 ( 5.305064)
Array#+[] 10.530000 0.000000 10.530000 ( 10.549397)
参考
- module Benchmark - Ruby 2.7.0 リファレンスマニュアル
- Rubyの文字列連結、最速は? - アクトインディ開発者ブログ
- Rubyで経過時間を「正確に」測定する方法(翻訳) - TechRacho
- 精密さが必要なら
Process.clock_gettime
で測ると良いらしい。
- 精密さが必要なら