3

Nvidia GPU で を呼び出すclEnqueueNDRangeと、プログラムは処理が完了するまで待機してから続行します。より正確には、同等の C++ バインディングを呼び出してCommandQueue::enqueueNDRangeいますが、これは違いはありません。これは、リモートの Nvidia ハードウェア (3 台の Tesla M2090) でのみ発生します。AMD GPU を搭載したオフィスのワークステーションでは、呼び出しはノンブロッキングで、すぐに戻ります。テストするローカルの Nvidia ハードウェアがありません。以前は使用していましたが、そのときも同様の動作を覚えていますが、少し曖昧です。

これにより、複数の GPU に作業を分散することが難しくなります。std::async新しい C++11 仕様で/を使用して enqueueNDRange への呼び出しごとに新しいスレッドを開始しようとしましたstd::finishが、それも機能していないようです - nvidia-smi で GPU 使用率を監視すると、メモリ使用量がGPU 0 が上がると、何らかの作業が行われ、次に GPU 0 のメモリが下がり、GPU 1 のメモリが増え、それが何らかの作業を行います。私の gcc バージョンは 4.7.0 です。

カーネルを開始する方法は次のとおりです。インクリメントは、目的のグローバル作業サイズをデバイス数で割ったもので、目的のローカル作業サイズの最も近い倍数に切り上げられます。

std::vector<cl::CommandQueue> queues;
/* Population of queues happens somewhere
cl::NDrange offset, increment, local;
std::vector<std::future<cl_int>> enqueueReturns;
int numDevices  = queues.size();

/* Calculation of increment (local is gotten from the function parameters)*/

//Distribute the job among each of the devices in the context
for(int i = 0; i < numDevices; i++)
{   
    //Update the offset for the current device
    offset = cl::NDRange(i*increment[0], i*increment[1], i*increment[2]);

    //Start a new thread for each call to enqueueNDRangeKernel
    enqueueReturns.push_back(std::async(
                   std::launch::async,
                   &cl::CommandQueue::enqueueNDRangeKernel,
                   &queues[i],
                   kernels[kernel],
                   offset,
                   increment,
                   local,
                   (const std::vector<cl::Event>*)NULL,
                   (cl::Event*)NULL));
    //Without those last two casts, the program won't even compile
}   
//Wait for all threads to join before returning
for(int i = 0; i < numDevices; i++)
{   
    execError = enqueueReturns[i].get();

    if(execError != CL_SUCCESS)
        std::cerr << "Informative error omitted due to length" << std::endl
}   

カーネルは への呼び出しで確実に実行されているはずですstd::async。小さなダミー関数を作成し、GDB でブレークポイントを設定して、std::async呼び出された瞬間にステップインさせることができるからです。しかし、enqueueNDRangeKernel のラッパー関数を作成し、そこで実行し、実行後に print ステートメントを挿入すると、出力の間に時間がかかることがわかります。

PS Nvidia dev ゾーンがハッカーなどによってダウンしているため、そこに質問を投稿できませんでした。

編集:言及するのを忘れました-引数としてカーネルに渡しているバッファー(および、上記で言及した、GPU間で渡されるように見えるバッファー)は、CL_MEM_COPY_HOST_PTRを使用して宣言されています。同じ効果が発生して、CL_READ_WRITE_BUFFERを使用していました。

4

2 に答える 2

3

Nvidia の担当者にメールを送信したところ、かなり公平な回答が得られました。Nvidia SDK には、個別に作成する必要があるデバイスごとに示すサンプルがあります。

  • キュー- したがって、各デバイスを表し、注文をキューに入れることができます
  • buffers - デバイスに渡す必要がある配列ごとに 1 つのバッファー。それ以外の場合、デバイスは単一のバッファーを通過し、それが使用可能になるのを待って、すべてを効果的にシリアル化します。
  • kernel - これはオプションだと思いますが、引数の指定がずっと簡単になります。

さらに、個別のスレッドでキューごとに EnqueueNDRangeKernel を呼び出す必要があります。これは SDK サンプルにはありませんが、Nvidia の担当者は呼び出しがブロックされていることを確認しました。

これをすべて行った後、複数の GPU で同時実行を実現しました。ただし、まだ少し問題があります。次の質問に進みます...

于 2012-07-24T17:35:42.537 に答える
0

はい、あなたが正しい。私の知る限り-nvidiaの実装には同期「clEnqueueNDRange」があります。ライブラリ(梵天)も使用しているとき、これに気付きました。回避策やこれを防ぐ方法があるかどうかはわかりません。別の実装(したがってデバイス)を使用して保存してください。

于 2012-07-19T14:17:19.653 に答える