avgtime - コマンドの実行時間を手軽に計測するユーティリティ
目的
コマンドの実行時間を手軽に測りたい。
- 処理時間が短い場合のために、コマンドを複数回まとめて実行した結果を計測する
- システム状態によるバラツキを考慮して、複数回の試行の平均を取る(その際、最大値と最小値を除く)
zsh で "time repeat ..." が測定できない
コマンドを複数回実行して時間を測るのだから、time + repeat でいけるだろうと試したところ、
% time repeat 100 ls ....
lsは100回実行されているが、timeの結果が出力されない。
repeatを外せば、timeを表示する。
% time ls ls 0.00s user 0.00s system 0% cpu 0.002 total
ちなみに手元の環境は zsh-4.2.0 だが、最新の zsh-4.2.7 でも同じ結果になる。
zshall(1)を調べると、
- time は引数に pipeline を取る
- pipeline とは、単独の simple command か、simple command を '|' か '|&' で接続したもの
- repeat は complex command に分類され、simple command ではない(他に if, for, while, until, case, select, sub-shell実行が該当)
となっており、"time repeat" というシーケンスは使えないようだ。
ということは、検索するか、自作するしか無い。
この手のちょっとしたツールは、探すよりも作った方が早い(経験則)。
スクリプトで作る
Rubyには、Process.time メソッドがある(http://www.ruby-lang.org/ja/man/html/Process.html#Process.2etimes:titie)。
これを利用して、script内でコマンドのloop実行、かつ複数回試行して平均を計算する、という内容にした。
#!/usr/bin/env ruby # count processor times of sub-command in average, without min and max. def die(msg) STDERR.puts msg exit 0 end ## try_times = ARGV.shift.to_i loop_count = ARGV.shift.to_i command = ARGV.join " " die "usage: #$0 try-times loop-count command..." end if ARGV.length<3 die "try-times must be greater than 2." if try_times<=2 die "loop-count must be greater than 0." if loop_count<=0 used_times = [] try_times.times{ |n| prev = Process.times loop_count.times{ system command } cur = Process.times passage = cur.cutime - prev.cutime + cur.cstime - prev.cstime STDERR.puts "try #{n+1}: #{passage}" used_times.push passage } max = used_times.max min = used_times.min total = 0 used_times.each{ |e| total += e } average = (total - max - min) / (try_times - 2) STDERR.puts "average: #{average}"
測定結果は、stderr に出すようにした(コマンド自体の出力と混乱しないように)。
% avgtime 5 1 sleep 1 >/dev/null try 1: 0.0 try 2: 0.0 try 3: 0.0 try 4: 0.0 try 5: 0.0 average: 0.0 % avgtime 5 1000 ls >/dev/null try 1: 1.91 try 2: 1.91 try 3: 1.92 try 4: 1.88 try 5: 1.88 average: 1.88666666666667 % avgtime 5 1000 'echo "" | gcc -c -x c -' try 1: 21.41 try 2: 21.39 try 3: 21.46 try 4: 21.43 try 5: 21.44 average: 21.4033333333333
おまけ:excel の TRIMMEAN関数
同じような処理をする関数が、エクセルにあるらしい(http://office.microsoft.com/ja-jp/excel/HP052093221041.aspx)。
意外なところで使えそうなので、備忘録。
追記: Benchmarkライブラリ
rubyの標準ライブラリに、Benchmarkクラスというものがあるのを知った(プログラミング言語 Ruby リファレンスマニュアル)。
まあ、似たようなことをしてくれるのだが、複数回試行して平均を出すというのは無いようなので(Benchmark.bmbmでリハーサル機能はあるが、別集計になる)、avgtimeもこのまま残しておこう。
ところでBenchmark::Tmsクラスは四則演算を定義してるが、Benchmark.bmbmのリハーサルでのtotalの積算にしか使ってないみたいだ。RDocの中で使用例として書いているぐらいか。
比較用メソッド('>'や'max'など)があれば、avgtimeと同じことをもっと簡単にできるのだがなあ。