将结构用作缓冲支架

时间:2018-09-11 13:24:12

标签: c++ c opencl

在当前的OpenCL实现中,我想节省参数的时间,避免每次想要在内核中使用缓冲区并为我的内核使用较短的参数列表时都传递参数。

因此,我创建了一个结构(工作区),该结构将指向缓冲区的指针保存在设备内存中,该结构的行为就像一个具有成员变量的对象,您想要随时访问它,并且希望在整个执行过程中保持生命。我从未在AMD GPU甚至CPU上遇到过问题。但是Nvidia引起了很多问题。始终似乎是对齐问题,从未到达正确的缓冲区等。

这里有一些代码可以帮助您,请参见以下问题:

在主机上定义的结构:

 #define SRC_IMG 0       // (float4 buffer) Source image
 #define LAB_IMG 1       // (float4 buffer) LAB image

 // NOTE: The size of this array should be as much as the last define + 1.
 #define __WRKSPC_SIZE__ 2 

 // Structure defined on host.
 struct Workspace
 {
      cl_ulong getPtr[__WRKSPC_SIZE__];
 };

 struct HostWorkspace
 {
      cl::Buffer srcImg;
      cl::Buffer labImg;
 };

设备上定义的结构:

typedef struct __attribute__(( packed )) gpuWorkspace
{
    ulong getPtr[__WRKSPC_SIZE__]; 
} gpuWorkspace_t; 

请注意,在设备上,我使用ulong,在主机上,我使用cl_ulong,如此处OpenCL: using struct as kernel argument所示。

因此,一旦创建了用于源图像或LAB图像的cl :: Buffer,我将它们保存到HostWorkspace对象中,因此在释放该对象之前,将保留对cl :: Buffer的引用,因此整个项目中都存在缓冲区在主机上,在设备上事实上。

现在,我需要给那些设备喂食,所以我有一个简单的内核,它可以如下初始化我的设备工作区:

__kernel void Workspace_Init(__global gpuWorkspace_t* wrkspc,
                             __global float4* src,
                             __global float4* LAB)
{
    // Get the ulong pointer on the first element of each buffer.
    wrkspc->getPtr[SRC_IMG] = &src[0];
    wrkspc->getPtr[LAB_IMG] = &LAB[0];
}

其中wrkspc是用struct Workspace分配的缓冲区,而src + LAB只是将缓冲区分配为一维数组图像。

然后,在我的任何内核中,如果要使用src或LAB,请按以下步骤操作:

__kernel void ComputeLABFromSrc(__global gpuWorkspace_t* wrkSpc)
{
    // =============================================================
    // Get pointer from work space.
    // =============================================================

    // Cast back the pointer of first element as a normal buffer you
    // want to use along the execution of the kernel.
    __global float4* srcData = ( __global float4* )( wrkSpc->getPtr[SRC_IMG] );
    __global float4* labData = ( __global float4* )( wrkSpc->getPtr[LAB_IMG] );

    // Code kernel as usual.
}

当我开始使用它时,我喜欢4-5张效果不错的图像,但结构却不同:

struct Workspace
{
    cl_ulong imgPtr;
    cl_ulong labPtr;
};

每个图像都有自己的指针。

在某个时候,我获得了更多的图像,并且遇到了一些问题。因此,我在线搜索,发现了一些建议,即设备/主机之间的结构的sizeof()可能会有所不同,因此我将其同时更改为单个数组,直到16个元素都可以正常工作。

因此,我进行了更多搜索,找到了关于属性((包装))的建议,该建议已放入设备结构中(请参见上文)。但是现在,我到达了26个元素,当我在设备或主机上检查结构的大小时,大小为208(元素* sizeof(cl_ulong)== 26 * 8)。但是我仍然有一个与先前模型类似的问题,我的指针在先前图像的中间其他地方被读取了,等等。

所以我想知道,是否有人尝试过类似的模型(也许使用不同的方法),或者有任何技巧来使用此模型建立“可靠的”模型。

请注意,所有内核均编码正确,在AMD或CPU上使用相同代码执行时,我得到了很好的结果。唯一的问题是在英伟达上。

1 个答案:

答案 0 :(得分:4)

不要尝试跨内核边界存储GPU端指针值。他们不能保证保持不变。始终使用索引。而且,如果内核使用特定的缓冲区,则需要将其作为参数传递给该内核。

参考文献:

  1. OpenCL 1.2规范(据我所知,nvidia并未实现更新的标准)未定义指针到整数强制转换的行为,反之亦然。
  2. 第6.9p节指出:“声明为结构或联合的内核函数的参数不允许将OpenCL对象作为结构或联合的元素进行传递。” 您正在尝试做:将缓冲区的结构传递给内核。
  3. 第6.9a节指出:“程序中内核函数的参数不能声明为指向一个或多个指针的指针。” -本质上,这是您试图通过强制转换来颠覆的对象您的指针指向整数并返回。 (第1点)您不能通过绕过类型系统来“欺骗” OpenCL使其定义良好。

正如我在下面的注释线程中建议的那样,您将需要使用索引将位置保存在缓冲区对象内。如果要跨不同的内存区域存储位置,则需要将多个缓冲区统一为一个并将一个索引保存到该巨型缓冲区中,或者需要保存一个标识您要引用的缓冲区的数值。