C# で物理オブジェクトの処理を高速化するために、線形更新アルゴリズムを並列アルゴリズムに変更することにしました。ジョブのキューを完了するために構築された ThreadPool を使用するのが最善の方法だと思いました。
並列アルゴリズムを最初に実装したとき、すべての物理オブジェクトのジョブをキューに入れました。1 つのジョブはかなり短時間で完了することに注意してください (力、速度、位置を更新し、周囲のオブジェクトの古い状態との衝突をチェックしてスレッド セーフにするなど)。次に、1 つの待機ハンドルを使用して、すべてのジョブが終了するのを待ちます。物理オブジェクトが完了するたびに、インターロックされた整数をデクリメントします (ゼロになると、待機ハンドルを設定します)。次に行う必要があるタスクは、オブジェクトをすべて更新する必要があるため、待機が必要でした。
私が最初に気づいたのは、パフォーマンスがクレイジーだったことです。平均すると、スレッド プーリングは少し速くなったように見えましたが、パフォーマンスに大きなスパイクがありました (更新ごとに 10 ミリ秒のオーダーで、ランダムに 40 ~ 60 ミリ秒にジャンプしました)。ANTS を使用してこれをプロファイリングしようとしましたが、スパイクが発生した理由についての洞察を得ることができませんでした。
私の次のアプローチは、引き続き ThreadPool を使用することでしたが、代わりにすべてのオブジェクトをグループに分割しました。私は最初、8 つのグループだけから始めました。これは、コンピューターのコアがすべて同じだったからです。パフォーマンスは素晴らしかった。シングルスレッドのアプローチよりもはるかに優れており、スパイクはありませんでした (更新ごとに約 6 ミリ秒)。
私が考えた唯一のことは、1 つのジョブが他のジョブより先に完了した場合、アイドル状態のコアが存在するということでした。そのため、ジョブの数を約 20、さらには最大 500 に増やしました。予想どおり、5ms まで低下しました。
だから私の質問は次のとおりです:
- ジョブ サイズを高速または多にするとスパイクが発生するのはなぜですか?
- ThreadPool の最適な使用方法を理解するのに役立つ、ThreadPool の実装方法に関する洞察はありますか?