【发布时间】:2022-11-25 03:17:30
【问题描述】:
我想在 CUDA(Pascal 架构)中并行化以下 6D 嵌套 for 循环。
const int NX = 250, NY = 250, NZ = 250, NA = 100, NB = 100, NC = 100;
float data_out[NX * NY * NZ];
float data_in[NA * NB * NC];
float datax[NX];
float datay[NY];
float dataz[NZ];
for (int ix = 0; ix < Nx; ix++)
{
for (int iy = 0; iy < Ny; iy++)
{
for (int iz = 0; iz < Nz; iz++)
{
float result = 0.0f;
for (int ia = 0; ia < NA; ia++)
{
for (int ib = 0; ib < NB; ib++)
{
for (int ic = 0; ic < NC; ic++)
{
// some exemplary computation (see kernel)
}
}
}
data_out[iz + iy * NZ + ix * (NZ * NY)] = result;
}
}
}
目前,我实现了一个执行内部 3D 嵌套 for 循环(循环变量ia、ib、ic)的内核,也就是说,到目前为止我还没有使用并行归约。因此,每个内核计算NA * NB * NC = 1000000值的总和。
编辑:for 循环中的计算已更新以考虑值的任何非线性组合,即不能在 for 循环外计算值
__global__ void testKernel
(
float *data_out,
const float *data_in,
const float *datax,
const float *datay,
const float *dataz,
const int NX,
const int NY,
const int NZ,
const int NA,
const int NB,
const int NC
)
{
int ix = threadIdx.x + blockIdx.x*blockDim.x;
int iy = threadIdx.y + blockIdx.y*blockDim.y;
int iz = threadIdx.z + blockIdx.z*blockDim.z;
if (ix >= NX || iy >= NY || iz >= NZ)
return;
float3 xyz = make_float3(datax[ix], datay[iy], dataz[iz]);
float result = 0.0f;
for (int ia = 0; ia < NA; ia++)
{
for (int ib = 0; ib < NB; ib++)
{
for (int ic = 0; ic < NC; ic++)
{
// some exemplary nonlinear computation to show memory access
result += nonlinear_combination(data_in[ic + ib * NC + ia * (NC * NB)], xyz, ia, ib, ic);
}
}
}
data_out[iz + iy * NZ + ix * (NZ * NY)] = result;
}
int main()
{
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
const int NX = 250, NY = 250, NZ = 250, NA = 100, NB = 100, NC = 100;
float *d_data_out, *d_data_in, *d_datax, *d_datay, *d_dataz;
cudaMalloc((void**)&d_data_out, NX * NY * NZ * sizeof(float));
cudaMalloc((void**)&d_data_in, NA * NB * NC * sizeof(float));
cudaMalloc((void**)&d_datax, NX * sizeof(float));
cudaMalloc((void**)&d_datay, NY * sizeof(float));
cudaMalloc((void**)&d_dataz, NZ * sizeof(float));
dim3 blockSize(8, 8, 8);
dim3 gridSize(128, 128, 64);
cudaEventRecord(start);
testKernel<<<gridSize, blockSize>>>(d_data_out, d_data_in, d_datax, d_datay, d_dataz, NX, NY, NZ, NA, NB, NC);
cudaEventRecord(stop);
cudaEventSynchronize(stop);
float milliseconds = 0;
cudaEventElapsedTime(&milliseconds, start, stop);
printf("Elapsed time: %.2f ms\n", milliseconds);
cudaFree(d_data_out);
cudaFree(d_data_in);
cudaFree(d_datax);
cudaFree(d_datay);
cudaFree(d_dataz);
return 0;
}
由于外部 for 循环 (NX * NY * NZ = 15625000) 的总迭代次数已经高于并行线程的总数,因此并行化内部 for 循环以及使用并行缩减是否有任何好处?
另外,如何优化内存访问?确保块中的每个线程都访问数据的相同部分并将这部分数据复制到共享内存可能是有益的,对吗?
【问题讨论】:
-
内存读取是昂贵的。在你的循环中,你不必要地多次阅读
datam[],当它没有改变时。 -
@AnderBiguri 如何防止内存读取?我总是访问不同的索引,所以我不确定如何摆脱内存读取。
-
您可以将
datax[ix] + datay[iy] + dataz[iz]分解为两个非常基本的操作。 IE。第一个操作是完全减少data_in,第二个操作是计算datax[ix] + datay[iy] + dataz[iz]并根据减少的结果对其进行缩放。还是我忽略了什么? -
然后要优化第二个操作,您可以将所需的
datax、datay和dataz协作加载到共享内存中,以便合并对全局内存的所有访问。 -
@brnk 在cuda 代码中,只是从三个循环中获取
(datax[ix] + datay[iy] + dataz[iz])?您正在运行该行 100^3 次,而不是 1 次。