我需要与CUDA并行化一个嵌套循环:
for(int ix=0; ix<Nx; ix++) {
for(int iy=0; iy<Ny[ix]; iy++) {
SomeFunction(ix, iy);
...
其中Ny[]
是CUDA全局内存中的数组。这个循环在一次运行中被调用多次,Nx
并且Ny[]
的元素在不同的调用中发生变化,并且它们可能很大(Nx
从0到大约1百万,{{ 1}}从0到大约10000)。
原则上我可以使用CUDA内核,其中线程排列在由Ny
和Nx*max(Ny)
索引的大小为ix
的二维网格中,因此内核的计算成本为{{1 }}。问题在于,在我的情况下,iy
可能比O[Nx*max(Ny)/Ncores]
的平均值大一个数量级。在某些调用中,max(Ny)
的一些元素可能非常大(比如1000左右),而其他大多数元素都非常小。
我希望上面循环的良好并行实现具有Ny
的计算成本,在我的情况下会比Ny
小得多,但我不知道我这样做。我有一个模糊的想法,一个好的解决方案可以基于首先排序O[Nx*average(Ny)/Ncores]
的元素。
仅用于测试目的,这里是一段代码,它产生的Ny值与我的分布相似。
O[Nx*max(Ny)/Ncores]
答案 0 :(得分:1)
如果SomeFunction比简单的加载和存储计算成本更高,我会考虑创建一个int2数组,它可以存储SomeFunction()的执行参数。然后将此数组复制到设备并运行一个内核,该内核将从线程索引指示的位置读取数组中的参数,并使用它们执行SomeFunction():
#include<iostream>
#include<cuda_runtime.h>
int SomeFunctionHost(int x, int y)
{
return x*y;
}
__device__ int SomeFunction(int x, int y)
{
return x*y;
}
__global__ void executionKernel(int2 * args, int * results, int n)
{
int blockId = blockIdx.x;
int threadId = blockId * (blockDim.x * blockDim.y) + (threadIdx.y * blockDim.x) + threadIdx.x;
if(threadId < n)
{
int2 myArgs = args[threadId];
results[threadId] = SomeFunction(myArgs.x, myArgs.y);
}
}
int main ()
{
//Create execution parameters
int Nx_max=1000000;
int *Ny;
int Nx, i;
Ny=(int*)malloc(Nx_max*sizeof(int));
UpdateArray(Ny, &Nx);
int count = 0;
for(i=0; i<Nx; i++) {
count += Ny[i];
}
int2 * hParams, * dParams;
int * hResults, * dResults;
hParams = (int2*)malloc(count*sizeof(int2));
hResults = (int* )malloc(count*sizeof(int));
cudaMalloc( (void **) &dParams, count*sizeof(int2));
cudaMalloc( (void **) &dResults, count*sizeof(int));
cudaMemset( dResults, 0 , count * sizeof(int));
int index = 0;
for(i=0; i<Nx; i++)
{
for(int j=0; j<Ny[i];j++)
{
hParams[index].x = i;
hParams[index].y = j;
index++;
}
}
//Copy execution parameters to the device
cudaMemcpy(dParams, hParams, count * sizeof(int2), cudaMemcpyHostToDevice);
//Define the grid configuration
dim3 blockDim(32,32,1);
int gridLength = count/(blockDim.x*blockDim.y) + 1;
dim3 gridDim(gridLength, 1, 1);
//Run kernel
executionKernel<<<gridDim, blockDim>>>(dParams, dResults, count);
//Copy the results back to the host
cudaMemcpy(hResults, dResults, count * sizeof(int), cudaMemcpyDeviceToHost);
//TEST
for(int i=0;i<count;i++)
{
if(SomeFunctionHost(hParams[i].x, hParams[i].y) != hResults[i])
{
std::cout << "WRONG RESULT !" << std::endl;
}
}
std::cout << "DONE!" << std::endl;
free(hParams);
free(hResults);
free(dParams);
free(dResults);
return 0;
}
这样可以确保网格之间的工作分布均匀。我强烈建议您尝试不同的网格配置。也许尝试并行化参数生成阶段,看看它是否给你带来任何好处。玩得开心!