Уменьшение блока в CUDA

Я пытаюсь сделать сокращение 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] массив.

мне интересно: есть ли ядро, чтобы сделать все суммирование? или я все неправильно понял? Я был бы очень признателен, если бы кто-нибудь мог обучить меня этому. Большое спасибо.

3 ответов


чтобы лучше понять эту тему, вы можете посмотреть на это ( http://developer.download.nvidia.com/compute/cuda/1.1-Beta/x86_website/projects/reduction/doc/reduction.pdf) pdf NVIDIA, который графически объясняет все стратегии, которые вы использовали в своем коде


ваше понимание правильно. Сокращения продемонстрировали здесь в конечном итоге последовательность блочных сумм, депонированных в глобальной памяти.

чтобы суммировать все эти суммы блоков вместе, требуется некоторая форма глобальной синхронизации. Вы должны подождать, пока все блоки не будут завершены, прежде чем добавлять их суммы вместе. На данный момент у вас есть несколько вариантов, некоторые из которых:

  1. запустите новое ядро после основного ядра, чтобы суммировать блок-суммы вместе!--8-->
  2. добавить суммы блоков на хост
  3. используйте atomics, чтобы добавить суммы блоков вместе, в конце основного ядра
  4. используйте такой метод, как уменьшение threadfence чтобы добавить суммы блоков вместе в основное ядро.

Если вы ищете вокруг тега CUDA, вы можете найти примеры всех этих и обсуждения их плюсов и минусов. Чтобы увидеть, как основное ядро, которое вы разместили, используется для полного сокращения, посмотрите на параллельный код образца уменьшения.


Роберт Crovella уже ответил на этот вопрос, который в основном о понимании, а не производительность.

однако, для всех тех, кто сталкивается с этим вопросом, я просто хочу подчеркнуть, что тигренок делает имеющиеся характеристики уменьшения блока. Ниже я предоставляю простой пример работы о том, как использовать CUB's 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];
....