2012-05-25

100ナノ秒ぐらいの分解能をもつクロック実装

可能であれば、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で得た。この本は非常に素晴らしいので万人におすすめできる。

3 comments:

Anonymous said...

細かいツッコミをすると、サンプリング定理より、
必要な精度の少なくとも2倍の周波数でサンプリングする必要があります。

つまり、100ナノ秒の精度で計測するには少なくとも200MHzのクロックが必要なわけです。

江添亮 said...

ああ、そうか。
サンプリング定理はこの場合の精度にも影響するのか。
考えたらそれもそうだ。

Anonymous said...

さらに細かいことですが、最近の x86/x64 プロセッサの TSC では constant TSC や invariant TSC がサポートされていて、プロセッサの実際の動作周波数とは別に (一定周期で) カウントが進みます。

マルチプロセッサの問題と移植性の問題を除けば、割と使いやすいタイマー源だと言えると思います。