跳转至

工作组

在 OpenCL 中,工作区大小、工作组大小和工作项的大小是通过调用 OpenCL API 函数(如 clEnqueueNDRangeKernel)来设置的。这些参数决定了并行计算的分布和调度方式,因此正确设置它们对性能优化至关重要。


三种概念:工作区大小、工作组大小、工作项大小

  1. 工作区大小(Global Work Size, 全局工作区)
  2. 全局工作区表示所有工作项的总数,是整个计算任务的规模。
  3. 它通常是一个多维数组,指定了每个维度上的工作项总数。
  4. 例如,如果全局工作区大小是 (16, 16),表示有 256 个工作项。

  5. 工作组大小(Local Work Size, 局部工作区)

  6. 每个工作组中包含的工作项数量。
  7. 工作组是 OpenCL 中的基本调度单元,所有工作项在一个工作组内共享局部内存。
  8. 工作组大小通常是全局工作区大小的一个因数(即可以整除全局工作区)。

  9. 工作项(Work Item)

  10. 工作项是 OpenCL 中的最小计算单元,代表一段内核代码的运行实例。
  11. 每个工作项由全局 ID 标识(通过 get_global_id() 获取),并且在其所在的工作组内也有局部 ID(通过 get_local_id() 获取)。

设置工作区大小和工作组大小

1. 使用 API clEnqueueNDRangeKernel 设置

在 OpenCL 中,通过 clEnqueueNDRangeKernel 来设置全局工作区大小和局部工作区大小。函数的原型如下:

C
cl_int clEnqueueNDRangeKernel(
    cl_command_queue command_queue,  // 命令队列
    cl_kernel kernel,                // 内核对象
    cl_uint work_dim,                // 工作区的维度(1D, 2D, 3D)
    const size_t *global_work_offset,// 全局偏移量(通常为 NULL)
    const size_t *global_work_size,  // 全局工作区大小
    const size_t *local_work_size,   // 局部工作区大小(可为 NULL,表示由 OpenCL 自动决定)
    cl_uint num_events_in_wait_list, // 等待的事件数
    const cl_event *event_wait_list, // 等待事件列表
    cl_event *event                  // 输出事件
);

2. 参数说明

  • work_dim:指定工作区的维度(1D、2D 或 3D),例如 1 表示 1D,2 表示 2D。
  • global_work_offset:全局偏移量,通常为 NULL,表示从全局坐标 (0, 0, 0) 开始计算。
  • global_work_size:全局工作区大小,指定每个维度的工作项总数。
  • local_work_size:局部工作区大小,指定每个维度的工作组大小。如果为 NULL,OpenCL 运行时会选择合适的大小。

设置示例

1. 一维工作区

C
size_t global_work_size = 1024; // 全局工作区大小为 1024 个工作项
size_t local_work_size = 64;   // 每个工作组包含 64 个工作项

clEnqueueNDRangeKernel(
    command_queue, kernel, 1, NULL,
    &global_work_size, &local_work_size,
    0, NULL, NULL
);
  • 全局工作区有 1024 个工作项。
  • 每个工作组有 64 个工作项,因此会生成 1024 / 64 = 16 个工作组。

2. 二维工作区

C
size_t global_work_size[2] = {16, 16}; // 全局工作区大小为 16x16
size_t local_work_size[2] = {4, 4};   // 每个工作组大小为 4x4

clEnqueueNDRangeKernel(
    command_queue, kernel, 2, NULL,
    global_work_size, local_work_size,
    0, NULL, NULL
);
  • 全局工作区有 16x16 = 256 个工作项。
  • 每个工作组有 4x4 = 16 个工作项,总共会生成 256 / 16 = 16 个工作组。

3. 自动分配工作组大小

如果不指定工作组大小(将 local_work_size 设置为 NULL),OpenCL 会自动选择一个合适的工作组大小:

C
size_t global_work_size[2] = {16, 16}; // 全局工作区大小为 16x16

clEnqueueNDRangeKernel(
    command_queue, kernel, 2, NULL,
    global_work_size, NULL,
    0, NULL, NULL
);
  • OpenCL 会自动决定工作组大小,但这可能不是最优的选择。

注意事项

  1. 全局工作区大小必须是工作组大小的倍数
  2. 如果 global_work_size 不是 local_work_size 的整数倍,OpenCL 会报错。
  3. 解决方法是调整全局工作区大小,或在内核代码中处理越界问题。

  4. 工作组大小的限制

  5. 每个设备对工作组大小有硬件限制,可以通过 clGetDeviceInfo 获取:
    C
    clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, ...);
    
  6. 如果工作组大小超过设备限制,程序会报错。

  7. 选择合适的工作组大小

  8. 工作组大小应尽量与硬件的计算单元(如 GPU 的线程块)对齐,以优化性能。
  9. 通常选择 32、64、128 或 256 这样的值,具体取决于设备架构。

  10. 使用对齐的全局工作区大小

  11. 全局工作区大小通常设置为工作组大小的整数倍,以避免处理边界条件。

总结

  • 全局工作区大小:决定总共需要多少个工作项来完成计算。
  • 局部工作区大小:决定每个工作组中包含多少个工作项。
  • 设置方法:通过 clEnqueueNDRangeKernel 指定 global_work_sizelocal_work_size
  • 优化建议:根据硬件架构选择合适的工作组大小(通常是 32、64、128 等倍数)。