跳转到for循环Cuda中的下一个块

时间:2016-06-30 11:14:50

标签: c++ for-loop cuda

我正在尝试将c ++代码转换为Cuda代码,并且我有以下三重嵌套for循环,它将填充数组以进行进一步的OpenGL渲染(我只是创建一个坐标顶点数组):

for(int z=0;z<263;z++) {                    
   for(int y=0;y<170;y++) {
       for(int x=0;x<170;x++) {
           g_vertex_buffer_data_3[i]=(float)x+0.5f;
           g_vertex_buffer_data_3[i+1]=(float)y+0.5f;
           g_vertex_buffer_data_3[i+2]=-(float)z+0.5f; 
           i+=3;            
       }
   }
}

我希望获得更快的操作,因此我将使用Cuda进行上面列出的操作。我想为每个点创建一个块,因为每个点都有3个坐标,我想拥有每个3个线程的块。我想使用这个配置,因为我有一个7600700点的3d矩阵,所以我认为最逻辑的是创建一个由块组成的3d矩阵,然后在每个块中使用3个线程用于x,y,z每个点的坐标。我将c ++代码转换成了这个(这只是我用来理解如何使用Cuda的一个小程序,在这里我只使用了几点):

__global__ void mykernel(int k, float *buffer, int size) {
    const unsigned long int blockId = blockIdx.x + blockIdx.y * gridDim.x + gridDim.x * gridDim.y * blockIdx.z;

    const unsigned long int threadId = (blockId * blockDim.x + threadIdx.x)*blockDim.x;
    if(threadId<size ) {
      buffer[threadId]=blockIdx.x+0.5;
      buffer[threadId+1]=blockIdx.y+0.5;
      buffer[threadId+2]=blockIdx.z+0.5;
    }
 }

int main(void) {
  int dim=3*5*5*7;
  float* g_vertex_buffer_data_2 = new float[dim];
  float* g_vertex_buffer_data_3;
  int i=0;

  HANDLE_ERROR(cudaMalloc((void**)&g_vertex_buffer_data_3, sizeof(float)*dim));

  dim3 dimBlock(3);

  dim3 dimGrid(5,5,7);

  mykernel<<<dimGrid, dimBlock>>>(i, g_vertex_buffer_data_3, dim);

  HANDLE_ERROR(cudaMemcpy(g_vertex_buffer_data_2,g_vertex_buffer_data_3,sizeof(float)*dim,cudaMemcpyDeviceToHost));

  cudaFree(g_vertex_buffer_data_3);

  return 0;

}

使用此代码,我得到了一些不错的东西。问题是在if语句之后我希望代码“跳过”到下一个块,因为我得到了相同的结果三次(我有三个线程所以代码在跳转到之前通过它们中的每个下一个块)。 我尝试用一​​小段输出来解释自己:

g_buffer_data_2[0]=0.5        g_buffer_data_2[0]=0.5               
g_buffer_data_2[1]=0.5        g_buffer_data_2[1]=0.5             
g_buffer_data_2[2]=0.5        g_buffer_data_2[2]=0.5             
g_buffer_data_2[3]=0.5        g_buffer_data_2[3]=1.5             
g_buffer_data_2[4]=0.5        g_buffer_data_2[4]=0.5             
g_buffer_data_2[5]=0.5        g_buffer_data_2[5]=0.5             
g_buffer_data_2[6]=0.5        g_buffer_data_2[6]=2.5             
g_buffer_data_2[7]=0.5        g_buffer_data_2[7]=0.5             
g_buffer_data_2[8]=0.5        g_buffer_data_2[8]=0.5        
g_buffer_data_2[9]=1.5        g_buffer_data_2[9]=3.5         
g_buffer_data_2[10]=0.5       g_buffer_data_2[10]=0.5         
g_buffer_data_2[11]=0.5       g_buffer_data_2[11]=0.5
g_buffer_data_2[12]=1.5       g_buffer_data_2[12]=4.5                   
[...]

左边是我得到的,而右边则是我想要的。 我应该修改什么?我应该每个块只使用一个线程吗?但这会降低表现吗?

1 个答案:

答案 0 :(得分:1)

由于你不熟悉CUDA,你可以从构造至少dim/3个线程开始,每个线程只填充一个点。

dim3 size(170, 170, 263);

每个块3个线程仍然太小而无法获得最佳性能。常见的选择是使用接近每个设备块最大线程数的2的幂。在warpSize dim上使用.x个线程是一种很好的做法。线程应使用3-D块和网格进行组织,以匹配您的循环xyz

dim3 dimBlock(32, 4, 4);
dim3 dimGrid((size.x + dimBlock.x - 1) / dimBlock.x,
             (size.z + dimBlock.y - 1) / dimBlock.y,
             (size.z + dimBlock.z - 1) / dimBlock.z);

另一方面,使用float3简化索引是一个很好的情况:

float3* g_vertex_buffer_data_3;
cudaMalloc((void**) &g_vertex_buffer_data_3,
           sizeof(float3) * size.x * size.y * size.z);

所以内核应该是这样的,

__global__ void mykernel(float3 *buffer, dim3 size) {
  int x = blockDim.x * blockIdx.x + threadIdx.x;
  int y = blockDim.y * blockIdx.y + threadIdx.y;
  int z = blockDim.z * blockIdx.z + threadIdx.z;
  if (x < size.x && y < size.y && z < size.z) {
    float3 buf;
    buf.x = x + 0.5f;
    buf.y = y + 0.5f;
    buf.z = -z + 0.5f;
    buffer[(z * size.y + y) * size.x + x] = buf;
  }
}

这就是你启动它的方式。

mykernel<<<dimGrid, dimBlock>>>(g_vertex_buffer_data_3, size);