irb の進化が止まらない。最も先進的な REPL は Ruby の irb かもしれない1。複数行編集,シンタックスハイライト,オートインデント,メソッド名補完は衝撃的だった。
さて,2020 年 12 月 20 日リリースの irb 1.2.8 で measure という機能が追加された。
これは式の評価にかかる時間を計測してくれるもの。
Ruby で 100 万番目の素数を表示するのは
require "prime"
p Prime.lazy.drop(999999).first
と書けばいい。答えは 15485863。
これがどのくらいの時間で実行されるのか知りたいとする。計測手段はいくつもあるが irb 上でサクッとできるなら,それはそれで嬉しい。
ではまず
gem update irb
で最新の irb をインストールしておこう。記事執筆時点(2020/12/23)での最新は 1.2.9 だ。
そして,
irb -r prime
で irb を起動する。
いや,もちろん
irb
で起動しておいて,
irb(main):001:0> require "prime"
とやってもいいのだが,面倒なので,使うライブラリーは -r
オプションに渡したほうがいいだろう。
そして,
irb(main):001:0> measure
と打つと,式の評価時間を計測するモード2になる・・・はずなのだが,実はバージョン 1.2.8, 1.2.9 では何らかの手違いにより,LoadError が出るようになっている3。
幸い,measure コマンドが使えるようにするためのプルリクエスト が @elim さんにより出され,既に取り込まれている。
(2020-12-25 追記)irb 1.3.0(2020-12-24 リリース)にめでたく反映された。これで measure
が使える。
現状では,仕方ないので
irb(main):001:0> IRB.conf[:MEASURE] = true
と打とう。こんなの覚えられないけど,覚えなくていい。次のバージョンでは measure
でいけるようになるはずだ。(→ 追記:なった)
こうすると,irb 上で式を評価するたび,いちいち時間を測ってくれる。
ではさっそく:
irb(main):002:0> Prime.lazy.drop(999999).first
processing time: 7.395823s
=> 15485863
7.4 秒くらいだった。
計測モードをやめるには
irb(main):003:0> IRB.conf[:MEASURE] = false
とする。
measure
コマンドが使えるようになったら,
irb(main):003:0> measure :off
でいけるようになるはずだ。
ところで,この手の速度(実行時間)計測では気をつけないといけないことがいろいろある。
irb の measure 機能は,デフォルトでは処理前後の時刻の差を取っているだけのようなので,CPU が他のことで忙しいときに計測すると大きめの値が出そう。
実行時間はさまざまな要因で揺れ動く。実際,さきの 100 万番目の素数は,もう一度測ったら 6.5 秒程度であった。
(2020-12-25 追記)なお,Prime はメモ化を使っているのか,irb を終了せずにもう一度やるとずっと短い時間で終了する。
計測結果が 7.395823s
などと出ていても,有効数字はそんなに無い。表示される桁数が多いのは単に Float をそのまんま表示しているだけ。
まあおそらく小数第二位の桁(0.01 の桁)には意味が無いだろう。
だから,
irb(main):004:0> 1+1
processing time: 0.000282s
=> 2
とやって出てくる「0.000282s」という数字には全く意味が無い。「ホトンドゼロデスネー」としか言えない。
このような〈軽すぎる式評価〉の実行時間は,多数回実行させた時間を繰り返し数で割って求める必要がある。そうなるとベンチマークテストライブラリーの出番なわけで,irb なんかでやることではなくなってしまうだろう。
なお,筆者は試してないが,計測・表示の処理はカスタマイズできるようだ。
ユーザーのホームディレクトリーの .irbrc というファイルに,
IRB.conf[:MEASURE_PROC][:CUSTOM] = proc { |context, code, line_no, &block|
# 云々
}
てな感じで記述すればいいらしい。
これで,例えば「結果表示は小数第一位までにするぞ」とかが可能になる。
具体的なことは irb のコミットメッセージをどうぞ:
https://github.com/ruby/irb/commit/3899eaf2e21fcb8b6c1ba3fdb1ac4ec8c9b4798f