我正在实现一个OpenCL内核,每个线程都可能生成不同数量的数据。它基本上是一个半径搜索功能,因此每个点周围可以有不同数量的元素。
我当然可以运行两次,一次找出我需要多少元素并在C ++代码端分配,但这是一种可怕的方法。有没有办法可以在我的内核代码中“保存”我的状态,退出,重新分配拉出数据所需的资源,并且可能有另一个内核将数据拉出来?
答案 0 :(得分:2)
更多建议:
如果你们两个都知道:
那么分配最大可能输出可能是有意义的,并且还要确保写出实际使用了多少输出空间。在这种情况下,请注意内存大小限制。
如果您可以在工作组而不是在线程上更好地绑定输出大小,则将输出写入工作组本地内存并最终/偶尔将其刷新到主存储器(例如使用原子)
< / LI> 醇>此外,无论您选择做什么,都要尽量让每个线程尽可能地收集自己的寄存器中的结果,以减少在触发任何冲突/同步成本之前的需要。
答案 1 :(得分:2)
如果你要强有力地实现这一点,那么你需要有某种启发式来确定&#34;我的程序可能产生的最大可能输出是多少?&#34; 。我不知道你的算法的细节,所以知道确定这是多么复杂,这超出了我的能力。我的建议是找到一个&#34;剥离&#34;算法的版本,其唯一的任务是评估每个工作项,&#34;这会生成一个解决方案吗?如果是这样,则以原子方式递增全局变量&#34;。
//Host Code
cl_mem mem = clCreateBuffer(context, CL_MEM_READ_WRITE, 1 * sizeof(cl_long), nullptr, nullptr);
clSetKernelArg(kernel, /**/, sizeof(cl_mem), &mem);
clEnqueueNDRangeKernel(/*...*/);
cl_long num_of_solutions;
clEnqueueReadBuffer(queue, mem, true, 0, 1 * sizeof(cl_long), &num_of_solutions, nullptr, nullptr);
//Increase your memory on your final buffer to accomodate the number of solutions reported.
//Kernel Code
kernel void count_solutions(global long * num_of_solutions) {
size_t id = get_global_id(0);
/* Implementation is dependent on you, but 'get_number_of_generated_solutions'
* would, ideally, get the number of generated solutions *without* the heavy lifting
* associated with actually generating those solutions at all. But that's dependent on
* whether that's actually possible for your specific algorithm.
*/
int found_solutions = get_number_of_generated_solutions(id);
//not sure if you need to explicitly enable 64-bit atomics or not
atomic_add(num_of_solutions, found_solutions);
}
然后,根据该结果分配空间。
答案 2 :(得分:1)
您可以保存状态并重新创建缓冲区,但如果重复它将会很慢。
需要更少的内存,更多的开销,但可以通过并发操作隐藏,例如分4步(每个工作区的四分之一),一个重新创建缓冲区,其他运行内核,其他上传数据,其他下载结果。
您可以使用最大大小的缓冲区并使用其第一个元素来计算总虚拟分配,然后使用GPU上的前缀和计算每个工作项自己的堆栈起始点,或者只需使用cpu线程扫描。
前缀和部分需要多个(logn)内核步骤。
比重新创建缓冲区更好的性能。性能取决于元素分配到框。每盒仍然无限制分配*。
你可以为每个工作项提供一个固定的(2的非幂)元素,使用它们的第一个元素进行计数,剩下的元素用于元素(粒子?)索引保存。
这比前缀和版本更快,因为原子添加在本地完成(例如每个盒子N个粒子),并且不需要额外的前缀和操作。
没有每盒分配*灵活性,性能更稳定,可能是最快的(对于每个盒子的少量元素,如15)。
将粒子添加到地图中的框中的示例,其中每个框跨越4x4像素,并且地图包含128x128个框(512x512地图)
int addParticleToBox(__global int * box, int boxIndex, int particleIndex)
{
int newAllocIndex=atomic_add(&box[boxIndex*15],1/*size of index of element*/);
if(nexAllocIndex<(15))
box[boxIndex*15+newAllocIndex]=particleIndex;
}
__kernel void fillBoxes(__global int * box, __global float *x, __global float *y)
{
int i=get_global_id(0);
int boxX=floor(x[i])/4 ;
int boxY=floor(y[i])/4 ;
if(boxX>=0 && boxX< 128 && boxY>=0 && boxY<128)
{
int boxIndex=boxX+boxY*128;
addParticleToBox(box,boxIndex,i/*particle index*/);
}
}
然后当您需要相邻粒子时,获取粒子盒,获取该盒子的相邻盒子,读取0-index以了解每个粒子有多少粒子,从1到n循环以获得那些相邻粒子索引。