StupidDog's blog

IT関連の手近な所で、疑問に思った事を調べた記録

Macのbashビルトインコマンドのtimeと/usr/bin/timeは別物

はじめに

あるコマンドの時間計測やリソース使用量を表示する time と言うコマンドがあります。
このコマンドを使い実行時のメモリー使用量を測ろうかと思ったところで問題が発生しました。

問題

Mactimeコマンドを使おうとすると bashビルトインのコマンドが呼び出されてしまいリソース使用量は取得できないので、別途インストールが必要なのだと思って GNU timeHomebrewでインストールしたが以下のバグが残ったままでした。
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.7FreeBSDftpサーバーから取得しているために壮大なバグが残ったままです。

…してHomebrewgnu-timeフォーミュラにパッチを追加してプルリクエストしちゃったのですが、upstreamにイシュってと…

  • OS毎(getrusage)に単位が違う値を返してくるgetrusage
  • 元のコードは作成時のru_maxrssがページ単位だったのでコードは正しい
  • 現在、各配布先で独自のパッチがある

こんな感じでバグとは言えないけど、OS毎の対応を行うなら新旧含めて、どこまで対応するのか難しい。
んで、結論は各環境でパッチによる対応が良いのでは...Red Hatとか別のパッチもあたってるし...
そして、これらを英語で説明できるほど達者じゃ無い...ノω≦)これが一番高いハードル。