可能であれば、100ナノ秒ぐらいの分解能を持つクロックを実装してみた。
POSIX版。
class nano_resolution_clock { public : using rep = std::chrono::nanoseconds::rep ; using period = std::chrono::nanoseconds::period ; using duration = std::chrono::nanoseconds ; using time_point = std::chrono::time_point< nano_resolution_clock > ; static const bool is_steady = true ; static time_point now() { timespec ts{} ; clock_gettime( CLOCK_MONOTONIC, &ts ) ; return time_point( duration( static_cast<rep>( ts.tv_sec ) * static_cast<rep>( 1000000000 ) + static_cast<rep>( ts.tv_nsec ) ) ) ; } } ;
Win32 API版のnowの実装(コンパイルすらしてない)
static time_point nano_resollution_clock::now() { LARGE_INTEGER counts ; QueryPerformanceCounter( &counts ) ; LARGE_INTEGER freq ; QueryPerformanceFrequency( &freq ) ; return time_point( duration( static_cast<rep>(counts.QuadPart) * ( static_cast<rep>(1000000000) / static_cast<rep>(freq.QuadPart) ) ) ) ; }
さて、実際の精度はどうかというと、まずナノ秒は無理である。現在の最新のIBM互換機で達成できる最高の精度は、せいぜい100ナノ秒といったところだろう。なぜかというと、精度は環境のハードウェアに左右されるからだ。
IBM互換機では、複数のタイマーハードウェアがある。最も精度が高いのは、Time Stamp Counter(TSC)である。TSCはCPUの専用のピンが割り当てられており、CPUクロックと同期してカウントされるので、当然、精度が高い。たとえば、1GHzのクロックで駆動されるCPUであれば、1ナノ秒の精度を持つ。ただし、現代では信用できない。なぜならば、モダンなCPUは、省電力や発熱を抑えるなどの理由から、クロックが可変になっているからだ。また、マルチプロセッサー環境(マルチコアではない)では、カウンターは共有されていないので、プロセスを実行する物理プロセッサーが途中で切り替わると、悲惨な結果になる。
次に精度が高く、現実的に理想のタイマーは、High Precision Event Timer(HPET)である。これは、IntelとMicrosoftが猛烈にプッシュした比較的新しいハードウェアだ。規格上、10MHz以上の周波数が保証されているので、100ナノ秒の精度が保証されていることになる。たとえば、私の使っているマザーボードに載っているHPETは、14318180Hzで駆動しているらしい。つまり、私のマザーボードに搭載されているHPETは、約70ナノ秒ぐらいの精度をもっていることになる。
次なるはACPI Power Management Timerである。これは、ACPIを実装しているマザーボードならば必ず搭載しているハードウェアである。ACPI Power management Timerのクロック周波数は規格により固定されていて、きっちり3579545 Hzである。つまり、約280ナノ秒の精度がある。
これより先は、まともな精度が望めない。たとえばProgrammable Interval Timer(PIT)があるが、この精度はマザーボードによりけりだ。たいてい1ミリ秒の精度があるが、マザーボードによっては、実質10ミリ秒程度の精度しかないこともある。Real Time Clock(RTC)もあるが、これはマザボに載っている、電源がなくても動くようバッテリー駆動されている時計だ。一応2Hzから8192Hzまでの範囲でプログラム可能だが、所詮はマザボの時計なのだから、精度は期待できない。
ちなみに、この知識はUnderstanding the Linux Kernelで得た。この本は非常に素晴らしいので万人におすすめできる。
4 comments:
細かいツッコミをすると、サンプリング定理より、
必要な精度の少なくとも2倍の周波数でサンプリングする必要があります。
つまり、100ナノ秒の精度で計測するには少なくとも200MHzのクロックが必要なわけです。
ああ、そうか。
サンプリング定理はこの場合の精度にも影響するのか。
考えたらそれもそうだ。
さらに細かいことですが、最近の x86/x64 プロセッサの TSC では constant TSC や invariant TSC がサポートされていて、プロセッサの実際の動作周波数とは別に (一定周期で) カウントが進みます。
マルチプロセッサの問題と移植性の問題を除けば、割と使いやすいタイマー源だと言えると思います。
興味深いですね。
もっと詳しく知りたいと思いました、
Post a Comment