【问题标题】:CUB reduction using 2D grid of blocks使用 2D 块网格减少 CUB
【发布时间】:2018-06-02 08:37:31
【问题描述】:

我正在尝试使用 CUB 减少方法进行总和。

最大的问题是: 我不确定在使用二维网格时如何将每个块的值返回给主机。

#include <iostream>
#include <math.h>
#include <cub/block/block_reduce.cuh>
#include <cub/block/block_load.cuh>
#include <cub/block/block_store.cuh>
#include <iomanip>

#define nat 1024
#define BLOCK_SIZE 32
#define GRID_SIZE 32

struct frame
{
   int  natm;
   char  title[100];
   float conf[nat][3];
};

using namespace std;
using namespace cub;

__global__
void add(frame* s, float L, float rc, float* blocksum)
{
int i = blockDim.x*blockIdx.x + threadIdx.x;
int j = blockDim.y*blockIdx.y + threadIdx.y;

float E=0.0, rij, dx, dy, dz;

// Your calculations first so that each thread holds its result
  dx = fabs(s->conf[j][0] - s->conf[i][0]);
  dy = fabs(s->conf[j][1] - s->conf[i][1]);
  dz = fabs(s->conf[j][2] - s->conf[i][2]);
  dx = dx - round(dx/L)*L;
  dy = dy - round(dy/L)*L;
  dz = dz - round(dz/L)*L;

   rij = sqrt(dx*dx + dy*dy + dz*dz);

  if ((rij <= rc) && (rij > 0.0))
    {E =  (4*((1/pow(rij,12))-(1/pow(rij,6))));}

//  E = 1.0;
__syncthreads();
// Block wise reduction so that one thread in each block holds sum of thread results

typedef cub::BlockReduce<float, BLOCK_SIZE, BLOCK_REDUCE_RAKING, BLOCK_SIZE> BlockReduce;

__shared__ typename BlockReduce::TempStorage temp_storage;

float aggregate = BlockReduce(temp_storage).Sum(E);

if (threadIdx.x == 0 && threadIdx.y == 0)
    blocksum[blockIdx.x*blockDim.y + blockIdx.y] = aggregate;

}

int main(void)
{
  frame  * state = (frame*)malloc(sizeof(frame));

  float *blocksum = (float*)malloc(GRID_SIZE*GRID_SIZE*sizeof(float));

  state->natm = nat; //inicializando o numero de atomos;

  char name[] = "estado1";
  strcpy(state->title,name);

  for (int i = 0; i < nat; i++) {
    state->conf[i][0] = i;
    state->conf[i][1] = i;
    state->conf[i][2] = i;
  }

  frame * d_state;
  float *d_blocksum;

  cudaMalloc((void**)&d_state, sizeof(frame));

  cudaMalloc((void**)&d_blocksum, ((GRID_SIZE*GRID_SIZE)*sizeof(float)));

  cudaMemcpy(d_state, state, sizeof(frame),cudaMemcpyHostToDevice);


  dim3 dimBlock(BLOCK_SIZE,BLOCK_SIZE);
  dim3 gridBlock(GRID_SIZE,GRID_SIZE);

  add<<<gridBlock,dimBlock>>>(d_state, 3000, 15, d_blocksum);

  cudaError_t status =  cudaMemcpy(blocksum, d_blocksum, ((GRID_SIZE*GRID_SIZE)*sizeof(float)),cudaMemcpyDeviceToHost);

  float Etotal = 0.0;
  for (int k = 0; k < GRID_SIZE*GRID_SIZE; k++){
       Etotal += blocksum[k];
  }
 cout << endl << "energy: " << Etotal << endl;

  if (cudaSuccess != status)
  {
    cout << cudaGetErrorString(status) << endl;
  }

 // Free memory
  cudaFree(d_state);
  cudaFree(d_blocksum);

  return cudaThreadExit();
}

发生的情况是,如果GRID_SIZE 的值与BLOCK_SIZE 相同,如上所述。计算是正确的。但是如果我改变GRID_SIZE的值,结果就会出错。这让我认为错误出在这段代码中:

blocksum[blockIdx.x*blockDim.y + blockIdx.y] = aggregate;

这里的想法是返回一个一维数组,其中包含每个块的总和。

我不打算更改 BLOCK_SIZE 的值,但 GRID_SIZE 的值取决于我正在查看的系统,我打算使用大于 32 的值(始终是该值的倍数)。

我找了一些使用 2D 网格和 CUB 的例子,但没有找到。

我真的是 CUDA 程序的新手,也许我犯了一个错误。

edit:我放了完整的代码。 作为比较,当我为串行程序计算这些精确值时,它给了我能量:-297,121

【问题讨论】:

  • 请提供minimal reproducible example。当您针对不起作用的代码寻求 SO 帮助时,您应该提供一个。请参阅第 1 项here。此外,任何时候您在使用 CUDA 代码时遇到问题,最好使用proper CUDA error checking 并使用cuda-memcheck 运行您的代码。即使您不理解错误输出,它也可能对那些试图帮助您的人有用。

标签: cuda cub


【解决方案1】:

可能主要问题是您的输出索引不正确。这是代码的简化版本,展示了任意 GRID_SIZE 的正确结果:

$ cat t1360.cu
#include <stdio.h>
#include <cub/cub.cuh>
#define BLOCK_SIZE 32
#define GRID_SIZE 25
__global__
void add(float* blocksum)
{
   float E = 1.0;
  // Block wise reduction so that one thread in each block holds sum of thread results
    typedef cub::BlockReduce<float, BLOCK_SIZE, cub::BLOCK_REDUCE_RAKING, BLOCK_SIZE> BlockReduce;

    __shared__ typename BlockReduce::TempStorage temp_storage;
    float aggregate = BlockReduce(temp_storage).Sum(E);
    __syncthreads();
    if (threadIdx.x == 0 && threadIdx.y == 0)
        blocksum[blockIdx.y*gridDim.x + blockIdx.x] = aggregate;
}

int main(){

  float *d_result, *h_result;
  h_result = (float *)malloc(GRID_SIZE*GRID_SIZE*sizeof(float));
  cudaMalloc(&d_result, GRID_SIZE*GRID_SIZE*sizeof(float));
  dim3 grid  = dim3(GRID_SIZE,GRID_SIZE);
  dim3 block = dim3(BLOCK_SIZE, BLOCK_SIZE);
  add<<<grid, block>>>(d_result);
  cudaMemcpy(h_result, d_result, GRID_SIZE*GRID_SIZE*sizeof(float), cudaMemcpyDeviceToHost);
  cudaError_t err = cudaGetLastError();
  if (err != cudaSuccess) {printf("cuda error: %s\n", cudaGetErrorString(err)); return -1;}
  float result = 0;
  for (int i = 0; i < GRID_SIZE*GRID_SIZE; i++) result += h_result[i];
  if (result != (float)(GRID_SIZE*GRID_SIZE*BLOCK_SIZE*BLOCK_SIZE)) printf("mismatch, should be: %f, was: %f\n", (float)(GRID_SIZE*GRID_SIZE*BLOCK_SIZE*BLOCK_SIZE), result);
  else printf("Success\n");
  return 0;
}

$ nvcc -o t1360 t1360.cu
$ ./t1360
Success
$

我对您的内核代码所做的重要更改是在输出索引中:

blocksum[blockIdx.y*gridDim.x + blockIdx.x] = aggregate;

我们想要一个模拟二维索引到一个宽度和高度为GRID_SIZE 的数组中,每个点包含一个float 数量。因此这个数组的宽度由gridDim.x(不是blockDim)给出。 gridDim 变量以块的形式给出了网格的尺寸 - 这与我们的结果数组的设置方式完全一致。

如果GRID_SIZEBLOCK_SIZE 不同,您发布的代码将失败(例如,如果GRID_SIZE 小于BLOCK_SIZEcuda-memcheck 将显示非法访问,如果GRID_SIZE 大于@ 987654336@ 那么这个索引错误将导致块在输出数组中覆盖彼此的值)因为blockDimgridDim 之间的这种混淆。

另请注意,float 操作通常只有大约 5 个十进制数字的精度。小数点后第 5 位或第 6 位的微小差异可能归因于order of operations differences when doing floating-point arithmetic。您可以通过切换到 double 算法来证明这一点。

【讨论】:

  • 很抱歉没有问得很清楚。我会更加关注接下来的帖子。非常感谢您的帮助,您的 cmets 帮助我澄清了一些令人困惑的细节。
  • 你不必道歉。如果您正在寻求帮助,并且您对您提出的请求做出了回应,那么这几乎是任何人都可以要求的。如果您继续使用 SO,您将学习事物的节奏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-20
  • 2014-10-01
  • 2011-10-13
  • 1970-01-01
相关资源
最近更新 更多