次の方法で共有


ゲームのタイミングとマルチコア プロセッサ

XNA デベロッパー コネクション (XDC)、ソフトウェア デザイン エンジニア

Chuck Walbourn 著

2005 年 12 月

はじめに

最近のコンピューターで電源管理技術の普及が進んでいることにより、高精度の CPU タイミングを取得するための一般的な方法であった RDTSC 命令が、予期したとおりに機能しない場合があります。ここでは、Windows API の QueryPerformanceCounterQueryPerformanceFrequency を使用した、より正確で確実な解決策を示します。

背景

x86 P5 命令セットの導入以降、多くのゲーム開発者が高精度タイミングを実行するために Read Time Stamp Counter (タイム スタンプ カウンターの読み取り) つまり RDTSC 命令を使用してきました。Windows マルチメディア タイマーの精度は、サウンドとビデオの処理には十分ですが、十数ミリ秒以下のフレーム時間でデルタ時間情報を提供するには不十分です。多くのゲームがいまだに起動時にマルチメディア タイマーを使用して CPU の周波数を設定しており、その周波数値を使用して RDTSC で得た結果をスケーリングすることで正確な時間を得ています。RDTSC には限界があるため、Windows API では QueryPerformanceCounterQueryPerformanceFrequency の両ルーチンを通じてこの機能をより正確に実現できるようにしています。

このように RDTSC をタイミングに使用すると、3 つの基本的な問題に直面します。

  1. 連続性のない値。RDTSC を直接使用する場合は、スレッドが常に同じプロセッサで実行されていることを前提とします。マルチプロセッサおよびデュアル コア システムでは、コア間のサイクル カウンターの同期化が保証されません。各種コアをそれぞれ別の時間に休止および復旧する最新の電源管理技術が加わると事態はさらに悪くなり、通常はコアが同期されなくなります。これによりアプリケーションでは、スレッドがプロセッサ間を移動するときに不具合やクラッシュが発生し、大きなデルタ、負のデルタ、またはタイミングの停止をもたらすタイミング値を取得することになります。

  2. 専用ハードウェアの使用可能性。RDTSC は、アプリケーションがプロセッサのサイクル カウンターに要求するタイミング情報をロックします。これは、長年にわたって、高精度のタイミング情報を取得する最善の方法でしたが、最近のマザーボードは、RDTSC の欠点とは無関係に高精度のタイミング情報を提供する専用のタイミング デバイスを搭載するようになっています。

  3. CPU 周波数の変動性。プログラムが実行されている間は CPU の周波数が一定であると想定されていることが少なくありません。しかし、最新の電源管理技術が使用されている場合、この想定は誤りです。CPU の周波数を変更する技術は、当初はラップトップ コンピューターやその他のモバイル デバイスに限定されていましたが、現在はハイエンドのデスクトップ PC で多用されています。この技術を無効にして一定の周波数を維持することは、一般には許されません。

推奨事項

ゲームには正確なタイミング情報が必要ですが、開発者は RDTSC の使用に伴う問題が発生しないようにタイミング コードを実装する必要があります。高精度タイミングを実装する際に必要な手順を次に示します。

  1. RDTSC の代わりに QueryPerformanceCounterQueryPerformanceFrequency を使用します。この 2 つの API は、RDTSC を使用することもできますが、代わりにマザーボード上のタイミング デバイス、または高品質で高精度のタイミング情報を提供する他のシステム サービスを使用することもできます。QueryPerformanceCounter は API 呼び出しなので RDTSC の方がはるかに高速ですが、この API は 1 つのフレームで数百回呼び出しても顕著な影響はありません(ただし、パフォーマンスの低下を避けるために、ゲームでの QueryPerformanceCounter の呼び出しは最小限に抑えるようにしてください)。

  2. デルタを計算する際には、タイミング値の誤りによってクラッシュが発生したり時間関連の計算が不安定になったりしないように、値をクランプする必要があります。クランプの範囲は、0 (負のデルタ値を回避するため) から予想される最小フレーム レートに基づく妥当な値までとします。クランプするとアプリケーションをデバッグする際に役立つ可能性がありますが、パフォーマンス分析を行ったり、最適化されていないモードでゲームを実行したりする場合は注意が必要です。

  3. すべてのタイミングを 1 つのスレッドで計算します。複数のスレッドでタイミングを計算すると (たとえば各スレッドを特定のプロセッサに関連付けるなど)、マルチコア システムのパフォーマンスが大きく低下します。

  4. Windows API SetThreadAffinityMask を使用して、この 1 つのスレッドを 1 つのプロセッサ上に残すように設定します。一般に、これはメインのゲーム スレッドです。QueryPerformanceCounterQueryPerformanceFrequency は通常は複数のプロセッサに対応しますが、BIOS やドライバーにバグがあると、スレッドがプロセッサ間を移動したときにこれらのルーチンから異なる値が返される場合があります。したがって、このスレッドは 1 つのプロセッサで保持するのが最善策です。

    それ以外のスレッドでは、そのスレッドのタイマー データを収集しないようにする必要があります。ワーカー スレッドでタイミングを計算することは、同期化のボトルネックになるのでお勧めできません。代わりに、ワーカー スレッドでメイン スレッドからタイムスタンプを読み取るようにします。ワーカー スレッドはタイムスタンプの読み取りのみを行うため、クリティカル セクションを使用する必要はありません。

  5. QueryPerformanceFrequency は 1 度だけ呼び出します。これは、システムの実行中は周波数が変わらないためです。

アプリケーションの互換性

RDTSC の動作については、多くの開発者が長年にわたってさまざまな推測を行ってきたため、既存のアプリケーションの一部では、マルチプロセッサまたはマルチコアのシステムで実行した場合に、タイミングの実装に起因する問題がきわめて高い確率で発生することが予想されます。通常、このような問題は、不具合が発生したり動きが遅くなったりすることで明らかになります。電源管理に対応していないアプリケーションに対する簡単な救済策はありませんが、マルチプロセッサ システムでアプリケーションを常に 1 つのプロセッサで強制的に実行するための shim が存在します。

この shim を作成するには、アプリケーション互換性とユーザー アカウント制御のページからマイクロソフト アプリケーション互換ツールキットをダウンロードします。

このツールキットに含まれる Compatibility Administrator を使用して、アプリケーションおよび関連する修正プログラムのデータベースを作成します。このデータベースに新しい互換性モードを作成し、互換性修正プログラム SingleProcAffinity を選択してアプリケーションのすべてのスレッドが 1 つのプロセッサ/コアで実行されるようにします。コマンド ライン ツール Fixpack.exe (これもツールキットに含まれています) を使用することで、このデータベースをインストール、テスト、配布できるようにインストール可能なパッケージに変換できます。

Compatibility Administrator の使用方法については、ツールキットのドキュメントを参照してください。Fixpack.exe の構文と使用例については、このツールのコマンド ライン ヘルプを参照してください。

顧客指向の情報については、Microsoft ヘルプとサポートで次のサポート技術情報の文書を参照してください。