c++ CUDA:使用线性化2D共享内存的数组中所有元素的总和

dddzy1tm  于 2023-05-20  发布在  其他
关注(0)|答案(1)|浏览(107)

我是CUDA的新手,一般来说是算法。有人能告诉我,如果我这样做是正确的,或者如果有一个更好的方法来做到这一点。一个问题是代码的输入和输出应该在GPU上,这样主机和设备之间就没有内存复制。

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>
#include <stdint.h>

#include <iostream>

#define TILE_WIDTH 8

__global__ void gpu_sumElements(int height, int width, float *in, float *out){

    extern __shared__ float cache[];

    int w = blockIdx.x * blockDim.x + threadIdx.x; // Col // width
    int h = blockIdx.y * blockDim.y + threadIdx.y;

    int index = h * width + w;
    int cacheIndex = threadIdx.y * blockDim.x + threadIdx.x;

    float temp = 0;

    if ((w < width) && (h < height)){
        temp += in[index];
        //index += (height * width);
    }

    cache[cacheIndex] = temp;
    __syncthreads();

    int i = (blockDim.x * blockDim.y) / 2;
    while (i != 0){
        if (cacheIndex < i)
            cache[cacheIndex] += cache[cacheIndex + i];
        __syncthreads();
        i /= 2;
    }

    if (cacheIndex == 0)
        out[blockIdx.y * gridDim.x + blockIdx.x] = cache[0];
}

int main(){
                                                                        // Initial Parameters       
    int width = 2363;
    int height = 781;
    float my_sum = 0;

    int block_height = (height - 1) / TILE_WIDTH + 1;
    int block_width = (width - 1) / TILE_WIDTH + 1;

    dim3 dimGrid(block_width, block_height, 1);
    dim3 dimBlock(TILE_WIDTH, TILE_WIDTH, 1);

    int sharedMemSize = TILE_WIDTH * TILE_WIDTH * sizeof(float);
                                                                        // Initialize host arrays
    float *test_array = new float[height * width];
    float *out = new float[height * width];
    for (int i = 0; i < (height * width); i++)
        test_array[i] = 1.0f;
                                                                        // Initialize device arrays 
    float *gpu_temp_array;
    float *gpu_out;
    cudaMalloc((void **)&gpu_temp_array, (height * width * sizeof(float)));
    cudaMalloc((void **)&gpu_out, (height * width * sizeof(float)));
    cudaMemcpy(gpu_out, test_array, (height * width * sizeof(float)), cudaMemcpyHostToDevice);

                                                                        // Copy these, need them elsewhere
    float sum_height = height; 
    float sum_width = width ;
    dim3 sum_dimGrid = dimGrid;
    int i = (height * width); 

                                                                        // Launch kernel, get & print results
    while (i != 0){
        gpu_sumElements<<<sum_dimGrid, dimBlock, sharedMemSize>>>(sum_height, sum_width, gpu_out, gpu_temp_array);
        cudaMemcpy(gpu_out, gpu_temp_array, (sum_height * sum_width * sizeof(float)), cudaMemcpyDeviceToDevice);
        cudaMemset(gpu_temp_array, 0, (height * width * sizeof(float)));

        sum_height = ceil(sum_height/TILE_WIDTH);
        sum_width = ceil(sum_width/TILE_WIDTH);;
        sum_dimGrid.x = (sum_width - 1) / TILE_WIDTH + 1; 
        sum_dimGrid.y = (sum_height - 1) / TILE_WIDTH + 1;
        i /= TILE_WIDTH*TILE_WIDTH;
    }

    cudaMemcpy(out, gpu_out, (height * width * sizeof(float)), cudaMemcpyDeviceToHost);
    std::cout << out[0] << std::endl << std::endl;  

    delete[] test_array;
    delete[] out;
    cudaFree(gpu_out);
    cudaFree(gpu_temp_array);

    system("pause");
    return 0;
}
li9yvcax

li9yvcax1#

通常,使用多个内核启动来产生一个(最终)结果的并行归约通常是不必要的。cuda示例代码和accompanying PDF很好地记录了生成组织良好的并行约简的过程,该约简只需要为任意数据大小启动两次内核。
要创建仅使用单个内核启动的并行缩减,至少有两种常见方法:
1.使用所谓的“threadfence reduction”方法。这也在CUDA示例代码中捕获。在该方法中,通过跟踪“内核排出”来执行最终的归约阶段。具体来说,每个线程块在完成其工作时更新“完成计数”变量(原子地)。由于启动的线程块的数量是已知的,因此线程块可以确定它是否是最后一个完成的线程块。如果是,则该线程块将其他线程块产生的所有中间结果相加,这些结果现在被写入全局内存。“threadfence”绰号是由于每个线程块必须确保其部分结果在退出之前在全局内存中可用(使用threadfence intrinsic)。这种方法可以处理“任意”约简。
1.让每个线程块(中的单个线程)使用其自己的部分结果原子地更新最终的内核范围的结果。这仅对于为其提供对应原子函数的约简是方便地可实现的,例如求和、求最大值、求最小值等。
上述任何一种方法都将受益于CUDA并行缩减示例代码中涵盖的基本技术,特别是将线程块的数量减少到最小值,这仍然允许充分利用GPU。这种优化允许最小数量的原子操作。考虑到这些优化,减少可以更快,并且“更简单”(例如单个内核调用,没有中间结果的大量主机管理),而不是相应的2内核或多内核缩减。

相关问题