ユーザーのシステム上の GPU 間で処理負荷を分割するアプリケーションがあります。基本的に、メイン アプリケーション スレッドによって定期的にトリガー されると、 GPU 処理間隔を開始する GPU ごとに CPU スレッドがあります。
GPU 処理間隔の例として、次の画像 (NVIDIA の CUDA プロファイラー ツールを使用して生成) を検討してください。ここでは、アプリケーションは単一の GPU を使用しています。
ご覧のとおり、GPU 処理時間の大部分は 2 つの並べ替え操作によって消費されます。私はこれに Thrust ライブラリ (thrust::sort_by_key) を使用しています。また、実際の並べ替えを開始する前に、thrust::sort_by_key がフードの下でいくつかの cudaMallocs を呼び出しているようです。
次に、アプリケーションが処理負荷を 2 つの GPU に分散させた同じ処理間隔を考えます。
完璧な世界では、2 つの GPU の処理間隔が 1 つの GPU の処理間隔の正確に半分になることが予想されます (各 GPU が半分の作業を行っているため)。ご覧のように、cudaMalloc が同時に呼び出されると、ある種の競合の問題により時間がかかるように見えるため (2 ~ 3 倍長くなる場合もあります)、部分的にはそうではありません。2 つの GPU のメモリ割り当てスペースは完全に独立しているため、cudaMalloc でシステム全体のロックを行うべきではないため、これが必要な理由がわかりません。GPU ごとのロックの方が合理的です。
cudaMalloc の同時呼び出しに問題があるという私の仮説を証明するために、2 つの CPU スレッド (GPU ごとに) がそれぞれ cudaMalloc を数回呼び出す非常に単純なプログラムを作成しました。最初にこのプログラムを実行して、別々のスレッドが cudaMalloc を同時に呼び出さないようにしました。
割り当てごとに最大 175 マイクロ秒かかることがわかります。次に、cudaMalloc を同時に呼び出すスレッドでプログラムを実行しました。
ここでは、各呼び出しに約 538 マイクロ秒、つまり前のケースの 3 倍の時間がかかりました! 言うまでもなく、これはアプリケーションの速度を大幅に低下させており、2 つ以上の GPU で問題が悪化するのは当然のことです。
Linux と Windows でこの動作に気付きました。Linux では Nvidia ドライバー バージョン 319.60 を使用し、Windows では 327.23 バージョンを使用しています。CUDA ツールキット 5.5 を使用しています。
考えられる理由: これらのテストでは GTX 690 を使用しています。このカードは基本的に、同じユニットに収容された 2 つの 680 のような GPU です。これは私が実行した唯一の「マルチ GPU」設定なので、cudaMalloc の問題は 690 の 2 つの GPU 間のハードウェア依存関係と関係があるのでしょうか?