我正在尝试将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
[...]
左边是我得到的,而右边则是我想要的。 我应该修改什么?我应该每个块只使用一个线程吗?但这会降低表现吗?
答案 0 :(得分:1)
由于你不熟悉CUDA,你可以从构造至少dim/3
个线程开始,每个线程只填充一个点。
dim3 size(170, 170, 263);
每个块3个线程仍然太小而无法获得最佳性能。常见的选择是使用接近每个设备块最大线程数的2的幂。在warpSize
dim上使用.x
个线程是一种很好的做法。线程应使用3-D块和网格进行组织,以匹配您的循环x
,y
和z
:
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);