ワープ内では、他のスレッドよりも「先に進む」スレッドはありません。条件付き分岐があり、それがワープ内の一部のスレッドによって取得され、他のスレッドでは取得されない場合 (ワープの「発散」とも呼ばれる)、他のスレッドは分岐が完了するまでアイドル状態になり、それらはすべて共通の命令で一緒に「収束」します。 . したがって、スレッドのワープ内同期のみが必要な場合、それは「自動的に」行われます。
しかし、異なるワープはこの方法では同期されません。そのため、アルゴリズムが特定の操作を多くのワープで完了する必要がある場合は、明示的な同期呼び出しを使用する必要があります (CUDA プログラミング ガイドのセクション 5.4 を参照してください)。
編集:いくつかのことを明確にするために、次のいくつかの段落を再編成しました。
ここには、実際には 2 つの異なる問題があります。命令の同期とメモリの可視性です。
__syncthreads()
命令の同期を強制し、メモリの可視性を確保しますが、ブロック間ではなく、ブロック内のみです (CUDA プログラミング ガイド、付録 B.6)。これは、共有メモリでの書き込み後読み取りには役立ちますが、グローバル メモリ アクセスの同期には適していません。
__threadfence()
グローバルメモリの可視性を保証しますが、命令の同期は行わないため、私の経験では使用が制限されています (ただし、付録 B.5 のサンプルコードを参照してください)。
カーネル内ではグローバル命令同期はできません。f()
任意のスレッドを呼び出す前にすべてのスレッドで実行する必要がある場合は、とを 2 つの異なるカーネルg()
に分割し、それらをホストからシリアルに呼び出します。f()
g()
共有カウンターまたはグローバル カウンターをインクリメントするだけの場合は、アトミック インクリメント関数atomicInc()
(付録 B.10) の使用を検討してください。上記のコードの場合、x1
およびx2
が (グリッド内のすべてのスレッドにわたって) グローバルに一意でない場合、非アトミックなインクリメントは、付録 B.2.4 の最後の段落と同様に競合状態になります。
最後に、グローバル メモリに対する操作、特に同期関数 (アトミックを含む) はパフォーマンスに悪いことに注意してください。
解決しようとしている問題を知らなければ、推測するのは困難ですが、一部の場所でグローバル メモリの代わりに共有メモリを使用するようにアルゴリズムを再設計できる可能性があります。これにより、同期の必要性が減り、パフォーマンスが向上します。