私は現在CUDAを学んでおり、いくつかの演習に取り組んでいます。その 1 つは、要素ごとに 1 スレッド、行ごとに 1 スレッド、列ごとに 1 スレッドの 3 つの異なる方法で行列を追加するカーネルを実装することです。行列は正方形で、1D ベクトルとして実装されています。
A[N*row + col]
直感的には、最初のオプションはスレッドのオーバーヘッドが原因で最も遅く、2 番目のオプションは単一のスレッドが隣接するデータを処理するため、最も高速であると予想しました。
CPU で、8000 x 8000 の密行列を使用すると、次のようになります。
Adding on CPU - Adding down columns
Compute Time Taken: 2.21e+00 s
Adding on CPU - Adding across rows
Compute Time Taken: 2.52e-01 s
したがって、より多くのキャッシュヒットにより、約1桁の速度が向上します。同じ行列を持つ GPU では、次のようになります。
Adding one element per thread
Compute Time Taken: 7.42e-05 s
Adding one row per thread
Compute Time Taken: 2.52e-05 s
Adding one column per thread
Compute Time Taken: 1.57e-05 s
これは私には直感的ではありません。最後のケースの 30 ~ 40% の高速化は、約 1000 x 1000 行列以上で一貫しています。これらのタイミングはカーネル実行のみであり、ホストとデバイス間のデータ転送は含まれていないことに注意してください。以下は、比較のための私の 2 つのカーネルです。
__global__
void matAddKernel2(float* A, float* B, float* C, int N)
{
int row = threadIdx.x + blockDim.x * blockIdx.x;
if (row < N)
{
int j;
for (j = 0; j < N; j++)
{
C[N*row + j] = A[N*row + j] + B[N*row + j];
}
}
}
__global__
void matAddKernel3(float* A, float* B, float* C, int N)
{
int col = threadIdx.x + blockDim.x * blockIdx.x;
int j;
if (col < N)
{
for (j = 0; j < N; j++)
{
C[col + N*j] = A[col + N*j] + B[col + N*j];
}
}
}
私の質問は、GPU スレッドが隣接するデータを操作することで利益を得ないように思われるのはなぜですか?