提高elementwise CUDA核算术强度的技术

问题描述

我编写了一个 CUDA 内核,用于计算一组源粒子和目标粒子之间的成对相互作用。

我的 M 目标粒子看起来像,

[[x_1,y_1,z_1],...,[x_m,y_m,z_m]]

我的N个源粒子看起来像

[[x_1,[x_n,y_n,z_n]]

与 M

我首先将所有源/目标数据传输到 GPU,然后循环处理源粒子的批次并评估与所有目标的成对交互。

像这样(在cupy语法中)

for i in range(n_blocks):
    left_idx = i*width
    right_idx = (i+1)*width
    gpu_func(
       grid_dimensions,block_dimensions,targets,sources[left_idx:right_idx,:],width
    )

其中 sourcestargets 是 GPU 上包含源数据和目标数据的数组。

我的问题是我该怎么做才能避免这个循环?我是 CUDA 的新手。我的想法是检查来自每个线程的全局线程索引是否满足我在主机设备循环中上面提到的“left_idx”和“right_index”条件,这是正确的吗?有没有更好的方法来做到这一点?我觉得我目前对 GPU 的利用严重不足,因为对于我的问题,源/目标的数量明显少于我机器上的 CUDA 核心数量

解决方法

我的问题是我该怎么做才能避免这个循环?我是 CUDA 的新手。

并行编程中的一个基本概念 - CUDA 或其他 - 不是循环(按时间顺序发生),而是让不同的处理元素/线程/自动机/并行处理所有“循环迭代”的任何工作:N 个处理元素每个处理 1 件工作,而不是 1 个处理元件使用循环执行 N 件工作。

在 CUDA 中,在您使用 M 和 N 的情况下,这可能意味着拥有一个 M x N 的 2D 网格,每个 CUDA 线程处理一对源和目标粒子集。或者一个更小的网格,每个 CUDA 线程在几对上运行一个循环,但对更少。

您可能还记得 CUDA vectorAdd 示例,它计算两个向量的元素相加。这是一个可能的内核:

__global__ void vecAdd(int *A,int *B,int *C,int N)
{
   int i = blockIdx.x * blockDim.x + threadIdx.x;
   C[i] = A[i] + B[i]; 
}

你看到了吗?没有任何循环。网格中的大量线程确保每个可能的 i - 0 和 N-1 之间 - 都计算了 C 的相应元素。