【问题标题】:How to use buffer allocation and mapping memory mechanism iin OpenCL?OpenCL中如何使用缓冲区分配和映射内存机制?
【发布时间】:2018-08-11 15:47:47
【问题描述】:

对于我使用 OpenCL 映射缓冲区的代码是否正确,我有点困惑。

我了解缓冲区/映射特定操作是在 OpenCL 环境中将映射(零复制)内存机制与 GPU 一起使用的最有效方式。

我不明白为什么 res_nb 在每次迭代时都没有初始化为 0。 在每次迭代中,res_nb 乘以 2

我知道我应该做错误检查等等。

OpenCL 代码

__kernel void test(
    __global uint* res_nb_g,
)
{
    // atomicAdd will return the value which was stored at "res_nb_g" before "1" was added.
    int i = atomic_add(res_nb_g, 1);
}

C 代码

cl_uint res_nb = 0;

cl_mem res_nb_g = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_ALLOC_HOST_PTR | CL_MEM_COPY_HOST_PTR, sizeof(cl_uint), &res_nb, &clStatus);

clSetKernelArg(test_kernel, 0, sizeof(res_nb_g), &res_nb_g);

for (int run = 0; run < 10; run++) {
    res_nb = *((cl_uint *)clEnqueueMapBuffer(clqueue, res_nb_g, CL_TRUE, CL_MAP_WRITE, 0, sizeof(cl_uint), 0, NULL, NULL, NULL));
    res_nb = 0;
    clEnqueueUnmapMemObject(clqueue, res_nb_g, &res_nb, 0, NULL, NULL);

    clEnqueueNDRangeKernel(clqueue, test_kernel, 1, NULL, &g_work_size, &l_work_size, 0, NULL, NULL);

    clFinish(clqueue);

    res_nb = *((cl_uint *)clEnqueueMapBuffer(clqueue, res_nb_g, CL_TRUE, CL_MAP_READ, 0, sizeof(cl_uint), 0, NULL, NULL, &clStatus));
}

使用我的解决方案进行编辑:

    cl_uint *res_nb = 0;
    cl_mem res_nb_g = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, sizeof(cl_uint), NULL, &clStatus);
    clCheckError(clStatus);

    clStatus = clSetKernelArg(test_kernel, 0, sizeof(res_nb_g), &res_nb_g);
    clCheckError(clStatus);

    for (cl_uint run = 0; run < nbruns; run++) {
        res_nb = (cl_uint *)clEnqueueMapBuffer(clqueue, res_nb_g, CL_TRUE, CL_MAP_WRITE, 0, sizeof(cl_uint), 0, NULL, NULL, &clStatus);
        clCheckError(clStatus);
        *res_nb = 0;
        clStatus = clEnqueueUnmapMemObject(clqueue, res_nb_g, res_nb, 0, NULL, NULL);
        clCheckError(clStatus);

        clStatus = clEnqueueNDRangeKernel(clqueue, test_kernel, 1, NULL, &g_work_size, &l_work_size, 0, NULL, NULL);
        clCheckError(clStatus);

        clFinish(clqueue); // Not necessary

        res_nb = (cl_uint *)clEnqueueMapBuffer(clqueue, res_nb_g, CL_TRUE, CL_MAP_READ, 0, sizeof(cl_uint), 0, NULL, NULL, &clStatus);
        clCheckError(clStatus);
        // Edit: remark @ Andrew Savonichev
        clStatus = clEnqueueUnmapMemObject(clqueue, res_nb_g, res_nb, 0, NULL, NULL);
        clCheckError(clStatus);
    }

【问题讨论】:

    标签: c++ c performance memory-management opencl


    【解决方案1】:

    首先,让我解释一下clEnqueueMapBuffer 的作用:它映射一个设备 缓冲到主机地址空间并返回一个指向这个映射的指针 记忆。正是这个指针必须传递给clEnqueueUnmapMemObject in 以便将更改提交回设备缓冲区。

    res_nb = *((cl_uint *)clEnqueueMapBuffer(clqueue, res_nb_g, [...]);

    在这一行中,您立即取消引用指针并将值分配给本地 多变的。 clEnqueueMapBuffer 返回的原始指针丢失。

    clEnqueueUnmapMemObject(clqueue, res_nb_g, &amp;res_nb, 0, NULL, NULL);

    此行可能会因CL_INVALID_VALUE 而失败,因为&amp;res_nb 不是 一个原始指针。它只是一个指向未知的局部变量的指针 OpenCL 运行时。

    res_nb = *((cl_uint *)clEnqueueMapBuffer(clqueue, res_nb_g, CL_TRUE, [...]);

    这一行也有同样的问题,但也没有对应的问题 clEnqueueUnmapMemObject。即使缓冲区映射为读取,您仍然 必须调用clEnqueueUnmapMemObject 让运行时“释放”这个映射 记忆。

    请参阅 OpenCL 中的 clEnqueueMapBufferclEnqueueUnmapMemObject 详细说明。

    【讨论】:

    • 非常感谢您的帮助。我更新了我的解决方案。这是正确的方向/更有效的方式吗?
    • 您仍然需要取消映射最后一个 clEnqueueMapBuffer,但是是的,这应该可以正常工作。另外,如果使用顺序命令队列,在clEnqueueNDRangeKernel和clEnqueueMapBuffer之间不需要clFinish,因为后者是阻塞的。
    • 好的。比clEnqueueWriteBuffer/clEnqueueReadBuffer 更有效吗?
    • 一般来说我会说是的,但你总是可以做一个基准来检查哪种方法更适合你的用例。
    猜你喜欢
    • 2014-12-04
    • 2022-07-15
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2016-03-09
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    相关资源
    最近更新 更多