【问题标题】:How to effectively swap OpenCL memory buffers?如何有效地交换 OpenCL 内存缓冲区?
【发布时间】:2012-06-14 20:45:53
【问题描述】:

正如标题所暗示的那样,我正在寻找如何有效地交换两个 OpenCL 缓冲区。我的内核使用两个全局缓冲区,一个作为输入,一个作为输出。但是,我在 for 循环中使用相同的 NDRange 调用我的内核,每次设置内核参数、将内核排入队列并交换缓冲区,因为之前的输出缓冲区将是下一次迭代的输入缓冲区种子。

交换这两个缓冲区的适当方法是什么?我想将缓冲区复制回主机到一个已经 malloc 的数组并使用clEnqueueWriteBuffer()clEnqueueReadBuffer() 将其复制到下一个输入缓冲区是一种低效的方法。否则我只是使用一个临时的cl_mem 变量来进行交换。

【问题讨论】:

    标签: buffer opencl swap


    【解决方案1】:

    您不需要,只需使用clSetKernelArg 设置正确的内核参数,然后再将您的内核排入队列(使用clEnqueueNDRangeKernel)。缓冲区将保留在设备上,不会将任何内容复制回主机。

    当然,在这种情况下,您的缓冲区必须使用CL_MEM_READ_WRITE 创建。

    【讨论】:

    • 嗯,我想过。但似乎我每次迭代都必须交替输入和输出内核参数不是吗?
    • 是的 - 你会的。在每次迭代时交换并设置它们。这是乒乓球(mathematik.uni-dortmund.de/~goeddeke/gpgpu/…
    • 好吧,我当然想过。但是,如果我没有在我的问题中说清楚,对不起。 “第二次”意味着两次。不是几百万。否则大规模并行有什么意义。谢谢你们。
    • 您可以在调用setArg 之前使用std::swap(bufferA, bufferB);,以避免对偶数和奇数迭代使用单独的代码。
    【解决方案2】:

    如上一个答案:不,您根本不需要交换缓冲区。

    但是,我不同意建议的答案。函数clSetKernelArg() 不是线程安全的,并且不设计为在操作循环中调用。

    正确的解决方案是创建 2 个使用相同程序和源创建的内核。这种方法更符合 OpenCL 编程理念“一个内核完成一项任务”。拥有许多具有相同代码但参数不同的内核是要走的路。

    第一个内核将有:

    kernel1 = clCreateKernel(program, "mykernel", NULL);
    clSetKernelArg(kernel1, 0, &buff1);
    clSetKernelArg(kernel1, 1, &buff2);
    

    另一个是:

    kernel2 = clCreateKernel(program, "mykernel", NULL);
    clSetKernelArg(kernel2, 0, &buff2);
    clSetKernelArg(kernel2, 1, &buff1);
    

    这样,您无需在每次迭代时停止执行。你可以简单地运行:

    for(int it=0; it<iter; it++){
        clEnqueueNDRangeKernel(it%2 ? kernel1 : kernel2, ....);
    }
    clFinish(command);
    

    这种方法肯定会比更改内核参数更好,更高效,API 调用更少。此外,在某些系统上,clSetKernelArgs() 可能是阻塞调用,因为 API 实现不佳。所以最好尽量避开它们。

    【讨论】:

    • 虽然我同意 clSetKernelArg 不是线程安全的,但您关于“不允许您在未完成前一次迭代的情况下运行下一次迭代”的说法是不正确的,一旦您调用 clEnqueueNDRangeKernel,所有参数都会被复制,这样您就可以在内核入队后立即设置任何您想要的内容。
    • 我更新了我的答案。你是对的,99% 的可用平台都没有阻塞,只有一些旧的驱动程序,或者执行不好的驱动程序在 clSetKernelArgs() 上阻塞
    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-03
    • 2023-03-21
    相关资源
    最近更新 更多