時刻取得関数

国立研究開発法人 情報通信研究機構 時空標準研究室
Last update: 2007/08/02

通常、C のプログラムで時刻情報を取得するには、gettimeofday() を用いるが、 システムコールはユーザプロセスとカーネルプロセスを切替えるオーバーヘッドを伴うため、 処理時間が長く、しかも変動する。 精密な時間計測には、この変動は致命的である。 しかし、Pentium II 以降のインテルプロセッサには、 Time Stamp Counter (PCC, Processor Cycle Counter とも呼ばれる)と呼ばれる64ビットのレジスタが設けられ、 RDTSC 命令 により Time Stamp Counter を読み出すことができるようになっている。 この、Time Stamp Counter は、CPU の1サイクル毎にカウントアップされ、 電源投入時に0にリセットされるため、 CPU の動作周波数の解像度で時を刻むことができる。

Linux 2.6 では、clock_gettime() も用いることができる。 clock_gettime() では、第一引数でクロックを指定することにより、 共通のインタフェースでREALTIMEクロック(システム時計)やPROCESS_CPUTIME(TSC)を使い分けることができる。 glibc-2.5 では、CLOCK_REALTIME の実体は gettimeofday() であり、一方、PROCESS_CPUTIME では RDTSC を用いている。

一方、通常の gettimeofday() で用いている struct timeval では、マイクロ秒までの解像度しか得られない。 しかも、RDTSC命令は、アセンブラにより、ユーザプロセス内で呼出すことができるため、変動も少ない。

このように優れたRDTSC であるが、 PCの起動時にゼロリセットされ、しかも温度補償されていない水晶の発振周波数を直接利用 しているため、 現実世界の時刻(JST:日本標準時あるいはUTC:協定世界時)との対応付けは困難である。 NICTでは、RDTSC をベースに、国際原子時(TAI)と同期させ、ユーザランドで時刻を取得できるnst_gettai()関数を 公開している。

ところで、近年のPC では、デュアルコアなどマルチCPUを使用している。 マルチCPUシステムでは、TSCレジスタはCPU毎に存在しており、同一時刻でもTSC毎に値が若干異っている。 また、省電力化に伴い、CPU動作周波数を動的に変化させる場合もあり、 その場合、TSC値と時刻は比例しなくなる。 このように、TSCを時刻源として利用することが困難な場合が増えてきているため、 HPET (High Precision Event Timer) の導入が進められている。 しかし、チップセットにあるHPETのカウンタをI/O命令で参照するため、 オーバヘッドが大きく、マイクロ秒以下の計測には適していない。

次に、これらの時刻取得関数の特徴をまとめる。
FunctionEPOCResolutionLatency (Overhead)SMP, Dynamic CPU clock
gettimeofday()UNIX time1 micro second1 micro second
clock_gettime(CLOCK_REALTIME)UNIX time1/HZ1 micro second
clock_gettime(CLOCK_MONOTONIC)Hardware Boot1/HZ1 micro second
clock_gettime(CLOCK_PROCESS_CPUTIME_ID)Process starts1 nano secondtens of nano second×
RDTSCHardware Boot1 / CPUclocktens of nano second×
nst_gettai()UNIX time1 nano secondtens of nano second×
HPETHardware Boottens of nano second0.5 micro second
なお、socket 入出力に関しては、 ioctl(SIOCGSTAMPNS) を使えば、カーネルでパケットを送受信したタイムスタンプ(timespec構造体)が得られる。

ioctl(netPath->eventSock, SIOCGSTAMPNS, &ts))

システムコールとRDTSC

gettimeofday() と RDTSC の所要時間を計測してみる。 所要時間の大きさも変動も、RDTSC の方が遥かに優れている。

高精度タイミング用システム設定

サブマイクロ秒のタイミング精度を必要とする場合の設定例。 ex. ASUS P5B-VM のBIOS設定で、
[Advanced]-[Jumperfree Configuration]-[Spread Spectrum]:        Disable
[Advanced]-[CPU Configuration]-[Intel(R) SpeedStep Technology]: Disable
また、linux カーネル設定で、
# CONFIG_SMP is not set
# CONFIG_CPU_FREQ is not set
などとして、SMPやCPU動作周波数の変動を抑える。

これらの設定は、処理能力や消費電力などに関しては性能を低下させるので、 サブマイクロ秒精度が必要な場合に限って設定しましょう。


Up