如何在OpenCL中使用缓冲区分配和映射内存机制?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在OpenCL中使用缓冲区分配和映射内存机制?相关的知识,希望对你有一定的参考价值。

关于使用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);
    }
答案

首先,让我解释一下clEnqueueMapBuffer的作用:它将设备缓冲区映射到主机地址空间,并返回指向此映射内存的指针。确切地说,必须将此指针传递给clEnqueueUnmapMemObject才能将更改提交回设备缓冲区。

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

在此行中,您立即取消引用指针并为本地变量赋值。 clEnqueueMapBuffer返回的原始指针丢失了。

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

这条线可能因CL_INVALID_VALUE而失败,因为&res_nb不是原始指针。它只是指向OpenCL运行时未知的局部变量的指针。

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

这条线也有同样的问题,但它也没有相应的clEnqueueUnmapMemObject。即使将缓冲区映射为Read,您仍然必须调用clEnqueueUnmapMemObject以使运行时“释放”此映射内存。

有关详细信息,请参阅OpenCL规范中的clEnqueueMapBufferclEnqueueUnmapMemObject

以上是关于如何在OpenCL中使用缓冲区分配和映射内存机制?的主要内容,如果未能解决你的问题,请参考以下文章

OpenCL 部分缓冲区 DMA 读/写

如何在 C++ 中急切提交分配的内存?

我应该如何释放类型映射中为 argout 结构数组分配的内存?

openCL缓存对象的传输与映射

openCL缓存对象的传输与映射

Redis的内存和实现机制