Macのbashビルトインコマンドのtimeと/usr/bin/timeは別物
はじめに
あるコマンドの時間計測やリソース使用量を表示する time と言うコマンドがあります。
このコマンドを使い実行時のメモリー使用量を測ろうかと思ったところで問題が発生しました。
問題
Macでtimeコマンドを使おうとすると bashビルトインのコマンドが呼び出されてしまいリソース使用量は取得できないので、別途インストールが必要なのだと思って GNU timeを Homebrewでインストールしたが以下のバグが残ったままでした。
GNU Time にある壮大なバグ - Qiita
対応
そこで調べてみると bashビルトイン以外に timeコマンドがあることが分かりました。
/usr/bin/timeならばリソース使用量も表示できます。
$ type -a time time is a shell keyword time is /usr/bin/time
bashビルトインコマンドのヘルプはhelp
$ help time time: time [-p] PIPELINE Execute PIPELINE and print a summary of the real time, user CPU time, and system CPU time spent executing PIPELINE when it terminates. The return status is the return status of PIPELINE. The `-p' option prints the timing summary in a slightly different format. This uses the value of the TIMEFORMAT variable as the output format. times: times Print the accumulated user and system times for processes run from the shell.
ビルトインではない timeコマンドのヘルプはmanコマンドで確認できます。
BSD系の timeコマンドがベースなのは分かります。
(分岐したOS毎に出力やオプションが変化しているので確認が必要)
TIME(1) BSD General Commands Manual TIME(1) NAME time -- time command execution SYNOPSIS time [-lp] utility DESCRIPTION ...
確認
GNU timeでバグの確認方法で使用されていたコマンドにより、目的のメモリー使用量を測れることが確認できました。
maximum resident set sizeの値がバイト単位で出力されている。
$ /usr/bin/time -l perl -e '"X" x 400 x 1024 x 1024' 0.27 real 0.12 user 0.14 sys 421330944 maximum resident set size 0 average shared memory size 0 average unshared data size 0 average unshared stack size 103186 page reclaims 0 page faults 0 swaps 0 block input operations 0 block output operations 0 messages sent 0 messages received 0 signals received 0 voluntary context switches 315 involuntary context switches
おまけ
GNU timeの壮大なバグについては、各配布先環境でパッチが当てられています。
このmaximum resident set sizeが曲者で、この値の元はシステムコールのgetrusage(2)のru_maxrssの値から算出しています。
しかし、OS毎にru_maxrssの単位が変わります。
manコマンドで確認したところ
OS | 単位 |
OS X 10.11.3 | バイト |
FreeBSD 11.0 | キロバイト |
Linux 2.6.32~ | キロバイト |
そして元のコードでは、ページ単位として処理されている。
「(ru_maxrss * pagesize) / 1024」の計算を行いキロバイト単位の結果を得ている。
pagesizeが 4096(4K) なので4倍なる。
そしてHomebrewでインストールするGNU timeは、全くパッチの当たっていないGNU time ver 1.7をFreeBSDのftpサーバーから取得しているために壮大なバグが残ったままです。
…してHomebrewのgnu-timeフォーミュラにパッチを追加してプルリクエストしちゃったのですが、upstreamにイシュってと…
- OS毎(getrusage)に単位が違う値を返してくるgetrusage
- 元のコードは作成時のru_maxrssがページ単位だったのでコードは正しい
- 現在、各配布先で独自のパッチがある
こんな感じでバグとは言えないけど、OS毎の対応を行うなら新旧含めて、どこまで対応するのか難しい。
んで、結論は各環境でパッチによる対応が良いのでは...Red Hatとか別のパッチもあたってるし...
そして、これらを英語で説明できるほど達者じゃ無い...ノω≦)これが一番高いハードル。