2

3D ゲーム/シミュレーション エンジンの最適化の一環として、エンジンを自己最適化しようとしています。

基本的に、私の計画はこれです。まず、エンジンでフレームごとの CPU サイクル数を測定します。次に、さまざまなサブシステムが消費する CPU サイクル数 (最小、平均、最大) を測定します。

この情報が与えられると、フレーム ループの特定のいくつかのポイントで、エンジンは現在実行するのに効率的な「オプションの処理」を実行するために利用できる「余分な CPU サイクル」の数を推定できます (関連するデータは現在キャッシュにあります)。 )、ただし、現在のフレームが CPU サイクルが不足する危険がある場合は、後続のフレームまで遅延する可能性があります。

アイデアは、単調な作業でゲームを可能な限り先取りすることです。そのため、「要求の厳しいフレーム」(「単一フレーム中の多くの衝突」など)を処理するために可能なすべての CPU サイクルを利用でき、glXSwapBuffers( ) vsync の最新の可能な瞬間の前にバック/フロント バッファーを交換するのに間に合うように)。


上記の分析では、一定のフレーム レートを確保するための基本的な要件として、バック/フロント バッファーの交換が想定されています。これが唯一のアプローチではないという主張を見てきましたが、論理がわかりません。

glXSwapBuffers() の直前と直後の 64 ビット CPU クロック サイクル タイムをキャプチャしたところ、フレームが約 2,000,000 クロック サイクル異なることがわかりました。これは、glXSwapBuffers()がvsync (バッファーを交換できるとき) までブロックせず、代わりにすぐに戻るという事実によるものと思われます。

次に、glXSwapBuffers() の直前に glFinish() を追加しました。これにより、変動が約 100,000 CPU クロック サイクルに減少しました... しかし、glFinish() は 100,000 から 900,000 CPU クロック サイクルのどこかでブロックされました (おそらく、nvidia ドライバーの作業量に依存します)。バッファーをスワップする前に完了する必要がありました)。glXSwapBuffers() が処理を完了してバッファーをスワップするのにかかる時間のこの種の変動により、「スマートなアプローチ」に希望があるかどうか疑問に思います。


肝心なのは、私の目標を達成する方法がわからないということです。これはかなり単純に見え、基礎となるサブシステム (たとえば、OpenGL ドライバー) にあまり多くを要求していないようです。ただし、glXSwapBuffers() の直前に glFinish() を使用しても、「フレーム時間」に約 1,600,000 サイクルの変動が見られます。測定された「フレームあたりの CPU クロック サイクル」レートを平均し、その平均値が実際のフレーム レートであると仮定できますが、その変動が大きいと、これらの値に依存する可能性があると誤って仮定して、実際にエンジンがフレームをスキップする可能性があります。

関連するさまざまなGLX/OpenGL関数の詳細、または私が試みているよりも実際にうまく機能する可能性のある一般的なアプローチについての洞察をいただければ幸いです。

PS: 私の CPU の CPU クロック レートは、コアが遅くなったり速くなったりしても変化しません。したがって、それは私の問題の原因ではありません。

4

2 に答える 2

1

私はあなたの問題を再解釈しようとします(何かを見逃した場合は、あなたが教えてくれ、答えを更新できるようにします):

Vsync イベントが発生するまでの時間を Tとすると、 1xT秒 (または 1 に近い秒数)を使用してフレームを作成する必要があります。

ただし、キャッシュの局所性を利用して完全に決定論的な時間動作を実現できるようにタスクをコーディングできたとしても (各タスクに必要な時間と自由に使える時間を事前に知っている)、理論的には次のような時間を達成します。

0.96xT

0.84xT

0.99xT


いくつかの事実に対処する必要があります。

  1. あなたはTを知りません(あなたはそれを測定しようとしましたが、それはヒックカップのようです:それらはドライバーに依存しています!)
  2. タイミングに誤差があります
  3. 異なる CPU アーキテクチャ: 関数の CPU サイクルを測定しますが、別の CPU では、プリフェッチまたはパイプライン処理の良し悪しにより、必要なサイクルが少なくなったり多くなったりします。
  4. 同じ CPU で実行している場合でも、別のタスクが prefetech アルゴリズムを汚染する可能性があるため、同じ関数が必ずしも同じ CPU サイクルになるとは限りません (以前に呼び出された関数と prefetech algorihtm によって異なります!)。
  5. オペレーティングシステムは、アプリケーションを一時停止してバックグラウンドプロセスを実行することにより、いつでも干渉する可能性があります。これにより、「充填」タスクの時間が効果的に増加し、Vsync イベントを見逃す可能性があります (「予測」時間が0.85xTのように妥当であっても)

時々あなたはまだの時間を得ることができます

1.3xT

同時に、可能なすべての CPU パワーを使用していませんでした (Vsync イベントを逃すと、基本的にフレーム時間が無駄になるため、無駄な CPU パワーになります)


あなたはまだ回避することができます;)

バッファリング フレーム:最大 2/3 フレームのレンダリング呼び出しを保存します (これ以上はありません! 既にいくらかのレイテンシが追加されており、特定の GPU ドライバーは並列処理を改善し、消費電力を削減するために同様のことを行います!)、その後、ゲーム ループを使用してアイドルまたは遅い仕事をする。

そのアプローチでは、1xTを超えることは合理的です。いくつかの「バッファフレーム」があるためです。

簡単な例を見てみましょう

  • タスクを0.95xTにスケジュールしましたが、アーキテクチャが異なるため、プログラムの開発に使用したものとは異なる CPU を搭載したマシンでプログラムが実行されているため、フレームは1.3xTかかります。
  • 後ろにいくつかのフレームがあることはわかっているので問題ありませんが、1xT - 0.3xTタスクを起動する必要があります。セキュリティ マージンを使用して、 0.7xT の代わりに 0.6xT のタスクを起動することをお勧めます
  • Ops 本当に問題が発生しました。フレームは再び1.3xTかかりました。フレームの予約を使い果たしました。単純な更新を行って GL 呼び出しを送信するだけで、プログラムは0.4xTを予測します。
  • 2xTを超える作業をスケジュールした場合でも、プログラムが次のフレームに0.3xTを要したことに驚くと、レンダリング スレッドで再び 3 フレームがキューに入れられます。
  • いくつかのフレームがあり、作業が遅れているため、1,5xTの更新をスケジュールします

少しレイテンシーを導入することで、CPU パワーを最大限に活用できます。もちろん、ほとんどの場合、キューに 2 つ以上のフレームがバッファーされていることを測定した場合は、プールを 3 つではなく 2 つに削減して、レイテンシーを節約できます。


もちろん、これはすべての作業を同期的に行うことを前提としています (GL cal を延期することは別として)。必要に応じて追加のスレッドを使用して (ファイルの読み込みやその他の負荷の高いタスク)、パフォーマンスを向上させることができます (必要な場合)。

于 2015-01-27T17:42:23.560 に答える