【问题标题】:How to allocate memory for OpenCL result data?如何为 OpenCL 结果数据分配内存?
【发布时间】: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

一些替代方案:

  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. ???

你是如何处理这个问题的?是否有任何供应商特定的解决方案?

【问题讨论】:

标签: opencl


【解决方案1】:

您可以使用单个缓冲区来完成此操作,无论是离散硬件还是集成硬件:

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

【讨论】:

    【解决方案2】:

    取决于用例:

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

    【讨论】:

      猜你喜欢
      • 2011-12-14
      • 2020-02-03
      • 2023-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-06
      • 2014-04-21
      相关资源
      最近更新 更多