Bullet Physicsのマルチスレッド対応について2(Bullet2.86 VS2017)

前回Bullet2.87のマルチスレッド機能の検証を行いましたが、思ったほど効果が得られませんでした。今回はスレッド使用の方針を変えます。Bullet内部のマルチスレッド機能ではなく、外部で用意したスレッドでstepSimulation関数を実行することで並列処理を行いました。結果は、一部デバッグ機能で問題がありましたが問題なく動作し、条件は限定されますが3コアでの並列実行により2倍以上の高速化が実現できました。ゲームプログラムでの描画処理、ゲーム判定用物理計算、物理エフェクトの3つ処理を想定した検証プログラムを作成します。


Bullet Physics 2.86

http://bulletphysics.org/
https://github.com/bulletphysics/bullet3/releasesよりダウンロードしたものを使用しています。


スレッドによる並列化仕様

依存しない処理を複数コアで同時実行することで高速化を実現します。ネットワーク処理のように待ち時間を有効利用するためのスレッド使用ではないため、マルチコアCPUでないと効果がありません。描画処理と2つの物理計算を3つのCPUコアで並列実行します。4コアCPUでは自由に使用できるコアは3つまでのようなので、これ以上並列化しても効果はないようです(Windowsの仕様?)。

スレッドにより並列実行するものはBullet物理ワールドクラスのstepSimulation関数のみ、メインスレッドではWindowsプログラム、描画処理、剛体の追加などの操作を行います。stepSimulation関数実行中はメインスレッドからのBullet物理ワールドへのアクセスは禁止、stepSimulation関数終了直後Bullet物理ワールドの状態を専用バッファにコピーし、描画処理はそのコピーした値を使用します。描画処理は1フレーム前の物理状態を反映することになります。


Bulletスレッド対応調査

stepSimulation関数を複数スレッドから同時実行できる必要があります。BT_THREADSAFEという定義がありますが、これは内部処理をスレッドセーフするための定義で、外部から呼ばれる関数などがスレッドセーフになるわけではありません。並列化にはstepSimulationからアクセスするグローバル変数や関数がスレッドセーフであることが必要です。ソースコードを調査すると、残念なことにグローバル変数を使用、メモリ確保の関数もスレッドセーフではありませんでした。ただ、すべてメモリ確保回数などのカウント系のデバッグ機能で直接物理計算に影響することはないようです。並列処理の有無で計算結果が同じなるかどうかのテストでは問題がなかったため、これらのカウンターを使用しなければ並列処理ができそうです。
※並列実行が保障されているわけではありません、利用する場合は自己責任で

時間計測などのプロファイリングとメモリデバッグ機能は並列実行すると不具合が発生するため無効にする必要があります。BT_NO_PROFILE定義、BT_DEBUG_MEMORY_ALLOCATIONSを有効にしない。
BT_THREADSAFEでこれらの処理がスレッドセーフになってほしいのですが、2.87やBullet3でも対応していないようです。


検証プログラムによる処理時間計測

並列化の有無での処理時間、CPU使用率の変化を測定しました。手前(Bullet Step1)と奥(Bullet Step2)は異なる物理ワールドで、stepSimulation関数をスレッドにより同時実行しています。また物理計算と同時に描画処理(Render Step)も並列処理しています。

並列化OFF

Total 47ms
Bullet Step1 16ms
Bullet Step2 14ms
Render Step 16ms

順次処理を実行しているため、全体の実行時間はすべての処理の足し算になっています。VisualStudioの診断ツールでCPU使用率30%程なので、コアを1つしか利用していないことになります。


並列化ON

Total 23ms
Bullet Step1 22ms
Bullet Step2 19ms
Render Step 18ms

全体の処理時間が、一番遅い処理Bullet Step1とほぼ同じになっています。他の処理と並列処理されている証拠です。VSの診断ツールでCPU使用率が100%に近いため、使用可能なコア3つを無駄なく使用していることがわかります。並列化した場合メモリキャッシュや帯域幅の関係で、それぞれの処理時間が1割ほど増加しているようです。


結論

Bulletが完全にスレッドセーフではないという問題がありましたが、予想通りを結果を得ることができました。実際に使用するにはもっと詳しい調査が必要だと思いますが、それに見合う結果が得られるはずです。
ゲームプログラムでマルチコアCPUを活用するには、個別処理内部を並列化するのではなく、個別処理を並列処理可能にして、処理時間や結果の依存関係を考慮したスケジュール管理方式がよさそうです。全体的にスレッドセーフにする必要があったり、メモリの依存関係や排他処理など実装が難しくなりますが、並列化の効果をより得ることができるはずです。


サンプルプログラム

描画処理、Bulletの物理計算2つを並列処理する検証プログラムです。並列処理の有無での処理時間の違いを確認できます。
必要なBulleのソースコードとプロジェクトを同梱、Bulletのダウンロードと環境構築は不要です。

DirectX11 Bullet Physics 2.86 Multi-Thread2
広告

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

認証:数字を入力してください(必須) * Time limit is exhausted. Please reload CAPTCHA.