[linux]プロセスの消費リソース表示 その1

Linuxでプロセスの実行時間を計測するのによく使われるのはtimeコマンドだろう。
例えば、以下のようにすれば実際にコマンド実行に10秒かかり、sleepなのでプロセッサ時間をほとんど消費していないことがわかる。

$ time sleep 10

real    0m10.003s
user    0m0.002s
sys     0m0.001s

伝統的なUnixのtimeコマンドは秒までしか表示しないけど、秒以下が小数点3桁まで表示されているのはtimeがBashの組み込みコマンドでPOSIXよりも機能が拡張されているからだ。
試しにtypeでtimeを調べてみると以下のように表示される。

$ type time
time はシェルの予約語です

一方、whichでtimeコマンドを探すと以下となる。

$ which time
/usr/bin/time
※実際はLinuxのディストリビューションによってパスは違う可能性がある。

こっちのtimeはなんだろうか。

$ /usr/bin/time sort -r foo.txt
0.00user 0.00system 0:10.00elapsed 100%CPU (0avgtext+0avgdata 1028maxresident)k
0inputs+0outputs (0major+316minor)pagefaults 0swaps

こちらはGNU版のtimeコマンドでより詳細な情報を表示してくれる。
そのままだと表示が見やすいとは言えないが、-vオプションを付けるとさらに詳細な項目を並べて表示してくれる。

$ /usr/bin/time -v sort -r foo.txt

        Command being timed: "sort -r foo.txt"
        User time (seconds): 0.00
        System time (seconds): 0.00
        Percent of CPU this job got: 60%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1024
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 317
        Voluntary context switches: 1
        Involuntary context switches: 105
        Swaps: 0
        File system inputs: 8
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0

これでページフォールトの回数やIOの様子もお手軽に見ることができる。

※ちなみに1回めの-vなしのIO回数が0になっているのは-v付きを先にやってしまったためにfoo.txtがキャッシュに読み込まれてしまったからだと思う。。。
別のファイルで試したら、ちゃんと8inputsとかになった。

[C]LinuxでCプログラムの処理時間を計測する方法いくつか その3

移植性は低いかもしれないけど、最近のLinuxでは最もよさそうな関数が以下となる。
Linuxのバージョンや他のUnixシステムでは使えない可能性もある。

[clock_gettime関数]
#include <time.h>
int clock_gettime(clockid_t clk_id, struct timespec *tp);

この関数ではその1、その2と違い、clk_id引数でどういった時間を取得するかを指定する。
よく使いそうなのは以下だろう。

CLOCK_MONOTONIC:ある時点からの単調増加のクロック数。NTP等の時間調整の影響を受ける。
CLOCK_MONOTONIC_RAW:ある時点からの単調増加のクロック数。NTP等の時間調整の影響を受けない。
CLOCK_PROCESS_CPUTIME_ID: CPU による高分解能のプロセス毎のタイマ。

timespec構造体は秒とナノ秒を保持している。

例:
    struct timespec start;
    struct timespec end;

    clock_gettime(CLOCK_MONOTONIC, &start);
    // 計測したい処理...
    clock_gettime(CLOCK_MONOTONIC, &end);

    long long elapsed = 1000 * 1000 * 1000 * (end.tv_sec - start.tv_sec);
    elapsed += end.tv_nsec - start.tv_nsec;

[getrusage関数]
#include <sys/time.h>
#include <sys/resource.h>
int getrusage(int who, struct rusage *usage);

こちらは時間だけでなく、リソースの使用状況を取得する関数となる。
who引数で何のリソースの使用状況を取得するかを指定する。
whoに指定する値は以下がある。

RUSAGE_SELF:プロセスの消費リソース。
RUSAGE_CHILDREN:子プロセスまで含めた消費リソース。
RUSAGE_THREAD:スレッドの消費リソース。

rusage構造体はユーザCPU使用時間やシステムCPU使用時間以外にも様々なリソースの使用状況を保持している。

例:
    struct rusage start;
    struct rusage end;
    int i;

    getrusage(RUSAGE_SELF, &start);
    // 計測したい処理...
    getrusage(RUSAGE_SELF, &end);

    long long elapsed = 1000 * 1000 * (end.ru_utime.tv_sec - start.ru_utime.tv_sec);
    elapsed += end.ru_utime.tv_usec - start.ru_utime.tv_usec;

その1、その2と同様に消費したリソース量を計測したいのか、実時間を計測したいのかによって使い分ける必要があるだろう。

[C]LinuxでCプログラムの処理時間を計測する方法いくつか その2

今回はPOSIXで決められている関数を使う方法。

[times関数]
#include <sys/times.h>
clock_t times(struct tms *buf);

times関数は引数bufにプロセスが消費したクロック数を格納する。
tms構造体はユーザ時間やシステム時間、子プロセスが消費した時間をフィールドとして持っている。

1秒あたりのクロック数は以下で求められる。
sysconf(_SC_CLK_TCK);

例:
    struct tms start;
    struct tms end;
    long elapsed;

    times(&start);
    // 計測したい処理...
    times(&end);

    elapsed = (long)(end.tms_utime - start.tms_utime);

[gettimeofday関数]
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);

times関数がクロック数を取得するものであるのに対して、こちらはシステム時刻を取得する関数となる。
また、timeval構造体はマイクロ秒まで保持することができる。

例:
    struct timeval start;
    struct timeval end;
    struct timeval elapsed;
    long elapsedusec;

    gettimeofday(&start, NULL);
    // 計測したい処理...
    gettimeofday(&end, NULL);

    timersub(&end, &start, &elapsed);
    elapsedusec = 1000 * 1000 * elapsed.tv_sec + elapsed.tv_usec;

gettimeofdayはシステム時刻を返すため、計測中にシステム時刻が変更された場合は影響を受けてしまう。 また、timeval構造体はtimersubマクロで差分を取得することができる。

しかしながら、現在のLinuxでは今回紹介した関数よりもclock_gettimeの使用が推奨されているようだ。
http://linuxjm.osdn.jp/html/LDP_man-pages/man2/times.2.html
http://linuxjm.osdn.jp/html/LDP_man-pages/man2/gettimeofday.2.html