Opencl 全局工作大小和本地工作大小对应用程序逻辑有影响吗?

Opencl 全局工作大小和本地工作大小对应用程序逻辑有影响吗?,opencl,Opencl,我试图理解OpenCL中所有不同维度的参数是如何组合在一起的。若我的问题不清楚,部分是因为一个结构良好的问题需要一些我并没有的答案 work_dim、global_work_size和local_work_size如何共同创建内核中使用的执行空间?例如,如果我将work_dim 2设置为2,那么我可以 get_global_id(0); get_global_id(1); 我可以使用全局工作大小将这两个维度分成n个工作组,对吗?所以如果我把全局的大小变成这样 那么每个维度将有4个工作组,总共8

我试图理解OpenCL中所有不同维度的参数是如何组合在一起的。若我的问题不清楚,部分是因为一个结构良好的问题需要一些我并没有的答案

work_dim、global_work_size和local_work_size如何共同创建内核中使用的执行空间?例如,如果我将work_dim 2设置为2,那么我可以

get_global_id(0);
get_global_id(1);
我可以使用全局工作大小将这两个维度分成n个工作组,对吗?所以如果我把全局的大小变成这样

那么每个维度将有4个工作组,总共8个?但是,作为一个初学者,我只使用全局id作为索引,所以不管怎样,只有全局id才是问题所在。你可以告诉我,我对这一切都很困惑,所以你能提供的任何帮助都会……帮助



既然您自己说您对执行空间中涉及的概念有点困惑,那么在回答您的问题之前,我将尝试对它们进行总结,并给出一些示例

螺纹/工作项组织在一个NDRange中,该NDRange可被视为1、2、3 DIM的网格。 NDRange主要用于将每个线程映射到每个线程都必须处理的数据块。因此,每个线程都应该被唯一标识,并且线程应该知道它是哪一个线程以及它在NDRange中的位置。还有内置函数的工作项。所有线程都可以调用这些函数,以向它们提供有关自身及其所在区域的信息

尺寸: 如前所述,NDRange最多可以有3个维度。因此,如果按这种方式设置尺寸:

size_t global_work_size[2] = { 4, 4 };
这并不意味着每个维度将有4个工作组,总共8个,但您的NDRange中将有4*4,即16个线程。这些螺纹将排列成“正方形”,侧面为4个单元。使用
uint get\u work\u dim()
函数,工作项可以知道nRange由多少个维度组成

全球规模: 线程还可以使用
size\u t get\u global\u size(uint D)
查询特定维度的NDRange有多大。因此,他们可以知道“直线/正方形/矩形/立方体”的范围有多大

全局唯一标识符: 由于该组织,每个线程都可以用对应于特定维度的索引进行唯一标识。因此,螺纹(2,1)指的是2D范围第三列第二行中的螺纹。内核中使用函数
size\u t get\u global\u id(uint D)
来查询线程的id

工作组(或本地)大小: NDRange可以拆分为称为工作组的较小组。这是您所指的本地工作大小,它也(逻辑上)最多有3个维度。请注意,对于低于2.0的OpenCL版本,给定维度中的NDRange大小必须是该维度中工作组大小的倍数。因此,为了保留您的示例,因为在维度0中我们有4个线程,所以维度0中的工作组大小可以是1、2、4,但不能是3。与全局大小类似,线程可以使用
size\u t get\u local\u size(uint D)
查询本地大小

本地唯一标识符: 有时,在工作组中可以唯一标识线程是很重要的。因此函数
size\u t get\u local\u id(uint D)
。注意前一句中的“内”。具有本地id(1,0)的线程将是其工作组(2D)中唯一具有此id的线程。但是具有本地id(1,0)的线程数量将与NDRange中的工作组数量相同

组数: 谈到组,有时线程可能需要知道有多少组。这就是函数
size\u t get\u num\u groups(uint D)
存在的原因。请注意,您必须再次将感兴趣的尺寸作为参数传递

每个组还具有一个id: …您可以使用函数
size\u t get\u group\u id(uint D)
在内核中查询。请注意,组ID的格式将类似于线程的格式:最多包含3个元素的元组

总结: 总而言之,如果您的2Dn范围的全局工作大小为(4,6),本地工作大小为(2,2),则表示:

  • 维度0中的全局大小将为4
  • 维度1中的全局大小将为6
  • 维度0中的本地大小(或工作组大小)将为2
  • 维度1中的本地大小(或工作组大小)将为2
  • 维度0中的线程全局ID的范围为0到3
  • 维度1中的线程全局ID的范围为0到5
  • 维度0中的线程本地ID的范围为0到1
  • 维度1中的线程本地ID的范围为0到1
  • NDRange中的线程总数将为4*6=24
  • 工作组中的线程总数为2*2=4
  • 工作组总数为(4/2)*(6/2)=6
  • 维度0中的组ID的范围为0到1
  • 维度1中的组ID的范围为0到2
  • 只有一个线程将使用全局id(0,0),但将有6个线程使用本地id(0,0),因为有6个组
例子: 下面是一个将所有这些概念结合使用的虚拟示例(请注意,性能会很糟糕,这只是一个愚蠢的示例)

假设您有一个6行4列int的2D数组。您希望将这些元素按2乘2个元素的平方进行分组,并将它们相加,例如,元素(0,0)、(0,1)、(1,0)、(1,1)将在一个组中(希望足够清楚)。因为你将有6个“正方形”,你将有6个求和的结果,所以你需要一个由6个元素组成的数组来存储这些结果

要解决这个问题,您可以使用我们的2D ND
size_t global_work_size[2] = { 4, 4 };
//in is a 24 int array, result is a 6 int array, temp is a 4 int array 
kernel void foo(global int *in, global int *result, local int *temp){
    //use vectors for conciseness
    int2 globalId = (int2)(get_global_id(0), get_global_id(1));
    int2 localId = (int2)(get_local_id(0), get_local_id(1));
    int2 groupId = (int2)(get_group_id (0), get_group_id (1));
    int2 globalSize = (int2)(get_global_size(0), get_global_size(1));
    int2 locallSize = (int2)(get_local_size(0), get_local_size(1));
    int2 numberOfGrp = (int2)(get_num_groups (0), get_num_groups (1));

    //Read from global and store to local
    temp[localId.x + localId.y * localSize.x] = in[globalId.x + globalId.y * globalSize.x];
    //Sync
    barrier(CLK_LOCAL_MEM_FENCE);    
    //Only the threads with local id (0, 0) sum elements up
    if(localId.x == 0 && localId.y == 0){
    int sum = 0;
        for(int i = 0; i < locallSize.x * locallSize.y ; i++){
            sum += temp[i];
        }
    //store result in global
    result[groupId.x + numberOfGrp.x * groupId.y] = sum; 
    }
}