如何为OpenCL结果数据分配内存?

时间:2014-11-26 20:37:19

标签: opencl

为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

一些替代方案:

  1. 处理额外的内存副本。如果内存带宽不是问题,则可以接受。
  2. 在主机上分配正常的内存阵列,在创建缓冲区时使用CL_MEM_USE_HOST_PTR。应该使用特定于设备的对齐进行分配 - 英特尔高清显卡为4k:https://software.intel.com/en-us/node/531272我不知道是否可以从OpenCL环境进行查询。在内核完成刷新缓存之后,应该映射结果(使用CL_MAP_READ)。但什么时候可以取消映射?映射完成后立即(似乎与AMD独立显卡不兼容)?释放数组还需要修改Windows上的客户端代码(由于_aligned_free与free不同)。
  3. 使用CL_MEM_ALLOCATE_HOST_PTR分配并在内核完成后映射。 cl_mem对象必须保持活动状态,直到使用缓冲区(甚至可能映射?),因此需要污染客户端代码。这也将数组保存在固定内存中,这可能是不可取的。
  4. 在没有CL_MEM_*_HOST_PTR的设备上分配,并在内核完成后映射它。从解除分配的角度来看,这与选项2相同,它只是避免了固定内存。 (实际上,不确定映射的内存是否固定。)
  5. ???
  6. 你是如何处理这个问题的?是否有任何供应商特定的解决方案?

2 个答案:

答案 0 :(得分:2)

对于分立和集成硬件,您可以使用单个缓冲区执行此操作:

  1. 使用CL_MEM_WRITE_ONLY分配(因为您的内核只写入缓冲区)。如果它有助于在某些平台上的性能(阅读供应商指南并进行基准测试),也可以选择使用CL_MEM_ALLOCATE_HOST_PTR或特定于供应商的(例如AMD)标志。
  2. 将写入缓冲区的内核排入队列。
  3. clEnqueueMapBuffer与CL_MAP_READ和阻止。在分立硬件上,这将通过PCIe进行复制;在集成硬件上,它是“免费的”。
  4. 使用返回的指针在CPU上使用结果。
  5. clEnqueueUnmapMemObject。

答案 1 :(得分:1)

取决于用例:

  1. 最小化内存占用和IO效率 :( Dithermaster的回答)
    • 使用CL_MEM_WRITE_ONLY标志创建,或者CL_MEM_ALLOCATE_HOST_PTR(取决于平台)。阻止地图进行阅读,使用它,取消地图。此选项要求数据处理程序(使用者)了解CL存在,并使用CL调用取消映射。
  2. 对于必须向第三方提供缓冲区数据的情况(即:需要C指针或类缓冲区的库,与CL无关):
    • 在这种情况下,使用映射内存可能不太好。与普通CPU内存相比,映射内存访问时间通常更长。所以,而不是映射,然后memcpy()和unmap;更容易直接对要复制输出的CPU地址执行clEnqueueReadBuffer()。在某些供应商的情况下,这不提供固定内存并且副本很慢,因此最好还原到选项“1”。但对于其他没有固定内存的情况,我发现它更快。
  3. 读取内核输出的其他任何不同条件?我想不是......