【问题标题】:First clEnqueueMapBuffer call takes much time第一次 clEnqueueMapBuffer 调用需要很长时间
【发布时间】:2019-04-15 12:10:49
【问题描述】:

我在 OpenCL 代码采用 YOLO 时遇到了性能问题。

该方法仅从设备中提取数据,第一次运行缓慢,接下来的几次调用运行很快。有调用日志,时间以微秒为单位:

clEnqueueMapBuffer      144469
memcpy  2
clEnqueueUnmapMemObject 31
clEnqueueMapBuffer      466
memcpy  103
clEnqueueUnmapMemObject 14
clEnqueueMapBuffer      468
memcpy  106
clEnqueueUnmapMemObject 17

第一次调用是 1 个字节的副本(其中 memcpy 需要 2 微秒)。

内存由代码分配:

if (!x)
    x = (float*) calloc(n, sizeof(float));

buf.ptr = x;

cl_int clErr;
buf.org = clCreateBuffer(opencl_context, CL_MEM_READ_WRITE | 
CL_MEM_ALLOC_HOST_PTR | CL_MEM_COPY_HOST_PTR, buf.len * buf.obs, buf.ptr, &clErr);

拉取数据的代码如下:

#ifdef BENCHMARK
    clock_t t;
    double time_taken;
    t = clock();
#endif
    cl_int clErr;
    void* map = clEnqueueMapBuffer(opencl_queues[opencl_device_id_t], x_gpu.mem, CL_TRUE, CL_MAP_READ,
                                   0, x_gpu.len * x_gpu.obs, 0, NULL, NULL, &clErr);
#ifdef BENCHMARK
    t = clock() - t;
    time_taken = ((double)t);
    printf("clEnqueueMapBuffer\t%d\n", (int)time_taken);
    t = clock();
#endif
    if (clErr != CL_SUCCESS)
        printf("could not map array to device. error: %s\n", clCheckError(clErr));
    memcpy(x, map, (n - x_gpu.off) * x_gpu.obs);
#ifdef BENCHMARK
    t = clock() - t;
    time_taken = ((double)t);
    printf("memcpy\t%d\n", (int)time_taken);
    t = clock();
#endif
    clErr = clEnqueueUnmapMemObject(opencl_queues[opencl_device_id_t], x_gpu.mem, map, 0, NULL, NULL);
    if (clErr != CL_SUCCESS)
        printf("could not unmap array from device. error: %s\n", clCheckError(clErr));
#ifdef BENCHMARK
    t = clock() - t;
    time_taken = ((double)t);
    printf("clEnqueueUnmapMemObject\t%d\n", (int)time_taken);
#endif

第一次通话时出现这种延迟的原因可能是什么?如何减少延迟?

【问题讨论】:

  • 您确定在您调用clEnqueueMapBuffer() 之前,命令队列上的任何先前操作(例如排队内核)已经完成了吗?如果不确定,请尝试在映射前插入clFinish()。 (一般来说,clFinish() 是不需要的,没有它可能会更快,但是如果没有它,异步排队的命令会导致误导时间测量。)
  • 另一种可能性是简单的实现是将数据从 GPU VRAM 复制到主机内存,这需要时间。在这种情况下,您可能希望尝试在缓冲区创建时删除CL_MEM_ALLOC_HOST_PTR 标志,和/或使用clEnqueueReadBuffer() 让GPU 的DMA 控制器执行复制。 (如果您只是使用 memcpy 将所有数据复制出来,那么使用映射 API 并没有多大意义 - 这样做的目的是允许直接的零复制访问。)
  • 你是对的。有未完成的操作。调用 clFinish() 需要所有时间:clFinish 148326 clEnqueueMapBuffer 155
  • 酷,我已经发布了一个更详细地解释这一点的答案。希望一切都有意义!

标签: c performance memory-management gpu opencl


【解决方案1】:

您的clEnqueueMapBuffer() 调用被阻塞(CL_TRUE 用于blocking_map 参数),这意味着只有在映射操作完成后调用才会返回。如果您的命令队列不是并发的,则任何先前排队的异步命令(例如排队的内核)都需要在映射甚至开始之前完成。如果有这样的早期命令,您实际上是在测量它们的完成以及内存映射操作。为避免这种情况,请在开始计时之前添加clFinish() 电话。 (调用clFinish() 可能会更有效不,所以我建议你只保留它用于测量目的。)

【讨论】:

    猜你喜欢
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 2011-10-29
    • 2016-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多