[Linux][C] プロセス/スレッドごとのCPU時間を取得する

調べる機会があったので、ちょっと詳しくまとめました。

# プロセスのCPU時間の取得
## clock_gettime (CLOCK_PROCESS_CPUTIME_ID)

```c:使用例
struct timespec ts;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)
```

## getrusage (RUSAGE_SELF)
呼び出したプロセスの資源使用量、 そのプロセス内の全スレッドが使用している資源の合計を返す。

```c:使用例
struct rusage usg;
getrusage(RUSAGE_THREAD, &usg);
```

# スレッドのCPU時間の取得

## clock_gettime (CLOCK_THREAD_CPUTIME_ID)

```c:使用例
struct timespec ts;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts)
```

上記の取得の場合は、各スレッド内で自身のCPU時間のみしか取得できない。
スレッド外部から所得する場合は以下の `pthread_getcpuclockid` と組み合わせて取得する。

## pthread_getcpuclockid + clock_gettime

```c:使用例
// スレッド作成
pthread_t thread;
pthread_create(&thread, NULL, thread_start, NULL);

clockid_t cid;
pthread_getcpuclockid(pthread_self(), &cid); // メインスレッドのclockidを取得する場合
pthread_getcpuclockid(thread, &cid); // 特定スレッドのclockidを取得する場合

struct timespec ts;
clock_gettime(cid, &ts)
```

## getrusage (RUSAGE_THREAD)
呼び出したスレッドに関する資源使用量の統計を返す

```c:使用例
struct rusage usg;
getrusage(RUSAGE_THREAD, &usg);
```

# 使用例
実装例です。下記のMan Pageのサンプルコードを若干修正したものになります。
[Man page of PTHREAD_GETCPUCLOCKID](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_getcpuclockid.3.html)
アトミック変数が使いたかったので(stdatomic.h)、C11でビルドする必要があります。

```c:実装例
// to use RUSAGE_THREAD
#define _GNU_SOURCE
#include <sys/resource.h>

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>

// C11 only
#include <stdatomic.h>

#define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)

#define handle_error_en(en, msg) \
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

_Atomic int thread_end_flag = ATOMIC_VAR_INIT(0);

static void
pclock(char *msg, clockid_t cid)
{
    struct timespec ts;

    printf("%s", msg);
    if (clock_gettime(cid, &ts) == -1)
        handle_error("clock_gettime");
    printf("%4ld.%03ld\n", ts.tv_sec, ts.tv_nsec / 1000000);
}

static void *
thread_start(void *arg)
{
    printf("Subthread starting infinite loop\n");

    for (;;)
    {
        if (thread_end_flag) break;
        continue;
    }

    pclock("SubThread CPU time: ", CLOCK_PROCESS_CPUTIME_ID);
    // rusage
    int s;
    struct rusage usg;
    s = getrusage(RUSAGE_THREAD, &usg);
    if (s != 0)
        handle_error_en(s, "getrusage(thread)");
    printf("getrusage(thread) : user ");
    printf("%4ld.%03ld, ", usg.ru_utime.tv_sec, usg.ru_utime.tv_usec / 1000);
    printf("system ");
    printf("%4ld.%03ld\n", usg.ru_stime.tv_sec, usg.ru_stime.tv_usec / 1000);
}

int
main(int argc, char *argv[])
{
    pthread_t thread;
    clockid_t cid;
    int j, s;

    s = pthread_create(&thread, NULL, thread_start, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_create");

    printf("Main thread sleeping\n");
    sleep(1);

    printf("Main thread consuming some CPU time...\n");
    for (j = 0; j < 2000000; j++)
        getppid();

    pclock("Process total CPU time: ", CLOCK_PROCESS_CPUTIME_ID);

    s = pthread_getcpuclockid(pthread_self(), &cid);
    if (s != 0)
        handle_error_en(s, "pthread_getcpuclockid");
    pclock("Main thread CPU time:   ", cid);

    /* The preceding 4 lines of code could have been replaced by:
       pclock("Main thread CPU time:   ", CLOCK_THREAD_CPUTIME_ID); */

    s = pthread_getcpuclockid(thread, &cid);
    if (s != 0)
        handle_error_en(s, "pthread_getcpuclockid");
    pclock("Subthread CPU time: 1    ", cid);

    // rusage
    struct rusage usg;
    s = getrusage(RUSAGE_SELF, &usg);
    if (s != 0)
        handle_error_en(s, "getrusage(process)");
    printf("getrusage(process) : user ");
    printf("%4ld.%03ld, ", usg.ru_utime.tv_sec, usg.ru_utime.tv_usec / 1000);
    printf("system ");
    printf("%4ld.%03ld\n", usg.ru_stime.tv_sec, usg.ru_stime.tv_usec / 1000);

    thread_end_flag = 1;
    pthread_join(thread, NULL);
}
```

```console:実行
koara@ubuntu:~/work$ gcc -pthread -std=c11 test.c 
koara@ubuntu:~/work$ ./a.out 
Main thread sleeping
Subthread starting infinite loop
Main thread consuming some CPU time...
Process total CPU time:    1.143
Main thread CPU time:      0.078
Subthread CPU time: 1       1.064
getrusage(process) : user    1.076, system    0.064
SubThread CPU time:    1.143
getrusage(thread) : user    1.060, system    0.004
```

※1000で割ってたりしてるのは、Man Pageのサンプルに合わせたからです。桁落としをしてるっぽいです。

# 参考
[Man page of CLOCK_GETRES](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/clock_gettime.2.html)
[Man page of PTHREAD_GETCPUCLOCKID](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_getcpuclockid.3.html)
[Man page of GETRUSAGE](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/getrusage.2.html)
[アトミック - C言語入門](http://kaworu.jpn.org/c/%E3%82%A2%E3%83%88%E3%83%9F%E3%83%83%E3%82%AF)
[atomic変数間のatomicなコピー - yohhoyの日記](http://d.hatena.ne.jp/yohhoy/20130423/p1)