内存对象的基地址OpenCL
我想在GPU上遍历树,OpenCL,所以我在主机的一个连续块中组装树,我改变了所有指针的地址,以便在设备中保持一致,如下所示:内存对象的基地址OpenCL
TreeAddressDevice =(size_t)BaseAddressDevice +((size_t)TreeAddressHost - (size_t)BaseAddressHost);
我想要的存储器缓冲器的基地址: 在主机I为缓冲区分配内存,如下所示: cl_mem tree_d = clCreateBuffer(...);
问题是,cl_mems是跟踪数据内部表示的对象。从技术上讲,它们是指向对象的指针,但它们不是指向数据的指针。从内核中访问cl_mem的唯一方法是通过setKernelArgs将其作为参数传递。
这里http://www.proxya.net/browse.php?u=%3A%2F%2Fwww.khronos.org%2Fmessage_boards%2Fviewtopic.php%3Ff%3D37%26amp%3Bt%3D2900&b=28我发现下面的解决方案,但它亘古不变的工作:
__kernel void getPtr(__global void *ptr, __global void *out)
{
*out = ptr;
}
,可以按如下方式调用
代码:
...
cl_mem auxBuf = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(void*), NULL, NULL);
void *gpuPtr;
clSetKernelArg(getterKernel, 0, sizeof(cl_mem), &myBuf);
clSetKernelArg(getterKernel, 1, sizeof(cl_mem), &auxBuf);
clEnqueueTask(commandQueue, getterKernel, 0, NULL, NULL);
clEnqueueReadBuffer(commandQueue, auxBuf, CL_TRUE, 0, sizeof(void*), &gpuPtr, 0, NULL, NULL);
clReleaseMemObject(auxBuf);
...
现在 “gpuPtr” 应包含GPU内存空间中“myBuf”开头的地址。
解决方案很明显,我无法找到它?创建缓冲区时,如何取回指向设备内存的指针?
这是因为在OpenCL模型中,主机内存和设备内存不相交。设备内存中的指针在主机上没有任何意义。
您可以使用clEnqueueMapBuffer将设备缓冲区映射到主机内存。该映射会将设备同步到主机,并且取消映射将同步主机到设备。
更新。正如您在评论中所解释的那样,您想要将树结构发送到GPU。一种解决方案是将所有树节点存储在一个数组中,用数组中的索引替换指向节点的指针。
正如Eric所指出的,有两组内存要考虑:主机内存和设备内存。基本上,OpenCL试图通过引入缓冲对象让我们在主机端的程序中进行交互来隐藏这种交互的细节。现在,正如您所指出的那样,这种方法的问题在于,当我们想要做一些比OpenCL开发人员打算或在其范围内允许的更棘手的事情时,它隐藏了我们设备的细节。这里的解决方案是记住OpenCL内核使用C99,并且该语言允许我们访问指针而没有任何问题。考虑到这一点,我们可以只是要求将指针存储在一个无符号的整数变量中,以便以后参考。
你的实现是正确的,但它需要更多的C语法来完成传输。
的OpenCL内核:
// Kernel used to obtain pointer from target buffer
__kernel void mem_ptr(__global char * buffer, __global ulong * ptr)
{
ptr[0] = &buffer[0];
}
// Kernel to demonstrate how to use that pointer again after we extract it.
__kernel void use_ptr(__global ulong * ptr)
{
char * print_me = (char *)ptr[0];
/* Code that uses all of our hard work */
/* ... */
}
主机方案:
// Create the buffer that we want the device pointer from (target_buffer)
// and a place to store it (ptr_buffer).
cl_mem target_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE,
MEM_SIZE * sizeof(char), NULL, &ret);
cl_mem ptr_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE,
1 * sizeof(cl_ulong), NULL, &ret);
/* Setup the rest of our OpenCL program */
/* .... */
// Setup our kernel arguments from the host...
ret = clSetKernelArg(kernel_mem_ptr, 0, sizeof(cl_mem), (void *)&target_buffer);
ret = clSetKernelArg(kernel_mem_ptr, 1, sizeof(cl_mem), (void *)&ptr_buffer);
ret = clEnqueueTask(command_queue, kernel_mem_ptr, 0, NULL, NULL);
// Now it's just a matter of storing the pointer where we want to use it for later.
ret = clEnqueueCopyBuffer(command_queue, ptr_buffer, dst_buffer, 0, 1 * sizeof(cl_ulong),
sizeof(cl_ulong), 0, NULL, NULL);
ret = clEnqueueReadBuffer(command_queue, ptr_buffer, CL_TRUE, 0,
1 * sizeof(cl_ulong), buffer_ptrs, 0, NULL, NULL);
有你有它。现在,请记住,您不必使用我使用的char变量;它适用于任何类型。不过,我建议使用cl_ulong来存储指针。对于具有小于4GB可访问内存的设备而言,这并不重要。但对于地址空间较大的设备,您必须使用cl_ulong。如果您绝对需要在设备上节省空间,但拥有内存大于4GB的设备,则可以创建一个结构,将地址的低32位LSB存储为uint类型,并将MSB存储在小型。
谢谢你的回答...:D 使用标志CL_MEM_ALLOC_HOST_PTR,应用程序希望OpenCL实现从主机可访问内存分配内存,就像在CUDA上固定内存一样。但是,我不想要这个。 我需要将整个树复制到设备,但我应该更改主机上的地址: (size_t)设备上的内存对象的基地址+((size_t)Ptr - (size_t)HostPtr)。 由于这个原因,我需要设备上的内存对象的基地址。有什么办法吗? –
@zoevas不,你不是这样做的正确方法。你的树不应该关心“基地址”,应该使用偏移量(或指数,正如Eric所建议的那样)。在主机上的每个节点上,计算(nodeAddress - baseAddress)/ nodeSize,这是您的偏移量。在设备上,使用nodeList [nodeOffset]访问节点。 – Thomas