为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相同,它只是避免了固定内存。 (实际上,不确定映射的内存是否固定。)你是如何处理这个问题的?是否有任何供应商特定的解决方案?
答案 0 :(得分:2)
对于分立和集成硬件,您可以使用单个缓冲区执行此操作:
答案 1 :(得分:1)
取决于用例:
CL_MEM_ALLOCATE_HOST_PTR
(取决于平台)。阻止地图进行阅读,使用它,取消地图。此选项要求数据处理程序(使用者)了解CL存在,并使用CL调用取消映射。clEnqueueReadBuffer()
。在某些供应商的情况下,这不提供固定内存并且副本很慢,因此最好还原到选项“1”。但对于其他没有固定内存的情况,我发现它更快。