C++ 使用结构作为缓冲支架
在我当前的OpenCL实现中,我希望通过参数节省时间,避免每次我想在内核中使用缓冲区时都传递参数,并为内核提供一个较短的参数列表 因此,我创建了一个结构(工作区),其中保存了指向设备内存中缓冲区的指针,该结构的行为就像一个对象,其中包含您希望随时间访问的成员变量,并且希望在整个执行过程中保持活动状态。我从来没有在AMD GPU甚至CPU上遇到过问题。但是Nvidia在这方面造成了很多问题。这似乎总是一个对齐问题,从未到达正确的缓冲区,等等 下面是一些帮助代码,请参见下面的问题: 在主机上定义的结构:C++ 使用结构作为缓冲支架,c++,c,opencl,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,如下所示
所以,一旦为源映像或实验室映像创建了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];
}
__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.
}
其中wrkspc是一个用结构工作区分配的缓冲区,src+LAB只是作为1D数组映像分配的缓冲区
之后,在我的任何内核中,如果我想使用src或LAB,我会执行以下操作:
__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];
}
__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(elements*sizeof(cl_ulong)==26*8)。但我仍然有一个类似的问题,我以前的模型,我的指针读取其他地方在前一个图像的中间,等等< /P>
所以我想知道,是否有人曾经尝试过类似的模型(可能采用不同的方法),或者有任何技巧可以用它来建立一个“可靠”的模型
请注意,所有内核都经过了很好的编码,在AMD或CPU上使用相同的代码执行时,我有一个很好的结果。唯一的问题是关于Nvidia 不要试图跨内核边界存储GPU端指针值。它们不能保证保持不变。始终使用索引。如果内核使用特定的缓冲区,则需要将其作为参数传递给该内核
参考资料:
OpenCL1.2规范(据我所知,nvidia没有实现更新的标准)没有定义指向整数强制转换的指针的行为,反之亦然
第6.9p节指出:“声明为结构或联合的内核函数的参数不允许OpenCL对象作为结构或联合的元素传递。”这正是您试图做的:将缓冲区结构传递给内核
第6.9a节指出:“程序中内核函数的参数不能声明为指向指针的指针。”-这本质上就是您试图通过将指针转换为整数并返回来颠覆的。(第1点)您不能通过绕过类型系统来“欺骗”OpenCL,使其具有良好的定义
正如我在下面的注释线程中所建议的,您需要使用索引来保存缓冲区对象中的位置。如果要跨不同的内存区域存储位置,则需要将多个缓冲区统一为一个缓冲区,并将一个索引保存到这个巨大的缓冲区中,或者保存一个数字值来标识所引用的缓冲区。不要尝试跨内核边界存储GPU端指针值。它们不能保证保持不变。始终使用索引。如果内核使用特定的缓冲区,则需要将其作为参数传递给该内核
参考资料:
OpenCL1.2规范(据我所知,nvidia没有实现更新的标准)没有定义指向整数强制转换的指针的行为,反之亦然
第6.9p节指出:“声明为结构或联合的内核函数的参数不允许OpenCL对象作为结构或联合的元素传递。”这正是您试图做的:将缓冲区结构传递给内核
第6.9a节指出:“程序中内核函数的参数不能声明为指向指针的指针。”-这本质上就是您试图通过将指针转换为整数并返回来颠覆的。(第1点)您不能通过绕过类型系统来“欺骗”OpenCL,使其具有良好的定义
正如我在下面的注释线程中所建议的,您需要使用索引来保存缓冲区对象中的位置。如果您想跨不同的内存区域存储位置,您需要将多个缓冲区统一为一个,并将一个索引保存到这个巨大的缓冲区中,或者保存一个数字值来标识您所指的缓冲区。关于始终使用索引,您能否更具体一些?我不明白。@Vuwox如果需要在缓冲区中保存从一个内核调用到另一个内核调用的位置,则需要使用整数数组索引,而不是指针。OpenCL不允许在结构中包含指针。所以我已经在你的术语中存储了“索引”。你是说我应该这样做:wrkspc->getPtr[SRC_IMG]=SRC[0]代码>而不是&src[0]
?因为这只会将包含的值存储在imo的位置0。我不明白。OpenCL允许在结构中使用指针