14.318180MHz
Linux のスリープ処理、タイマ処理の詳細を見る
これに触発されてWindowsだとどんなもんなのか、自分の記憶があやふやだったので確認してみた。
処理1(低精度)
DWORD Timestamp[256]; int i; for ( i = 0; i < 256; ++i ) { Timestamp[i] = ::timeGetTime(); // ここに処理を入れる Sleep(1); }
これの結果は
15, 31, 47, 62, 78, 93
という感じになる。
OSや機種など環境に依存するが、XPだと15msのことが多いようだ。
単に待てばいい処理ならこれでOK。
処理2(精度向上)
timeBeginPeriod( 1 ); Sleep( 1000 ); // 精度向上が反映されるのを待つ DWORD Timestamp[256]; int i; for ( i = 0; i < 256; ++i ) { Timestamp[i] = ::timeGetTime(); // ここに処理を入れる Sleep(1); } timeEndPeriod( 1 );
これの結果は
0, 1, 3, 5, 7, 9
という感じ。
精度が良くなったが、1ms飛んでいる模様。
先頭にSleepが入っている理由は、timeBeginPeriod()で精度を上げることができる(どこまで上げられるかはtimeGetDevCaps()で取得)のだが、いくばくかしないと反映されないため。
処理3(ほぼ1ms精度)
HANDLE event = ::CreateEvent( NULL, FALSE, FALSE, NULL ); // 自動リセットイベント timeBeginPeriod( 1 ); Sleep( 1000 ); int timer_id = ::timeSetEvent( TIMER_PERIOD, TIMER_PERIOD, (LPTIMECALLBACK)event, 0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET ); __int64 Timestamp[TIMER_LOOPS]; int i; for ( i = 0; i < 256; ++i ) { ::QueryPerformanceCounter( (LARGE_INTEGER *)( Timestamp + i ) ); // ここに処理を入れる ::WaitForSingleObject( event, 50 ); } ::timeKillEvent( timer_id ); timeEndPeriod( 1 ); ::CloseHandle( event );
GetTickCount()やtimeGetTime()ではmsオーダーしかわからないので、QueryPerformanceCounter()で見てみることに。
結果、イベントを使えばほぼ1msの周期処理が可能になることがわかった。
というわけでWindowsでも1ms精度なら可能…と思いきや数値を良く見てみると
0.985321, 0.977219, 0.976381, 1.952762
のように、おおむね0.97ms程度で、41〜42msに一度は1.4ms以上の部分がある。
1.2ms以上のものを除外してみたところ、0.976105msという結果が得られた。
どうやら1000Hzではなく、1024Hzがベースになっていて、たまにつじつまを合わせているようだ。
ところでnaoya氏の記事に出てくるマジックナンバー「1193182」について。これはPIT(8253 or 8254)に対するマスタークロックと説明されているのだが、何でこんな中途半端な値なのかというと、初代IBM-PCが入手しやすいTV用の14.318180MHzの水晶を使ったからだそうな。14.318180MHzを3分周した4.77MHzをCPUのクロック、それをさらに4分周した1.193182MHzをPITのクロックにしたのが今まで残っているという。
それでもって、今のPCに8254なんて載っているのかと思いデータシートを見てみた。基本的にはI/Oコントローラであるサウスブリッジに内蔵されているようなのだが、AMDのチップセットには説明があったが、Intelのものには見当たらず。
古いデータシートを見てみたところ、ICH3には記載があるが、ICH4からはなくなっていた。ICH4からはISAのサポートがなくなったとのことで、記載はなくても互換機能が搭載されているのか、外付けになっているのかはわからなかった。
ただ、ICH3のデータシートには「The 8254 unit is clocked by a 14.31818 MHz clock.」とあり、現在でもPITだけでなくFSBやPCIなど全ての基本クロックを未だに14.31818MHzから作り出しているらしい。レガシー恐るべし。