CUDA (または NVIDIA GPU) では、1 つのスレッドが実行中のすべてのスレッドの実行を中断する方法はありません。結果が見つかったらすぐにカーネルを終了することはできません。今日では不可能です。
ただし、1 つのスレッドが結果を見つけたら、できるだけ早くすべてのスレッドを終了させることができます。これを行う方法のモデルを次に示します。
__global___ void kernel(volatile bool *found, ...)
{
while (!(*found) && workLeftToDo()) {
bool iFoundIt = do_some_work(...); // see notes below
if (iFoundIt) *found = true;
}
}
これに関するいくつかのメモ。
- の使用に注意してください
volatile
。これは重要。
- カーネルを起動する前に、必ず初期化
found
(デバイス ポインタでなければなりません)を行ってください!false
- 別のスレッドが更新されても、スレッドはすぐには終了しません
found
。次に while ループの先頭に戻ったときにのみ終了します。
- どのように実装するかが
do_some_work
重要です。作業が多すぎる (または変動しすぎる) 場合、結果が見つかった後に終了するまでの遅延が長くなります (または変動します)。作業が少なすぎると、スレッドはfound
有用な作業を行うのではなく、ほとんどの時間をチェックに費やします。
do_some_work
また、タスクの割り当て (つまり、インデックスの計算/インクリメント) も担当します。その方法は、問題によって異なります。
- 起動するブロックの数が、現在の GPU のカーネルの最大占有率よりもはるかに多く、実行中のスレッド ブロックの最初の「ウェーブ」で一致が見つからない場合、このカーネル (およびその下のカーネル) はデッドロックする可能性があります。 . 最初のウェーブで一致が見つかった場合、後のブロックは の後にのみ実行されます
found == true
。つまり、起動してすぐに終了します。解決策は、同時に常駐できる数のブロックのみを起動し (別名「最大起動」)、それに応じてタスクの割り当てを更新することです。
- タスクの数が比較的少ない場合は、 を に置き換えて、
while
タスクif
の数をカバーするのに十分な数のスレッドを実行できます。その場合、デッドロックの可能性はありません (ただし、前のポイントの最初の部分が適用されます)。
workLeftToDo()
問題固有ですが、やるべき作業が残っていない場合は false を返すため、一致が見つからない場合にデッドロックすることはありません。
現在、特に L1 キャッシュのない古いアーキテクチャでは、上記の結果として過剰なパーティション キャンピング (すべてのスレッドが同じメモリをバンキングする) が発生する可能性があります。したがって、ブロックごとの共有ステータスを使用して、もう少し複雑なバージョンを作成することをお勧めします。
__global___ void kernel(volatile bool *found, ...)
{
volatile __shared__ bool someoneFoundIt;
// initialize shared status
if (threadIdx.x == 0) someoneFoundIt = *found;
__syncthreads();
while(!someoneFoundIt && workLeftToDo()) {
bool iFoundIt = do_some_work(...);
// if I found it, tell everyone they can exit
if (iFoundIt) { someoneFoundIt = true; *found = true; }
// if someone in another block found it, tell
// everyone in my block they can exit
if (threadIdx.x == 0 && *found) someoneFoundIt = true;
__syncthreads();
}
}
このように、ブロックごとに 1 つのスレッドがグローバル変数をポーリングし、一致したスレッドのみがグローバル変数に書き込むので、グローバル メモリ トラフィックが最小限に抑えられます。
余談ですが、__global__ 関数は無効です。これは、数千のスレッドから単一の CPU スレッドに値を返す方法を定義するのが難しいためです。ユーザーが自分の目的に合ったデバイスまたはゼロコピー メモリ内の戻り配列を考案するのは簡単ですが、一般的なメカニズムを作成するのは困難です。
免責事項: ブラウザーで記述されたコード、未テスト、未検証。