基本的な削減によってベクトル累積を実行できる単純な CUDA カーネルがあります。複数のブロックに分割することで、より大きなデータを処理できるようにスケールアップしています。ただし、カーネルが使用する適切な量の共有メモリを割り当てるという私の仮定は、不正なメモリ アクセスで失敗しています。この制限を増やすと消えますが、その理由を知りたいです。これが私が話しているコードです:
コアカーネル:
__global__ static
void vec_add(int *buffer,
int numElem, // The actual number of elements
int numIntermediates) // The next power of two of numElem
{
extern __shared__ unsigned int interim[];
int index = blockDim.x * blockIdx.x + threadIdx.x;
// Copy global intermediate values into shared memory.
interim[threadIdx.x] =
(index < numElem) ? buffer[index] : 0;
__syncthreads();
// numIntermediates2 *must* be a power of two!
for (unsigned int s = numIntermediates / 2; s > 0; s >>= 1) {
if (threadIdx.x < s) {
interim[threadIdx.x] += interim[threadIdx.x + s];
}
__syncthreads();
}
if (threadIdx.x == 0) {
buffer[blockIdx.x] = interim[0];
}
}
そして、これは呼び出し元です:
void accumulate (int* buffer, int numElem)
{
unsigned int numReductionThreads =
nextPowerOfTwo(numElem); // A routine to return the next higher power of 2.
const unsigned int maxThreadsPerBlock = 1024; // deviceProp.maxThreadsPerBlock
unsigned int numThreadsPerBlock, numReductionBlocks, reductionBlockSharedDataSize;
while (numReductionThreads > 1) {
numThreadsPerBlock = numReductionThreads < maxThreadsPerBlock ?
numReductionThreads : maxThreadsPerBlock;
numReductionBlocks = (numReductionThreads + numThreadsPerBlock - 1) / numThreadsPerBlock;
reductionBlockSharedDataSize = numThreadsPerBlock * sizeof(unsigned int);
vec_add <<< numReductionBlocks, numThreadsPerBlock, reductionBlockSharedDataSize >>>
(buffer, numElem, numReductionThreads);
numReductionThreads = nextPowerOfTwo(numReductionBlocks);
}
}
次の構成の GPU で 1152 要素のサンプル セットを使用してこのコードを試しました: タイプ: Quadro 600 MaxThreadsPerBlock: 1024 MaxSharedMemory: 48KB
出力:
Loop 1: numElem = 1152, numReductionThreads = 2048, numReductionBlocks = 2, numThreadsPerBlock = 1024, reductionBlockSharedDataSize = 4096
Loop 2: numElem = 1152, numReductionThreads = 2, numReductionBlocks = 1, numThreadsPerBlock = 2, reductionBlockSharedDataSize = 8
CUDA Error 77: an illegal memory access was encountered
「暫定的な」共有メモリが不正なメモリ アクセスを引き起こしているのではないかと疑い、次の行で共有メモリを勝手に 2 倍に増やしました。
reductionBlockSharedDataSize = 2 * numThreadsPerBlock * sizeof(unsigned int);
そして、カーネルが正常に動作し始めました!
私が理解していないのは、問題を解決するためにこの追加の共有メモリを提供しなければならなかった理由です (一時的に)。
このマジック ナンバーを確認するためのさらなる実験として、6912 ポイントのはるかに大きなデータセットを使用してコードを実行しました。今回は、2X や 4X でも役に立ちませんでした。
Loop 1: numElem = 6912, numReductionThreads = 8192, numReductionBlocks = 8, numThreadsPerBlock = 1024, reductionBlockSharedDataSize = 16384
Loop 2: numElem = 6912, numReductionThreads = 8, numReductionBlocks = 1, numThreadsPerBlock = 8, reductionBlockSharedDataSize = 128
CUDA Error 77: an illegal memory access was encountered
しかし、共有メモリのサイズを 8 倍に増やすと、問題は再び解消されました。
もちろん、48KB の共有メモリの制限をすぐに使い果たしてしまうため、ますます大きなデータ セットに対してこの倍率を任意に選択することはできません。だから私は自分の問題を解決する正当な方法を知りたい.