c++ - CUDA Vector Reduction 处理长度小于 512 的 vector ?

标签 c++ cuda gpu

我正在研究 NVIDIA 的并行 vector_reduction 算法教程,以使用 CUDA C++ API 实现该算法。我已经实现了该算法,但它仅适用于固定为 512 的 vector 长度。我无法弄清楚如何让它适用于小于 512 的 vector ?我希望它适用于任意大小,即 324、123、23。

#include <stdio.h>

#define NUM_ELEMENTS 512

__global__ void reduction(float *g_data, int n)
{
    __shared__ float partialSum[NUM_ELEMENTS];

    int tx = threadIdx.x;
    int i = tx + blockIdx.x * blockDim.x;

    if (i < n) {
        partialSum[tx] = g_data[i];
    }

    int stride;
    for (stride = blockDim.x/2; stride > 0;  stride >>= 1) {
        __syncthreads();
        if (tx < stride) {
           partialSum[tx] += partialSum[tx + stride];
        }
    }

    if (tx == 0) {
        g_data[blockIdx.x] = partialSum[tx];
    }
}

float computeOnDevice(float* h_data, int num_elements)
{
    float* d_data = NULL;
    float result;

    // Memory allocation on device side
    cudaMalloc((void**)&d_data, sizeof(float)*num_elements);

    // Copy from host memory to device memory
    cudaMemcpy(d_data, h_data, num_elements * sizeof(float), cudaMemcpyHostToDevice );

    dim3 blockSize, gridSize;

    // Number of threads in each thread block
    blockSize = dim3(num_elements, 1, 1);

    // Number of thread blocks in grid
    gridSize = dim3(1, 1, 1);

    // Invoke the kernel
    reduction<<<gridSize, blockSize>>>(d_data, num_elements);

    // Copy from device memory back to host memory
    cudaMemcpy(&result, d_data, sizeof(float), cudaMemcpyDeviceToHost);
    cudaFree(d_data);
    cudaDeviceReset();
    return result;
}

int main() {

    float *data = new float[NUM_ELEMENTS];
    for (int i = 0; i < NUM_ELEMENTS; i++) data[i] = 1;
    float r = computeOnDevice(data, NUM_ELEMENTS);
    printf(" result = %f\n" , r);
}

最佳答案

您的代码 100% 正确。问题是您的位移不考虑数组的最后一部分。您可以通过人为地将数组扩展到 2 的下一个幂来轻松解决此问题。这样您的整个数组就会减少,额外的“元素”(它们实际上并不存在)将被忽略。

#include <math.h>

__global__ void reduction(float *g_data, int n){
    // figure out exponent of next larger power of 2
    int exponent = ceilf(log2f(n));
    // calculate next larger power of 2
    int size = (int)powf(2, exponent);
    __shared__ float partialSum[NUM_ELEMENTS];

    int tx = threadIdx.x;
    int i = tx + blockIdx.x * blockDim.x;

    if (i < n){
        partialSum[tx] = g_data[i];
    }

    for (int stride = size / 2; stride > 0; stride >>= 1){
        __syncthreads();

        if (tx < stride) {
            // all threads that run out of bounds do nothing
            // equivalent to adding 0
            if((tx + stride) < n)
                partialSum[tx] += partialSum[tx + stride];
        }
    }

    if (tx == 0){
        g_data[blockIdx.x] = partialSum[tx];
    }
}

编辑

关于您的评论,这种归约方法永远不适用于在多个 block 中归约的数组。因此,对于计算能力 1.0-1.3,您可以减少的最大数组是 512 个元素,对于 >1.3 的计算能力,您最多可以减少 1024 个元素,这是每个 block 的最大线程数。

这是因为__shared__ 内存在线程之间共享而不是 block 。因此,要减少分散在多个 block 上的数组,您需要对数组进行分区,以便每个 block 减少一个 block ,然后利用 __global__ 内存来减少所有 block 的值。但是,__global__ 内存比(片上)__shared__ 内存慢大约 10-20 倍,因此一旦开始使用大量 block ,这将变得非常低效.

另一种方法是让每个线程处理多个索引,但是,最终您的 partialSum 数组将不再适合共享内存并溢出到全局内存中。这种方法也意味着您永远不能使用超过 512(或 1024)个线程,这违背了使用 CUDA 的目的,CUDA 依赖于运行大量线程来隐藏延迟并使从主机到设备的昂贵内存传输值得.

关于c++ - CUDA Vector Reduction 处理长度小于 512 的 vector ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54758305/

相关文章:

c++ - 比较类的 vector 时如何定义运算符 ==?

c++ - lambda 捕获变量的规则

c++ - 如何使用 C 链接制作 CUDA 目标文件?

performance - 使用 OpenGL、GLSL 和帧缓冲对象在 GPU 上进行图像处理 - 有关性能的问题

gpu - 如何在 Pytorch 中修复 "RuntimeError: CUDA error: device-side assert triggered"

c++ - 将 UART 寄存器 UDR0 与无符号字符数组进行比较

c++ - CvNormalBayesClassifier 训练误差

c++ - 调用函数时 CUDA/GPU 中的异常错误

c++ - 如何将使用 CUDA 的 C++ 程序转换为 MEX

python - 更新我的 NVIDIA 版本过时错误