最終的に何が必要かは、入力データが 1D 配列か 2D 配列か、およびグリッドとブロックが 1D か 2D かによって異なります。最も単純なケースは両方とも 1D です。
shmem[threadIdx.x] = gmem[blockDim.x * blockIdx.x + threadIdx.x];
これは合体。私が使用する経験則は、最も急速に変化する座標 (threadIdx) がオフセットとしてブロック オフセット (blockDim * blockIdx) に追加されるというものです。最終結果は、ブロック内のスレッド間のインデックス作成ストライドが 1 になることです。ストライドが大きくなると、合体が失われます。
簡単なルール (Fermi 以降の GPU) は、ワープ内のすべてのスレッドのアドレスが同じ 128 バイトの範囲に収まる場合、単一のメモリ トランザクションが発生するというものです (ロードに対してキャッシュが有効になっていると仮定すると、これはデフォルト)。それらが 2 つのアラインされた 128 バイト範囲に入る場合、2 つのメモリ トランザクションが発生します。
GT2xx 以前の GPU では、より複雑になります。ただし、その詳細については、プログラミング ガイドを参照してください。
追加の例:
合体していない:
shmem[threadIdx.x] = gmem[blockDim.x + blockIdx.x * threadIdx.x];
合体していませんが、GT200以降ではそれほど悪くはありません:
stride = 2;
shmem[threadIdx.x] = gmem[blockDim.x * blockIdx.x + stride * threadIdx.x];
まったく合体していない:
stride = 32;
shmem[threadIdx.x] = gmem[blockDim.x * blockIdx.x + stride * threadIdx.x];
合体、2D グリッド、1D ブロック:
int elementPitch = blockDim.x * gridDim.x;
shmem[threadIdx.x] = gmem[blockIdx.y * elementPitch +
blockIdx.x * blockDim.x + threadIdx.x];
合体、2D グリッドおよびブロック:
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
int elementPitch = blockDim.x * gridDim.x;
shmem[threadIdx.y * blockDim.x + threadIdx.x] = gmem[y * elementPitch + x];