私はCで時間を測定したいと思います、そして私はそれを理解するのに苦労しています、私が望むのはこのようなものです:
任意の助けをいただければ幸いです。
(私はmingwを使用してWindowsでコンパイルしています)
1マイクロ秒の解像度を提供する高解像度タイマーはシステム固有であるため、異なるOSプラットフォームでこれを実現するには、異なる方法を使用する必要があります。以下に説明する関数に基づいてクロスプラットフォームのC++タイマークラスを実装する次の記事をご覧ください。
Windows
Windows APIは、非常に高解像度のタイマー関数を提供します。QueryPerformanceCounter()
は現在の経過ティックを返し、QueryPerformanceFrequency()
は1秒あたりのティック数を返します。
例:
_#include <iostream>
#include <windows.h> // for Windows APIs
using namespace std;
int main()
{
LARGE_INTEGER frequency; // ticks per second
LARGE_INTEGER t1, t2; // ticks
double elapsedTime;
// get ticks per second
QueryPerformanceFrequency(&frequency);
// start timer
QueryPerformanceCounter(&t1);
// do something
// ...
// stop timer
QueryPerformanceCounter(&t2);
// compute and print the elapsed time in millisec
elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
cout << elapsedTime << " ms.\n";
return 0;
}
_
Linux、Unix、およびMac
UnixまたはLinuxベースのシステムでは、gettimeofday()
を使用できます。この関数は「sys/time.h」で宣言されています。
例:
_#include <iostream>
#include <sys/time.h> // for gettimeofday()
using namespace std;
int main()
{
struct timeval t1, t2;
double elapsedTime;
// start timer
gettimeofday(&t1, NULL);
// do something
// ...
// stop timer
gettimeofday(&t2, NULL);
// compute and print the elapsed time in millisec
elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0; // sec to ms
elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms
cout << elapsedTime << " ms.\n";
return 0;
}
_
上記の例は、C++でコンパイルする必要があることに注意してください。C++では、 mingw がサポートされています。
Linuxでは、 clock_gettime()
を使用できます。
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start); // get initial time-stamp
// ... do stuff ... //
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end); // get final time-stamp
double t_ns = (double)(end.tv_sec - start.tv_sec) * 1.0e9 +
(double)(end.tv_nsec - start.tv_nsec);
// subtract time-stamps and
// multiply to get elapsed
// time in ns
以下は、gettimeofday()システムコールに基づくタイマー管理用の汎用C関数のグループです。すべてのタイマープロパティは、1つのticktimer構造体に含まれています-必要な間隔、タイマーの初期化からの合計実行時間、呼び出したいコールバックへのポインター、コールバックが呼び出された回数。コールバック関数は次のようになります。
void your_timer_cb (struct ticktimer *t) {
/* do your stuff here */
}
タイマーを初期化して開始するには、ticktimer_init(your_timer、interval、TICKTIMER_RUN、your_timer_cb、0)を呼び出します。
プログラムのメインループでticktimer_tick(your_timer)を呼び出すと、コールバックを呼び出すのに適切な時間が経過したかどうかを判断します。
タイマーを停止するには、ticktimer_ctl(your_timer、TICKTIMER_STOP)を呼び出すだけです。
ticktimer.h:
#ifndef __TICKTIMER_H
#define __TICKTIMER_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#define TICKTIMER_STOP 0x00
#define TICKTIMER_UNCOMPENSATE 0x00
#define TICKTIMER_RUN 0x01
#define TICKTIMER_COMPENSATE 0x02
struct ticktimer {
u_int64_t tm_tick_interval;
u_int64_t tm_last_ticked;
u_int64_t tm_total;
unsigned ticks_total;
void (*tick)(struct ticktimer *);
unsigned char flags;
int id;
};
void ticktimer_init (struct ticktimer *, u_int64_t, unsigned char, void (*)(struct ticktimer *), int);
unsigned ticktimer_tick (struct ticktimer *);
void ticktimer_ctl (struct ticktimer *, unsigned char);
struct ticktimer *ticktimer_alloc (void);
void ticktimer_free (struct ticktimer *);
void ticktimer_tick_all (void);
#endif
ticktimer.c:
#include "ticktimer.h"
#define TIMER_COUNT 100
static struct ticktimer timers[TIMER_COUNT];
static struct timeval tm;
/*!
@brief
Initializes/sets the ticktimer struct.
@param timer
Pointer to ticktimer struct.
@param interval
Ticking interval in microseconds.
@param flags
Flag bitmask. Use TICKTIMER_RUN | TICKTIMER_COMPENSATE
to start a compensating timer; TICKTIMER_RUN to start
a normal uncompensating timer.
@param tick
Ticking callback function.
@param id
Timer ID. Useful if you want to distinguish different
timers within the same callback function.
*/
void ticktimer_init (struct ticktimer *timer, u_int64_t interval, unsigned char flags, void (*tick)(struct ticktimer *), int id) {
gettimeofday(&tm, NULL);
timer->tm_tick_interval = interval;
timer->tm_last_ticked = tm.tv_sec * 1000000 + tm.tv_usec;
timer->tm_total = 0;
timer->ticks_total = 0;
timer->tick = tick;
timer->flags = flags;
timer->id = id;
}
/*!
@brief
Checks the status of a ticktimer and performs a tick(s) if
necessary.
@param timer
Pointer to ticktimer struct.
@return
The number of times the timer was ticked.
*/
unsigned ticktimer_tick (struct ticktimer *timer) {
register typeof(timer->tm_tick_interval) now;
register typeof(timer->ticks_total) nticks, i;
if (timer->flags & TICKTIMER_RUN) {
gettimeofday(&tm, NULL);
now = tm.tv_sec * 1000000 + tm.tv_usec;
if (now >= timer->tm_last_ticked + timer->tm_tick_interval) {
timer->tm_total += now - timer->tm_last_ticked;
if (timer->flags & TICKTIMER_COMPENSATE) {
nticks = (now - timer->tm_last_ticked) / timer->tm_tick_interval;
timer->tm_last_ticked = now - ((now - timer->tm_last_ticked) % timer->tm_tick_interval);
for (i = 0; i < nticks; i++) {
timer->tick(timer);
timer->ticks_total++;
if (timer->tick == NULL) {
break;
}
}
return nticks;
} else {
timer->tm_last_ticked = now;
timer->tick(timer);
timer->ticks_total++;
return 1;
}
}
}
return 0;
}
/*!
@brief
Controls the behaviour of a ticktimer.
@param timer
Pointer to ticktimer struct.
@param flags
Flag bitmask.
*/
inline void ticktimer_ctl (struct ticktimer *timer, unsigned char flags) {
timer->flags = flags;
}
/*!
@brief
Allocates a ticktimer struct from an internal
statically allocated list.
@return
Pointer to the newly allocated ticktimer struct
or NULL when no more space is available.
*/
struct ticktimer *ticktimer_alloc (void) {
register int i;
for (i = 0; i < TIMER_COUNT; i++) {
if (timers[i].tick == NULL) {
return timers + i;
}
}
return NULL;
}
/*!
@brief
Marks a previously allocated ticktimer struct as free.
@param timer
Pointer to ticktimer struct, usually returned by
ticktimer_alloc().
*/
inline void ticktimer_free (struct ticktimer *timer) {
timer->tick = NULL;
}
/*!
@brief
Checks the status of all allocated timers from the
internal list and performs ticks where necessary.
@note
Should be called in the main loop.
*/
inline void ticktimer_tick_all (void) {
register int i;
for (i = 0; i < TIMER_COUNT; i++) {
if (timers[i].tick != NULL) {
ticktimer_tick(timers + i);
}
}
}
簡単なパフォーマンスプロファイリング(手動タイマーを使用)を行うために作成したヘッダーファイルを次に示します。
_#ifndef __ZENTIMER_H__
#define __ZENTIMER_H__
#ifdef ENABLE_ZENTIMER
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#Elif HAVE_INTTYPES_H
#include <inttypes.h>
#else
typedef unsigned char uint8_t;
typedef unsigned long int uint32_t;
typedef unsigned long long uint64_t;
#endif
#ifdef __cplusplus
extern "C" {
#pragma }
#endif /* __cplusplus */
#define ZTIME_USEC_PER_SEC 1000000
/* ztime_t represents usec */
typedef uint64_t ztime_t;
#ifdef WIN32
static uint64_t ztimer_freq = 0;
#endif
static void
ztime (ztime_t *ztimep)
{
#ifdef WIN32
QueryPerformanceCounter ((LARGE_INTEGER *) ztimep);
#else
struct timeval tv;
gettimeofday (&tv, NULL);
*ztimep = ((uint64_t) tv.tv_sec * ZTIME_USEC_PER_SEC) + tv.tv_usec;
#endif
}
enum {
ZTIMER_INACTIVE = 0,
ZTIMER_ACTIVE = (1 << 0),
ZTIMER_PAUSED = (1 << 1),
};
typedef struct {
ztime_t start;
ztime_t stop;
int state;
} ztimer_t;
#define ZTIMER_INITIALIZER { 0, 0, 0 }
/* default timer */
static ztimer_t __ztimer = ZTIMER_INITIALIZER;
static void
ZenTimerStart (ztimer_t *ztimer)
{
ztimer = ztimer ? ztimer : &__ztimer;
ztimer->state = ZTIMER_ACTIVE;
ztime (&ztimer->start);
}
static void
ZenTimerStop (ztimer_t *ztimer)
{
ztimer = ztimer ? ztimer : &__ztimer;
ztime (&ztimer->stop);
ztimer->state = ZTIMER_INACTIVE;
}
static void
ZenTimerPause (ztimer_t *ztimer)
{
ztimer = ztimer ? ztimer : &__ztimer;
ztime (&ztimer->stop);
ztimer->state |= ZTIMER_PAUSED;
}
static void
ZenTimerResume (ztimer_t *ztimer)
{
ztime_t now, delta;
ztimer = ztimer ? ztimer : &__ztimer;
/* unpause */
ztimer->state &= ~ZTIMER_PAUSED;
ztime (&now);
/* calculate time since paused */
delta = now - ztimer->stop;
/* adjust start time to account for time elapsed since paused */
ztimer->start += delta;
}
static double
ZenTimerElapsed (ztimer_t *ztimer, uint64_t *usec)
{
#ifdef WIN32
static uint64_t freq = 0;
ztime_t delta, stop;
if (freq == 0)
QueryPerformanceFrequency ((LARGE_INTEGER *) &freq);
#else
#define freq ZTIME_USEC_PER_SEC
ztime_t delta, stop;
#endif
ztimer = ztimer ? ztimer : &__ztimer;
if (ztimer->state != ZTIMER_ACTIVE)
stop = ztimer->stop;
else
ztime (&stop);
delta = stop - ztimer->start;
if (usec != NULL)
*usec = (uint64_t) (delta * ((double) ZTIME_USEC_PER_SEC / (double) freq));
return (double) delta / (double) freq;
}
static void
ZenTimerReport (ztimer_t *ztimer, const char *oper)
{
fprintf (stderr, "ZenTimer: %s took %.6f seconds\n", oper, ZenTimerElapsed (ztimer, NULL));
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#else /* ! ENABLE_ZENTIMER */
#define ZenTimerStart(ztimerp)
#define ZenTimerStop(ztimerp)
#define ZenTimerPause(ztimerp)
#define ZenTimerResume(ztimerp)
#define ZenTimerElapsed(ztimerp, usec)
#define ZenTimerReport(ztimerp, oper)
#endif /* ENABLE_ZENTIMER */
#endif /* __ZENTIMER_H__ */
_
ztime()
関数は、必要なメインロジックです。現在の時刻を取得し、マイクロ秒単位で測定される64ビットuintに保存します。その後、簡単な計算を行って経過時間を調べることができます。
ZenTimer*()
関数は、開始時間と終了時間を記録する単純なタイマー構造体_ztimer_t
_へのポインターを取るための単なるヘルパー関数です。 ZenTimerPause()
/ZenTimerResume()
関数を使用すると、たとえば、時間を計りたくないデバッグ情報を出力したい場合に、タイマーを一時停止および再開できます。
オリジナルのヘッダーファイルのコピーは、 http://www.gnome.org/~fejj/code/zentimer.h で見つけることができます。 <または何かのHTMLエスケープをアップします。 MIT/X11の下でライセンスされているので、あなたが行うプロジェクトに自由にコピーしてください。
Time.hライブラリを使用して、次のようなものを試してください。
long start_time, end_time, elapsed;
start_time = clock();
// Do something
end_time = clock();
elapsed = (end_time - start_time) / CLOCKS_PER_SEC * 1000;
Linuxシステムでサポートされている場合、clock_gettime(CLOCK_MONOTONIC)は、システムの日付の変更の影響を受けない高解像度タイマーである必要があります(例NTPデーモン)。
this one をご覧ください。ただし、正確な計算が必要な場合は、オペレーティングシステムで特定のライブラリを使用する必要があります。
X86 CPUタイムスタンプカウンターを使用するGNU/Linuxのソリューションは次のとおりです。
rdtsc.c:
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned long long int64;
static __inline__ int64 getticks(void)
{
unsigned a, d;
asm volatile("rdtsc" : "=a" (a), "=d" (d));
return (((int64)a) | (((int64)d) << 32));
}
int main(){
int64 tick,tick1;
unsigned time=0,ut,mt;
// ut is the divisor to give microseconds
// mt gives milliseconds
FILE *pf;
int i,r,l,n=0;
char s[100];
// time how long it takes to get the divisors, as a test
tick = getticks();
// get the divisors - todo: for max performance this can
// output a new binary or library with these values hardcoded
// for the relevant CPU - a kind-of ludicrous notion considering
// that this will only work on x86 compatible cpus anyways where
// performance is the least of your issues...
// ... curse of the Assembly coder ;-)
pf = fopen("/proc/cpuinfo","r");
do {
r=fscanf(pf,"%s",&s[0]);
if (r<0) {
n=5; break;
} else if (n==0) {
if (strcmp("MHz",s)==0) n=1;
} else if (n==1) {
if (strcmp(":",s)==0) n=2;
} else if (n==2) {
n=3;
};
} while (n<3);
fclose(pf);
l=strlen(s);
s[l-4]=s[l-3];
s[l-3]=s[l-2];
s[l-2]=s[l-1];
s[l-1]=(char)0;
mt=atoi(s);
s[l-4]=(char)0;
ut=atoi(s);
printf("%s Mhz - ut = %u, mt = %u // hardcode these for your a CPU-specific binary ;-)\n",s,ut,mt);
tick1 = getticks();
time = (unsigned)((tick1-tick)/ut);
printf("%u us\n",time);
// time the duration of sleep(1) - plus overheads ;-)
tick = getticks();
sleep(1);
tick1 = getticks();
time = (unsigned)((tick1-tick)/mt);
printf("%u ms\n",time);
return 0;
}
コンパイルして実行
$ gcc rdtsc.c -o rdtsc && ./rdtsc
CPUの正しい除数を/ proc/cpuinfoから読み取り、それを読み取るのにかかった時間をマイクロ秒で示し、sleep(1)を実行するのにかかる時間をミリ秒で示します。
.../proc/cpuinfoのMhz評価に常に小数点以下3桁が含まれると仮定します:-o