【发布时间】:2014-11-26 20:37:18
【问题描述】:
为 OpenCL 输出数据分配内存的最佳方式(在任何意义上)是什么?有什么解决方案可以同时适用于独立显卡和集成显卡?
作为一个超级简化的示例,请考虑以下 C++(主机)代码:
std::vector<float> generate_stuff(size_t num_elements) {
std::vector<float> result(num_elements);
for(int i = 0; i < num_elements; ++i)
result[i] = i;
return result;
}
这可以使用 OpenCL 内核来实现:
__kernel void gen_stuff(float *result) {
result[get_global_id(0)] = get_global_id(0);
}
最直接的解决方案是在设备和主机上都分配一个数组,然后在内核完成后复制:
std::vector<float> generate_stuff(size_t num_elements) {
//global context/kernel/queue objects set up appropriately
cl_mem result_dev = clCreateBuffer(context, CL_MEM_WRITE_ONLY, num_elements*sizeof(float) );
clSetKernelArg(kernel, 0, sizeof(cl_mem), result_dev);
clEnqueueNDRangeKernel(queue, kernel, 1, nullptr, &num_elements, nullptr, 0, nullptr, nullptr);
std::vector<float> result(num_elements);
clEnqueueReadBuffer( queue, result_dev, CL_TRUE, 0, num_elements*sizeof(float), result_host.data(), 0, nullptr, nullptr );
return result;
}
这适用于离散卡。但是对于共享内存图形,这意味着分配双倍和一个额外的副本。如何避免这种情况?可以肯定的一件事,应该放弃clEnqueuReadBuffer 并改用clEnqueueMapBuffer/clUnmapMemObject。
一些替代方案:
- 处理额外的内存副本。如果内存带宽不是问题,则可以接受。
- 在主机上分配一个普通的内存阵列,创建缓冲区时使用
CL_MEM_USE_HOST_PTR。应该使用特定于设备的对齐方式进行分配 - 使用英特尔高清显卡是 4k:https://software.intel.com/en-us/node/531272 我不知道这是否可以从 OpenCL 环境中查询。内核完成刷新缓存后,应映射结果(使用CL_MAP_READ)。但是什么时候可以取消映射?映射完成后立即(似乎不适用于AMD独立显卡)?数组的重新分配还需要修改 Windows 上的客户端代码(因为 _aligned_free 与 free 不同)。 - 使用
CL_MEM_ALLOCATE_HOST_PTR分配并在内核完成后映射。 cl_mem 对象必须保持活动状态,直到使用缓冲区(甚至可能映射?),因此它需要污染客户端代码。这也将数组保留在固定内存中,这可能是不受欢迎的。 - 在没有
CL_MEM_*_HOST_PTR的设备上分配,并在内核完成后映射它。从释放的角度来看,这与选项 2 相同,它只是避免固定内存。 (实际上,不确定映射的内存是否未固定。) - ???
你是如何处理这个问题的?是否有任何供应商特定的解决方案?
【问题讨论】:
-
一个恰当的问题。一般来说,您的意图似乎与术语“零拷贝”有关(请参阅 stackoverflow.com/q/12766578/3182664 或 stackoverflow.com/a/19974674/3182664 和 developer.amd.com/tools-and-sdks/opencl-zone/… ),但异质性使其难以掌握。我也期待在这里看到一个好的答案。
标签: opencl