我接受了this link上解释的第一个问题的延续。
我提醒您,我想应用一种能够使用OpenCL进行多次减少的方法(我的GPU设备仅支持OpenCL 1.2)。我需要计算一个数组的总和减少来检查主循环的每次迭代的收敛标准,
目前,我只做了一个减少一个版本的版本(即一次迭代 )。在这个版本中,为简单起见,我使用了一个顺序CPU循环来计算每个部分和的总和,并得到总和的最终值。
根据我先前的建议,我的问题是我不知道如何通过第二次调用NDRangeKernel
函数(即第二次执行内核代码)来执行最终总和。
实际上,通过第二次调用,我将始终面临同样的问题,即获得部分和的总和(本身是从NDRangeKernel
的第一次调用计算出来的):它似乎是一个递归问题。
让我们从上图中得到一个例子:如果输入数组大小是10240000
而WorkGroup size
是16
,我们得到10000*2^10/2^4 = 10000*2^6 = 640000 WorkGroups
。
所以在第一次通话之后,我得到640000 partial sums
:如何处理所有这些部分金额的最终结果?如果我再次调用内核代码,例如WorkGroup size = 16
和全局size = 640000
,我将获得nWorkGroups = 640000/16 = 40000 partial sums
,所以我必须再次调用内核代码并重复此过程直到nWorkGroups < WorkGroup size
。
也许我不太了解第二阶段,主要是“两阶段缩减”(on this link, I think this is the case of searching for minimum into input array)的内核代码部分
__kernel
void reduce(__global float* buffer,
__local float* scratch,
__const int length,
__global float* result) {
int global_index = get_global_id(0);
float accumulator = INFINITY;
// Loop sequentially over chunks of input vector
while (global_index < length) {
float element = buffer[global_index];
accumulator = (accumulator < element) ? accumulator : element;
global_index += get_global_size(0);
}
// Perform parallel reduction
...
如果有人能够解释上面代码片段的内核代码的作用。
是否与第二阶段的减少有关系,即最终的结果?
如果您不理解我的问题,请随时向我询问更多详情。
由于
答案 0 :(得分:3)
如评论中所述:声明
如果输入数组大小为10240000且WorkGroup大小为16,则得到10000 * 2 ^ 10/2 ^ 4 = 10000 * 2 ^ 6 = 640000个工作组。
不正确。您可以选择“任意”工作组大小和“任意”数量的工作组。这里选择的数字可以针对目标设备定制。例如,设备可能具有某种本地存储器大小。可以使用clDeviceGetInfo
:
cl_ulong localMemSize = 0;
clDeviceGetInfo(device, CL_DEVICE_LOCAL_MEM_SIZE,
sizeof(cl_ulong), &localMemSize, nullptr);
考虑到每个工作组需要
的事实,这可用于计算本地工作组的规模sizeof(cl_float) * workGroupSize
本地内存的字节。
类似地,工作组的数量可以从其他设备特定参数导出。
关于减少本身的关键点是工作组大小不限制可以处理的数组的大小。我对整个算法的理解也有些困难,所以我试着在这里解释一下,希望一些图像可能胜过千言万语:
正如您所看到的,工作组的数量和工作组大小是固定的,与输入数组长度无关:即使我在示例中使用了3个大小为8的工作组(给出全局大小)如图24所示,可以处理长度为64的数组。这主要是由于第一个循环,它只是遍历输入数组,其“步长”等于全局工作大小(此处为24)。对于24个线程中的每个线程,结果将是一个累积值。然后这些会同时减少。