Pointers 内存对象OpenCL的基址

Pointers 内存对象OpenCL的基址,pointers,opencl,base,memory-address,Pointers,Opencl,Base,Memory Address,我想用OpenCL在GPU上遍历一棵树,所以我在主机上将树组装成一个连续的块,并更改所有指针的地址,以便在设备上保持一致,如下所示: TreeAddressDevice=(size_t)BaseAddressDevice+((size_t)TreeAddressHost-(size_t)BaseAddressHost) 我想要内存缓冲区的基址: 在主机上,我为缓冲区分配内存,如下所示: cl_mem tree_d=clCreateBuffer(…) 问题在于cl_mems是跟踪数据内部表示的对象

我想用OpenCL在GPU上遍历一棵树,所以我在主机上将树组装成一个连续的块,并更改所有指针的地址,以便在设备上保持一致,如下所示:

TreeAddressDevice=(size_t)BaseAddressDevice+((size_t)TreeAddressHost-(size_t)BaseAddressHost)

我想要内存缓冲区的基址: 在主机上,我为缓冲区分配内存,如下所示: cl_mem tree_d=clCreateBuffer(…)

问题在于cl_mems是跟踪数据内部表示的对象。从技术上讲,它们是指向对象的指针,但不是指向数据的指针。从内核中访问cl_mem的唯一方法是通过setKernelArgs将其作为参数传入

在这里,我找到了以下解决方案,但它不起作用:

__kernel void getPtr( __global void *ptr, __global void *out )

    {
    *out = ptr;
    }
可以按如下方式调用

代码:

现在“gpuPtr”应该包含GPU内存空间中“myBuf”开头的地址


解决办法很明显,但我找不到?在创建缓冲区时,如何获取指向设备内存的指针?

这是因为在OpenCL模型中,主机内存和设备内存是不相交的。设备内存中的指针在主机上没有意义

您可以使用ClenqueueEmapBuffer将设备缓冲区映射到主机内存。映射将同步设备到主机,取消映射将同步回主机到设备


更新。正如您在注释中所解释的,您希望向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]访问节点。谢谢@GoreTox。仅仅为了查询一个设备指针而创建一个完整的内核和缓冲区听起来真的很糟糕。(我责备的是OpenCL,不是你。)真的没有cudaHostGetDevicePointer的等价物吗?嘿@AlltheRage,我已经很久没有在OpenCL工作了,所以他们可能认识到这一点的必要性,并实现了一个本机支持这一点的函数。然而,在我写这篇文章的时候,我根本找不到一个等价物。我找到的大多数帮助并不真的认为这样的事情是必要的。
// 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);