从OpenCL中的GPU内核创建GPU上主机ptr指向的缓冲区副本

时间:2012-07-08 05:34:56

标签: opencl gpu gpu-programming

我试图了解CL_MEM_USE_HOST_PTR和CL_MEM_COPY_HOST_PTR的工作原理。 基本上在使用CL_MEM_USE_HOST_PTR时,比如在创建2D图像时,这将不会复制到设备,而是GPU将引用主机上的映射内存(clEnqueueMapBuffer映射它),进行处理,我们可以将结果写入其他位置

另一方面,如果我使用CL_MEM_COPY_HOST_PTR,它将创建设备上主机ptr指向的数据的副本(我猜它将创建一个单独的副本而不仅仅是缓存)。现在,将对复制到设备的数据进行处理,然后将结果再次复制到主机。我希望我能正确理解它。

所以我的查询是...... 只是出于好奇,我想这样做。我将使用CL_MEM_USE_HOST_PTR,现在即使设备可以访问主机内存,我希望GPU内核在设备上创建一个单独的副本(不使用COPY_HOST_PTR,因为这又在主机本身完成)然后执行处理这些数据。怎么办呢?

3 个答案:

答案 0 :(得分:1)

使用CL_MEM_READ_WRITE创建要复制到的缓冲区,但不要在主机上初始化它。 我最近不得不为连续的整数初始化缓冲区

cl_mem _offsetBuffer;
_offsetBuffer = clCreateBuffer(_context, CL_MEM_READ_WRITE, (size_t)(count * sizeof(cl_int)), NULL, &errorCode);
上面的clCreateBuffer除了给你一个内存对象的句柄之外,对你的主机的内存没有任何作用。然后我使用内核来分配顺序值,因为显卡上的内存速度比在cpu上分配值要快得多。

__kernel void initOffsetBuffer(__global int* offsetBuffer, const int offsetBufferLength, const int startValue){
    int gid = get_global_id(0);
    int gs = get_global_size(0);
    int i;
    for(i=gid;i<offsetBufferLength;i+=gs){
        offsetBuffer[i] = i+startValue;
    }
}

此时主机内存中仍然没有缓冲区的副本。我需要使用clEnqueueReadBuffer将其复制到主机。

您可以轻松地将此代码修改为复制内核,而不仅仅是直接分配。

__kernel void copyBuffer(__global const int* srcBuffer, __global int* dstBuffer, const int bufferLength){
    int gid = get_global_id(0);
    int gs = get_global_size(0);
    int i;
    for(i=gid;i<bufferLength;i+=gs){
        dstBuffer[i] = srcBuffer[i];
    }
}

答案 1 :(得分:1)

OpenCL缓冲区通常在主机内存中有一个“位”副本(这是在OpenCL规范中调用缓冲区内容的方式)。这是必要的,因为设备内存是有限的,并且这些位通常仅在内核使用时传输到设备。

使用USE_HOST_PTR创建缓冲区时,允许OpenCL运行时将host_ptr位置用于此主机内存副本。当内核使用缓冲区时,这些位将被复制到设备中。执行后,您需要确保将这些位同步回主机内存。这是通过调用clEnqueueMapBuffer完成的,此函数返回的指针将位于主机内存区域内。

使用COPY_HOST_PTR创建缓冲区时,运行时会分配缓冲区的新主机内存副本,并将位复制到其中。通常,此时不会向设备发送任何内容。

答案 2 :(得分:0)

CL_MEM_HOST_PTR - 实际上,cl_mem对象将在设备上分配内存并复制主机指针指定的数据。对主机端不可见对设备端缓冲区对象的任何修改。

CL_MEM_USE_HOST_PTR - cl_mem对象使用host_ptr提供的内存,因此设备可以直接修改主机数据上分配的内容,这样我们就不会涉及任何数据传输。