私はCUDAを削減しようとしていますが、本当に初心者です。私は現在、NVIDIAのサンプルコードを研究しています。
特に入力配列が単一のブロックサイズよりも大きい場合(512 X 512
)、ブロックサイズとグリッドサイズを設定する方法が本当にわからないと思います。
これがコードです。
template <unsigned int blockSize>
__global__ void reduce6(int *g_idata, int *g_odata, unsigned int n)
{
extern __shared__ int sdata[];
unsigned int tid = threadIdx.x;
unsigned int i = blockIdx.x*(blockSize*2) + tid;
unsigned int gridSize = blockSize*2*gridDim.x;
sdata[tid] = 0;
while (i < n)
{
sdata[tid] += g_idata[i] + g_idata[i+blockSize];
i += gridSize;
}
__syncthreads();
if (blockSize >= 512) { if (tid < 256) { sdata[tid] += sdata[tid + 256]; } __syncthreads(); }
if (blockSize >= 256) { if (tid < 128) { sdata[tid] += sdata[tid + 128]; } __syncthreads(); }
if (blockSize >= 128) { if (tid < 64) { sdata[tid] += sdata[tid + 64]; } __syncthreads(); }
if (tid < 32)
{
if (blockSize >= 64) sdata[tid] += sdata[tid + 32];
if (blockSize >= 32) sdata[tid] += sdata[tid + 16];
if (blockSize >= 16) sdata[tid] += sdata[tid + 8];
if (blockSize >= 8) sdata[tid] += sdata[tid + 4];
if (blockSize >= 4) sdata[tid] += sdata[tid + 2];
if (blockSize >= 2) sdata[tid] += sdata[tid + 1];
}
if (tid == 0) g_odata[blockIdx.x] = sdata[0];
}
ただし、g_odata[blockIdx.x]
はすべてのブロックから部分的な合計を保存しているように見えます。最終結果を取得する場合は、g_odata[blockIdx.x]
配列内のすべての項を合計する必要があります。
私は疑問に思っています:全体の合計を行うためのカーネルはありますか?それとも私はここで物事を誤解していますか?誰かがこれで私を教育することができれば本当にありがたいです。どうもありがとう。
このトピックをよりよく理解するために、コードで使用したすべての戦略をグラフィカルに説明しているNVIDIAの このpdf を見ることができます。
あなたの理解は正しいです。示されている削減 ここ は、グローバルメモリに格納された一連のブロック合計になります。
これらのブロック合計をすべて合計するには、何らかの形式のグローバル同期が必要です。すべてのブロックが完了するまで待ってから、それらの合計を合計する必要があります。この時点でいくつかのオプションがあり、そのうちのいくつかは次のとおりです。
CUDAタグの周りを検索すると、これらすべての例と、それらの長所と短所の議論を見つけることができます。投稿したメインカーネルが完全な削減にどのように使用されているかを確認するには、 並列削減サンプルコード を参照してください。
Robert Crovellaはすでにこの質問に答えています。これは主に、パフォーマンスではなく理解に関するものです。
ただし、この質問にぶつかるすべての人のために、 [〜#〜] cub [〜#〜] がブロック削減機能を利用できるようにすることを強調したいと思います。以下に、CUBの BlockReduce
の使用方法に関する簡単な実例を示します。
#include <cub/cub.cuh>
#include <cuda.h>
#include "Utilities.cuh"
#include <iostream>
#define BLOCKSIZE 32
const int N = 1024;
/**************************/
/* BLOCK REDUCTION KERNEL */
/**************************/
__global__ void sum(const float * __restrict__ indata, float * __restrict__ outdata) {
unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;
// --- Specialize BlockReduce for type float.
typedef cub::BlockReduce<float, BLOCKSIZE> BlockReduceT;
// --- Allocate temporary storage in shared memory
__shared__ typename BlockReduceT::TempStorage temp_storage;
float result;
if(tid < N) result = BlockReduceT(temp_storage).Sum(indata[tid]);
// --- Update block reduction value
if(threadIdx.x == 0) outdata[blockIdx.x] = result;
return;
}
/********/
/* MAIN */
/********/
int main() {
// --- Allocate Host side space for
float *h_data = (float *)malloc(N * sizeof(float));
float *h_result = (float *)malloc((N / BLOCKSIZE) * sizeof(float));
float *d_data; gpuErrchk(cudaMalloc(&d_data, N * sizeof(float)));
float *d_result; gpuErrchk(cudaMalloc(&d_result, (N / BLOCKSIZE) * sizeof(float)));
for (int i = 0; i < N; i++) h_data[i] = (float)i;
gpuErrchk(cudaMemcpy(d_data, h_data, N * sizeof(float), cudaMemcpyHostToDevice));
sum<<<iDivUp(N, BLOCKSIZE), BLOCKSIZE>>>(d_data, d_result);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
gpuErrchk(cudaMemcpy(h_result, d_result, (N / BLOCKSIZE) * sizeof(float), cudaMemcpyDeviceToHost));
std::cout << "output: ";
for(int i = 0; i < (N / BLOCKSIZE); i++) std::cout << h_result[i] << " ";
std::cout << std::endl;
gpuErrchk(cudaFree(d_data));
gpuErrchk(cudaFree(d_result));
return 0;
}
この例では、長さN
の配列が作成され、結果は32
の連続する要素の合計になります。そう
result[0] = data[0] + ... + data[31];
result[1] = data[32] + ... + data[63];
....