web-dev-qa-db-ja.com

`time`と` strace -c`が異なるのはなぜですか?

私の理解では、timeはシステムコールに費やされた合計時間を記録します。次に、timestrace -fcによって報告されるsys時間の累積合計が同じになると予想します。しかし、それらは大きく異なります(13.5対0.005)。何が起こっている?

# time php index.php >/dev/null

real  0m16.292s
user  0m2.728s
sys   0m13.548s

# strace -fc php index.php >/dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 83.72    0.000365           0     54845           munmap
 10.09    0.000044           0     36705           mmap
  6.19    0.000027           0     18249           madvise
  0.00    0.000000           0       289           read
  0.00    0.000000           0       119           write
  0.00    0.000000           0       118         3 open
  0.00    0.000000           0       118           close
  0.00    0.000000           0        23           stat

[ cut 0 duration syscalls for brevity ]

100.00    0.000436                110951        82 total

(これらのテストを約50回再実行しましたが、どちらも一貫した結果が得られます)

5
Willem

プロセスは、システムコールを実行せずに任意の時間を費やすことができます。

たとえば、_while :; do :; done_を実行するシェルは、システムコールを実行せず、sys CPU時間を費やさず、userCPU時間のみを費やす無限の時間を費やします。

_strace -c_は、システムコールごとにsysCPU時間をカウントしようとします。 nanosleep(1000000000)は、CPU時間は0ミリ秒近くかかりますが、実時間は1秒です。

_$ bash -c 'time strace -fc sleep 1'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  [...]
  0.00    0.000000           0         1           nanosleep
  [...]
100.00    0.000000                    33         3 total

real    0m1.006s
user    0m0.001s
sys     0m0.003s
_

Sys時間をstraceによって報告された時間と比較する方が理にかなっていますが、プロセスに割り当てられたすべてのsysCPU時間がこれらのsyscallに費やされているわけではないことに注意してください。たとえば、mmapされたデータにアクセスすると、プロセスはシステムコールを実行せずに多くのシステム時間を費やす可能性があります。

ここで、これらの多数のmmaps()呼び出しは、メモリを割り当てることである可能性があります。それらはいくつかのマッピングを更新するだけなので、瞬時に実行されます。しかし、それらに初めて書き込むときは、実際のメモリでそれらの書き込みをバックアップするためにシステム時間が必要になる場所です。

または、共有ライブラリのようなオブジェクトファイルをマップすることもできます(数がopen()sの数からそれほど遠くないため、これも可能性があります)。繰り返しになりますが、mmap()は高速ですが、後でメモリを読み取ることは、ページフォールトと、ディスクからデータを読み取る実際の時間も意味します。これは、システムコールには含まれません。

より基本的には、以前のバージョンの質問のように_time strace -f your-application_を実行すると、timeはコマンドとstraceの両方の時間を計測します。

straceは多くのオーバーヘッドを追加します。 stracedアプリケーションのシステムコールごとにいくつかのシステムコールを実行します。

やってる

_strace -fc time your-application
_

の代わりに

_time strace -fc your-application
_

より良い一致を与える可能性が高くなります。

しかし、私が見つけたのは、プロセスが子を待機するために行うwait*()システムコールの場合、straceはそれらのwait*()システムコールによって報告された時間をシステム時間としてカウントするということです。これは、子プロセス(少なくとも待機中のプロセス)の時間が数回カウントされることを意味します。 timeは子プロセスでcmdを実行し、それを待機するため、これは_strace -f time cmd_で重要です。

_$ strace -c time head -c 100M /dev/urandom > /dev/null
0.00user 0.76system 0:00.76elapsed 99%CPU (0avgtext+0avgdata 1796maxresident)k
0inputs+0outputs (0major+83minor)pagefaults 0swaps
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.762288      762288         1           wait4
  0.00    0.000000           0         1           read
  0.00    0.000000           0       112           write
  0.00    0.000000           0         2           open
  0.00    0.000000           0         2           close
  0.00    0.000000           0         2           fstat
  0.00    0.000000           0         6           mmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         1           brk
  0.00    0.000000           0         4           rt_sigaction
  0.00    0.000000           0         3         3 access
  0.00    0.000000           0         1           clone
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           Arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.762288                   142         3 total
_

timestraceは同じシステム時間(wait4()システムコールによって返される)を報告しますが、_-f_を使用します:

_$ strace -fc time head -c 100M /dev/urandom > /dev/null
strace: Process 2298 attached
0.01user 1.33system 0:01.91elapsed 69%CPU (0avgtext+0avgdata 1920maxresident)k
0inputs+0outputs (0major+84minor)pagefaults 0swaps
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 54.60    1.331335     1331335         1           wait4
 39.43    0.961497          75     12804           read
  5.94    0.144825           6     25711           write
  0.01    0.000148          11        13         6 open
  0.00    0.000104           8        13           mmap
  0.00    0.000094          19         5         3 execve
  0.00    0.000063           8         8           mprotect
  0.00    0.000050           6         9           close
  0.00    0.000041           7         6         6 access
  0.00    0.000037           5         7           fstat
  0.00    0.000031          16         2           munmap
  0.00    0.000030           8         4           brk
  0.00    0.000007           4         2           Arch_prctl
  0.00    0.000006           6         1         1 ioctl
  0.00    0.000000           0         4           rt_sigaction
  0.00    0.000000           0         1           clone
------ ----------- ----------- --------- --------- ----------------
100.00    2.438268                 38591        16 total
_

1.33 istimeが行う1つのwait4()システムコールによって報告される時間。これは、headtimeの子)のsys時間を報告します。

ただし、straceは、その上にhead孫のすべてのシステムコールのシステム時間を取得しようとします。これは、2回カウントされることを意味します(正確ではありません)。孫のそのストレーシングは、以前の0.76ではなく1.33を取得するため、ストレースされたプロセスに起因するオーバーヘッドが発生します。 strace、time、headを同じプロセッサで強制的に実行すると(_taskset 1_を使用)、これは大幅に削減されます。

6