4

ブロック同期ができないことは承知しています。唯一の方法は、新しいカーネルを起動することです。

しかし、X ブロックを起動したとしましょう。ここで、X は GPU 上の SM の数に対応します。スケジューラーが各 SM にブロックを割り当てることを考慮する必要があります...そうですか? そして、GPU がセカンダリ グラフィック カード (完全に CUDA 専用) として使用されている場合、これは、理論的には、他のプロセスがそれを使用していないことを意味します...そうですか?

私の考えは次のとおりです。暗黙の同期。

1 つのブロックだけが必要な場合もあれば、すべての X ブロックが必要な場合もあるとします。1 つのブロックだけが必要な場合は、最初のブロック (または最初の SM) が「実際の」データで機能し、他の X-1 ブロック (または SM) がいくつかの "ダミー」データで、まったく同じ命令を実行しますが、他のオフセットがあります。

そのため、それらすべてが再び必要になるまで、それらすべてが引き続き同期されます。

この条件下でスケジューラは信頼できますか? それとも、確信が持てませんか?

4

2 に答える 2

3

いくつかの質問がまとめられているので、個別に解決しようと思います。

SM ごとに 1 ブロック

nVidia 自身のフォーラムでしばらく前にこれを尋ねたところ、これが起こっていないことを示す結果が得られました。どうやら、ブロックの数が SM の数と等しい場合、ブロック スケジューラは SM ごとにブロックを割り当てません。

暗黙の同期

いいえ。まず第一に、各ブロックに独自の SM があることを保証することはできません (上記を参照)。次に、すべてのブロックが同時にグローバル ストアにアクセスすることはできません。それらが同期的に実行されると、最初のメモリ読み取り/書き込みの時点でこの同期性が失われます。

ブロック同期

良いニュースがあります。はい、できます。CUDA C プログラミング ガイドのセクション B.11 で説明されているアトミック命令を使用して、バリアを作成できます。NGPU で同時に実行されるブロックがあるとします。

__device__ int barrier = N;

__global__ void mykernel ( ) {

    /* Do whatever it is that this block does. */
    ...

    /* Make sure all threads in this block are actually here. */
    __syncthreads();

    /* Once we're done, decrease the value of the barrier. */
    if ( threadIdx.x == 0 )
        atomicSub( &barrier , 1 );

    /* Now wait for the barrier to be zero. */
    if ( threadIdx.x == 0 )
        while ( atomicCAS( &barrier , 0 , 0 ) != 0 );

    /* Make sure everybody has waited for the barrier. */
    __syncthreads();

    /* Carry on with whatever else you wanted to do. */
    ...

    }

命令はアトミックにatomicSub(p,i)計算*p -= iされ、ブロック内の 0 番目のスレッドによってのみ呼び出されます。つまり、デクリメントをbarrier1 回だけ行います。この命令atomicCAS(p,c,v)*p = viffを設定*p == cし、 の古い値を返します*pbarrierこの部分は、到達するまで0、つまりすべてのブロックが通過するまでループします。

__synchtreads()ブロック内のスレッドは厳密なロックステップで実行されないため、この部分を の呼び出しでラップする必要があり、すべてのスレッドが 0 番目のスレッドを待機するように強制する必要があることに注意してください。

カーネルを複数回呼び出す場合は、 にbarrier戻す必要があることを覚えておいてくださいN

アップデート

jHackTheRipperの回答とCicadaのコメントへの返信として、GPU で同時にスケジュールできるよりも多くのブロックを開始しようとするべきではないことを指摘する必要がありました。これは多くの要因によって制限されるため、CUDA Occupancy Calculatorを使用して、カーネルとデバイスの最大ブロック数を見つける必要があります。

ただし、元の質問から判断すると、SM の数だけブロックが開始されているため、この点は意味がありません。

于 2012-07-04T10:58:40.670 に答える
-4

@Pedroは間違いなく間違っています!

グローバルな同期を達成することは、最近のいくつかの研究作業の主題であり、ついに非 Kepler アーキテクチャ (私はまだ持っていません) の対象となっています。結論は常に同じ (または同じであるべき) です。GPU 全体でこのようなグローバル同期を実現することは不可能です。

理由は簡単です。CUDA ブロックはプリエンプトできないため、GPU を完全に占有すると、バリア ランデブーを待機しているスレッドはブロックの終了を許可しません。したがって、SM から削除されず、残りのブロックが実行されなくなります。

結果として、このデッドロック状態から逃れることのできない GPU をフリーズするだけです。

-- ペドロの発言に答えるために編集 --

このような欠点は、 http ://www.openclblog.com/2011/04/eureka.html などの他の作成者によって認識されてい ます。

実際の OpenCL の作者による

-- Pedro の 2 番目の発言に答えるために編集 --

@Jared Hoberock がこの SO 投稿で同じ結論を下しています: CUDA のブロック間バリア

于 2012-07-04T12:42:45.590 に答える