C言語で現在時刻を取得する関数gettimeofday()が廃止予定となったのが POSIX.1-2008の話らしいので、もう10年も経っています。 しかしコンパイル時に警告が出るわけでもないため 古いコードの流用時につい直し忘れてしまうのです。 そこで戒めとして代替関数であるclock_gettime()の使い方についてメモを残しておきます。
clock_gettime() | gettimeofday() |
---|---|
|
|
clock_gettime()で現在時刻を表示
システム時刻を取得する場合はCLOCK_REALTIMEを指定します。
取得した.tv_sec
を文字列変換するのはこれまで通りlocaltime()とstrftime()です。
システム時刻なのでコマンドやNTPによって時間が遡ることもあります。
そういえばCentOS 7にはtimedatectlという時刻操作を統括するコマンドが増えていました。
#include <stdio.h>
#include <time.h>
void date_rfc3339(void)
{
char date[48];
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
struct tm local;
localtime_r(&now.tv_sec, &local);
size_t len = strftime(date, sizeof(date), "%FT%T", &local);
len += (size_t)snprintf(&date[len], sizeof(date) - len, ".%06ld", now.tv_nsec / 1000);
len += strftime(&date[len], sizeof(date) - len, "%z", &local);
date[len + 1] = '\0';
date[len - 0] = date[len - 1];
date[len - 1] = date[len - 2];
date[len - 2] = ':';
puts(date);
}
pthread_cond_timedwait()で待ち合わせを行う
引数にstruct timespec
をとる関数の1つに
pthread_cond_timedwait()があります。
これを使う際の注意点として、条件変数の
デフォルト属性がCLOCK_REALTIMEなので
CLOCK_MONOTONICに変更した方がよいです。
本当はCLOCK_MONOTONIC_RAWを指定したいのですけど、
pthread_condattr_setclock()にEINVALを返されました。残念。
pthread_condattr_t attr;
pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&cond, &attr);
pthread_condattr_destroy(&attr);
単純なスリープについても引数にstruct timespec
をとる関数
nanosleep()またはclock_nanosleep()を今後は使いましょう。
clock_gettime()で処理時間を測定
clock_gettime()では実時間の他に CLOCK_THREAD_CPUTIME_IDを指定して スレッドがCPUを使用した時間を取得することもできます。 測定を別のスレッドで行う場合はpthread_getcpuclockid()で スレッド識別子からクロック識別子を取得してそれを指定します。
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <errno.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
int primes[500];
int primes_num;
void *primes_generator(void *dmy)
{
int n, i;
struct timespec timeout = { .tv_nsec = 100000 };
nanosleep(&timeout, NULL);
primes[primes_num ++] = 2;
for( n = 3; primes_num < sizeof(primes) / sizeof(primes[0]); n += 2 )
{
for( i = 0; i < primes_num; i ++ )
if( 0 == n % primes[i] ) break;
if( i == primes_num )
{
primes[primes_num ++] = n;
if( 0 == primes_num % 100 )
pthread_cond_broadcast(&cond);
}
}
nanosleep(&timeout, NULL);
return NULL;
}
static inline void clocksub(struct timespec *a, struct timespec *b, struct timespec *result)
{
result->tv_sec = b->tv_sec - a->tv_sec;
result->tv_nsec = b->tv_nsec - a->tv_nsec;
if( result->tv_nsec < 0 )
{
result->tv_sec -= 1;
result->tv_nsec += 1000000000;
}
}
int main(int argc, char *argv[])
{
int ret;
pthread_mutex_init(&mutex, NULL);
pthread_condattr_t attr;
pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&cond, &attr);
pthread_condattr_destroy(&attr);
pthread_t primes_id;
pthread_create(&primes_id, NULL, primes_generator, NULL);
clockid_t primes_clock;
pthread_getcpuclockid(primes_id, &primes_clock);
struct timespec main_ts[3], primes_ts[3];
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &main_ts[0]);
clock_gettime(primes_clock, &primes_ts[0]);
do
{
date_rfc3339();
pthread_mutex_lock(&mutex);
struct timespec timeout;
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_nsec += 500000;
ret = pthread_cond_timedwait(&cond, &mutex, &timeout);
pthread_mutex_unlock(&mutex);
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &main_ts[1]);
clock_gettime(primes_clock, &primes_ts[1]);
clocksub(&main_ts[0], &main_ts[1], &main_ts[2]);
clocksub(&primes_ts[0], &primes_ts[1], &primes_ts[2]);
printf("%15s : %ld.%06ld\n", "main", main_ts[2].tv_sec, main_ts[2].tv_nsec / 1000);
printf("%15s : %ld.%06ld\n", "primes", primes_ts[2].tv_sec, primes_ts[2].tv_nsec / 1000);
main_ts[0] = main_ts[1];
primes_ts[0] = primes_ts[1];
} while( ETIMEDOUT != ret );
date_rfc3339();
pthread_join(primes_id, NULL);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
例として素数を求めるスレッドを作ってメインスレッドから測定してみました。 メインスレッドの方は待っているだけなのでCPUを使用していないことがわかります。
[ubichupas@localhost]$ gcc -Wall -o time_test time_test.c -lpthread [ubichupas@localhost]$ ./time_test 2018-05-06T15:59:18.705295+09:00 main : 0.000419 primes : 0.000348 2018-05-06T15:59:18.705801+09:00 main : 0.000085 primes : 0.000315 2018-05-06T15:59:18.706106+09:00 main : 0.000049 primes : 0.000441 2018-05-06T15:59:18.706548+09:00 main : 0.000104 primes : 0.000678 2018-05-06T15:59:18.707226+09:00 main : 0.000062 primes : 0.000641 2018-05-06T15:59:18.707869+09:00
最後にタイムゾーンに関する情報はグローバル変数で
printf("TZ=%s%+ld\n", tzname[0], timezone / 60 / 60);
な風に参照できるため、gettimeofday()はもう使用しません。
0 件のコメント: