timeのreal・user・sysの違い:経過時間とCPU時間

timeコマンドのreal、user、sysがそれぞれ何を数えるかをPOSIXの定義で整理し、user+sysがrealを超える理由、シェルのtimeと/usr/bin/timeの違いを説明します。

開発OSLinux

time でコマンドを計ると real、user、sys の3つの値が出ます。real だけで速さを判断したり、3つを足したりすると読み違えます。それぞれが何を数えるかは POSIX で決まっています。

time -p ./build.sh

3つの値の定義

POSIX の time ユーティリティでは、real はユーティリティを起動してから終了するまでの経過時間です。ウォールクロック時間とも呼ばれます。user はユーザー CPU 時間で、times() の tms_utime と tms_cutime の合計にあたります。sys はシステム CPU 時間で、tms_stime と tms_cstime の合計です。-p を付けると、秒単位で real、user、sys を1行ずつ出力します。

tms_utime はプロセス自身の命令の実行に使われた CPU 時間、tms_stime はシステムがそのプロセスのために使った CPU 時間です。Linux の getrusage(2) は同じ区別をユーザーモードとカーネルモードでの実行時間として説明しています。システムコールの処理でカーネルが使った時間は sys に入ります。

子プロセスは wait された後で加算される

tms_cutime と tms_cstime は子プロセスの分です。POSIX では、終了した子の時間は wait()、waitid()、waitpid() がその子のプロセス ID を返したときに親の値に含まれます。子が自分の子を待たなかった場合、その孫の時間は入りません。多数のプロセスを起動するスクリプトでも time で計れるのはこのためです。計測対象のユーティリティが待たなかったプロセスの CPU 時間は、その time の結果には含まれません。

user + sys と real を比べる

Linux の getrusage() に RUSAGE_SELF を渡すと、呼び出したプロセス内の全スレッドの使用量が合計されますが、子プロセスは含まれません。待機済みの子の使用量は RUSAGE_CHILDREN で別に集計され、time が使う子プロセス時間のフィールドに対応します。並列に動くスレッドと、time に含まれる待機済みの子プロセスは、それぞれ CPU 時間を積み上げます。そのため user + sys が real を上回ることがあります。GNU time の %P は (%U + %S) / %E で計算されるので、100% を超えることもあります。

逆に real が user + sys よりずっと大きければ、計測中の平均 CPU 使用量は1 CPU 未満だったことになります。ディスクやネットワーク、タイマー、CPU にスケジュールされるのを待っていた可能性がありますが、この3つの値だけでは原因を特定できません。スレッドや子プロセスが同時に動く処理では、real - (user + sys) をそのまま待ち時間とはみなせません。

シェルの time と /usr/bin/time

bash の time は予約語で、パイプライン全体やシェル関数も計れます。表示形式は TIMEFORMAT 変数で変えられます。GNU time が /usr/bin/time にインストールされている環境では、そのパスで別プログラムを実行でき、/usr/bin/time --version で実装を確認できます。GNU time は -v や書式指定子で、メジャー・マイナーページフォールト(%F、%R)や自発的・非自発的コンテキストスイッチ(%w、%c)も表示します。マニュアルにはシステムによって値が 0 になることがあると書かれており、Linux も管理していない rusage のフィールドは 0 にします。

標準と Linux の境目

POSIX が struct rusage に必須としているメンバーは ru_utime と ru_stime だけです。ページフォールトやコンテキストスイッチの回数は Linux などの実装が追加で埋める値なので、ほかの OS ではその OS の文書を確認してください。Linux のプログラム内で計るなら、clock_gettime(2) の CLOCK_PROCESS_CPUTIME_ID でプロセスの CPU 時間を、CLOCK_MONOTONIC で単調増加する経過時間を取れます。Linux の CLOCK_MONOTONIC はサスペンド中の時間を含まないため、その時間も数える必要があれば CLOCK_BOOTTIME を使います。CLOCK_REALTIME はシステム時刻の変更で値が飛ぶことがあり、所要時間の計測には通常向きません。

CPU 時間が短くても、待ち時間の原因までは分かりません。削除したファイルとディスク容量の関係は、この3つの値には直接出ない OS 動作の一例です。時刻とタイムスタンプは UNIX時間と2038年問題で説明しています。

ブログへ