Parallel processing 理解CUDA索引

Parallel processing 理解CUDA索引,parallel-processing,cuda,Parallel Processing,Cuda,我继承了一些需要处理的CUDA代码,但其中的一些索引工作让我感到困惑 一个简单的例子是数据的标准化。假设我们有一个共享数组a[2*N],它是一个2xN形状的矩阵,已经展开为一个数组。然后我们有归一化平均值和标准偏差:norm\u表示[2]和norm\u-stds[2]。目标是以并行的方式对数据进行标准化。一个最简单的例子是: __global__ void normalise(float *data, float *norm, float *std) { int tdy = threadIdx.

我继承了一些需要处理的CUDA代码,但其中的一些索引工作让我感到困惑

一个简单的例子是数据的标准化。假设我们有一个共享数组
a[2*N]
,它是一个2xN形状的矩阵,已经展开为一个数组。然后我们有归一化平均值和标准偏差:
norm\u表示[2]
norm\u-stds[2]
。目标是以并行的方式对数据进行标准化。一个最简单的例子是:

__global__ void normalise(float *data, float *norm, float *std) {
int tdy = threadIdx.y;

for (int i=tdy; i<D; i+=blockDim.y)
  data[i] = data[i] * norm[i] + std[i];
}

int main(int argc, char **argv) {

// generate data
int N=100;
int D=2;
MatrixXd A = MatrixXd::Random(N*D,1);
MatrixXd norm_means = MatrixXd::Random(D,1);
MatrixXd norm_stds = MatrixXd::Random(D,1);

// transfer data to device
float* A_d;
float* norm_means_d;
float* nrom_stds_d;
cudaMalloc((void **)&A_d, N * D * sizeof(float));
cudaMalloc((void **)&norm_means_d, D * sizeof(float));
cudaMalloc((void **)&norm_stds_d, D * sizeof(float));
cudaMemcpy(A_d, A.data(), D * N * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(norm_means_d, norm_means.data(), D * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(norm_stds_d, norm_stds.data(), D * sizeof(float), cudaMemcpyHostToDevice);

// Setup execution
const int BLOCKSIZE_X = 8;
const int BLOCKSIZE_Y = 16;
const int GRIDSIZE_X = (N-1)/BLOCKSIZE_X + 1;
dim3 dimBlock(BLOCKSIZE_X, BLOCKSIZE_Y, 1);
dim3 dimGrid(GRIDSIZE_X, 1, 1);

normalise<<dimGrid, dimBlock, 0>>>(A_d, norm_means_d, norm_stds_d);

}
程序停止工作,只输出乱七八糟的数据

有人能解释我为什么会有这种行为吗


PS.我对CUDA非常陌生

拥有一个二维内核来对数组执行元素操作确实是毫无意义的。也没有理由在8x16大小的块中工作。但是修改后的内核只使用第二维度(y);这可能就是它不起作用的原因。您可能只需要使用第一个维度(x)

但是,将Y尺寸用于实际的第二个尺寸是合理的,例如:

__global__ void normalize(
          float __restrict *data,
    const float __restrict *norm,
    const float __restrict *std) 
{
    auto pos = threadIdx.x + blockDim.x * blockIdx.x;
    auto d = threadIdx.y + blockDim.y * blockIdx.y; // or maybe just threadIdx.y;
    data[pos + d * N] = data[pos + d * N] * norm[d] + std[d];
}
其他需要考虑的问题:

  • 我在指针中添加了
    \u restrict
    。相关的时候一定要这样做
  • 让一个线程处理多个数据元素是一个好主意,但您应该在更长的维度中实现这一点,线程可以重用其norm和std值,而不是每次都从内存中读取它们

    • 使用二维内核对数组执行元素操作确实毫无意义。也没有理由在8x16大小的块中工作。但是修改后的内核只使用第二维度(y);这可能就是它不起作用的原因。您可能只需要使用第一个维度(x)

      但是,将Y尺寸用于实际的第二个尺寸是合理的,例如:

      __global__ void normalize(
                float __restrict *data,
          const float __restrict *norm,
          const float __restrict *std) 
      {
          auto pos = threadIdx.x + blockDim.x * blockIdx.x;
          auto d = threadIdx.y + blockDim.y * blockIdx.y; // or maybe just threadIdx.y;
          data[pos + d * N] = data[pos + d * N] * norm[d] + std[d];
      }
      
      其他需要考虑的问题:

      • 我在指针中添加了
        \u restrict
        。相关的时候一定要这样做
      • 让一个线程处理多个数据元素是一个好主意,但您应该在更长的维度中实现这一点,线程可以重用其norm和std值,而不是每次都从内存中读取它们

      感谢您的快速回答。根据您的评论,那么我发布的原始内核(在第一个代码块中)也不应该工作,因为它也只使用第二个维度,但它确实工作。为什么?嗯。嗯,事实上,我不知道。看起来不应该。你能更新你的程序,包括一个结果检查吗?是的,我在提交这个问题之前已经做了。我得到的结果是expected@imgeorgiev:我的意思是,在这里更新您的程序,以便我们可以复制。谢谢您的快速回答。根据您的评论,那么我发布的原始内核(在第一个代码块中)也不应该工作,因为它也只使用第二个维度,但它确实工作。为什么?嗯。嗯,事实上,我不知道。看起来不应该。你能更新你的程序,包括一个结果检查吗?是的,我在提交这个问题之前已经做了。我得到的结果是expected@imgeorgiev:我的意思是,在这里更新你的程序,这样我们就可以复制。CUDA内核中的D也是D==2吗?@Sebastian是的。在这个简单的例子中,我刚刚省略了其中的细节。你能插入一个printf()吗?奇怪的是,当只访问数据[0]和数据[1]时,数据的计算是正确的。CUDA内核中的D也是D==2吗?@Sebastian是的。在这个简单的例子中,我刚刚省略了其中的细节。你能插入printf()吗?奇怪的是,当只访问数据[0]和数据[1]时,数据的计算是正确的。