我刚接触OpenCL,并遇到以下问题:
我有一个大数组(6 * 1,000,000个浮点数)。对于数组的每个元素,我都需要进行计算。基本算法可在多达16个GPU(Tesla K80)上很好地工作:
1。)我为每个GPU设备创建数组的缓冲区对象和结果的缓冲区对象,并将其写入每个GPU内存。
2。)然后,为每个数组元素生成一个线程,并在GPU的内核内执行计算。
3。)将结果写入与全局线程ID相对应的结果数组元素。
4。)主机读取结果缓冲区。
我现在必须扩展此算法。实际上,一些数组元素(10-100)需要进行额外的计算才能产生额外的结果(另外12个浮点数)。
这是一些伪代码。
__kernel void calculation(__global float4 *input_array,
__global float4 *result_array){
int id = get_global_id(0);
//do calculation
float4 result = some_func(input_array[id]);
result_array[id] = result;
if(some_rare_condition){
//do another, much longer calculation
float4 result2 = another_func(input_array[id]);
}
}
问题是我只有一些额外的结果,我不知道什么是存储它们并使主机读取它们的最佳方法。
在计算出第一个结果之前,我不知道哪些数组元素需要额外的计算。
如果这是C ++,我将为附加结果创建一个向量,为索引创建一个向量。但是据我所知,OpenCL内核中没有动态内存容器。
如果我创建一个包含1000000个元素的第二个结果数组,并只写到所需的几个位置,那么当我将其传递回主机时,它将创建瓶颈。
如果我创建一个绝对小于要求的较小数组(例如1000个元素),则不确定如何让线程安全地对其进行写入。
答案 0 :(得分:1)
最简单的解决方案可能是使用use an atomic counter在较小的数组中分配索引。
项目在数组中出现的顺序是不可预测的,因此您还需要存储标识信息(例如原始id
),然后根据需要对它们进行排序。接下来处理此输出。
但是,这是否有效取决于许多因素。更糟的是,这些因素相互影响。
首先,听起来这第二个输出数组中要求某项的概率小于0.1%。像这样的小数目对原子有好处-越多的工作项想要增加该计数器,它们尝试同时这样做并相互阻塞并序列化的机会就越高。此外,发行很重要。任何群集都会对您不利。如果您的0.1%大多数情况下是相邻的,那么它们在工作组中也将相邻,并且工作组中的工作项通常在GPU上同步运行。因此,他们中的任何一个都需要互相等待才能完成增量。
另一方面,如果another_func()
在计算上是相当昂贵的(您的问题暗示这是昂贵的),那么平均分配是不好的,因为组中的大多数工作项都是在单个项目正在运行another_func()
并占据整个计算单元时空转。
可以通过简单的方法来抵消各种弊端:
another_func()
在第二个内核中,其中每个工作项都在需要进一步计算的项目之一上工作,因此所有工作项都运行another_func()
,这意味着内核已被完全占用。 当然可以根据需要将这两种变体结合起来,并且可能需要进一步完善,特别是如果您可以使用平台的探查器来查明瓶颈的话。