LoginSignup
3
10

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-02-08

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

プロセスのCPU時間の取得

clock_gettime (CLOCK_PROCESS_CPUTIME_ID)

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

getrusage (RUSAGE_SELF)

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

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

スレッドのCPU時間の取得

clock_gettime (CLOCK_THREAD_CPUTIME_ID)

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

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

pthread_getcpuclockid + clock_gettime

使用例
// スレッド作成
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)

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

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

使用例

実装例です。下記のMan Pageのサンプルコードを若干修正したものになります。
Man page of PTHREAD_GETCPUCLOCKID
アトミック変数が使いたかったので(stdatomic.h)、C11でビルドする必要があります。

実装例
// 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);
}
実行
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
Man page of PTHREAD_GETCPUCLOCKID
Man page of GETRUSAGE
アトミック - C言語入門
atomic変数間のatomicなコピー - yohhoyの日記

3
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
10