全局存储器和局部存储器两者间大小不一样,同时全局存储器是片外存储空间,局部存储器是片内存储空间,两者读写速度不一样。合理地利用异步全局存储器和局部存储器间的数据交换和预取以掩盖其延迟,可以提升程序性能。
OpenCL提供了如下函数来实现工作组内全局存储器向局部存储器或局部存储器向全局存储器复制数据:
event_t async_work_group_copy( local(global) gentype *dst
const global(local)gentype *src,
size_t num_gentypes,
event_t event)
event_t async_work_group_strided_copy( local(global) gentype *dst,
const global(local)) gentype *src,
size_t num_gentype,
size_t dst_stride,
event_t event)
gentype类型为:char、uchar、short、ushort、int、uint、f loat、long、ulong、double和charn、ucharn、shortn、ushortn、intn、uintn、f loatn、longn、ulongn、doublen。n为2、3、4、8、16。
两个函数都在一个工作组内所有工作项都执行这个异步复制,否则结果未知。两个函数实现全局存储器与局部存储器间数据复制。由于函数是异步执行的,返回一个事件对象,可以用后面将会讲解的wait_group_events来等待异步复制完成。最后一个参数event还可以用来将上述函数与之前的一个异步复制关联,使得一个事件可以用多个异步复制共享,否则, event应为0。
参数dst和src分别为全局存储器/局部存储器,或局部存储器/全局存储器。参数num_gentypes为复制元素的个数。
需要注意的是,函数async_work_group_copy()是复制连续num_gentypes个元素,而函数async_work_group_strided_copy()是跳跃复制num_gentypes个元素,跳跃跨度为dst_stride。dst_stride是对应写至dst的各个gentype元素的元素跨度。
在图形学中我们经常用一个结构体来表示顶点位置、法线、纹理坐标信息。内核希望读取顶点位置来完成计算,再把计算后的顶点位置存储。这就需要一个跳跃复制,例如如下代码:
typedef struct
{
float4 position;
float3 normal;
float3 texcorrd;
} vertext_t;
kernel void PositionCompute(global vertext_t *vertices,
local float4 *pos_array)
{
event_t evt = async_work_group_strided_copy(
(local float *) pos_array,
(global float *)vertices,
4, sizesof(vertext_t) / sizeof(float), 0);
wait_group_events(1, evt);
……
evt = async_work_group_strided_copy(
(global float *)vertices,(local float *)pos_array,
4, sizesof(vertext_t) / sizeof(float),
0);
wait_group_events(1, evt);
}
由于异步复制返回的是一个事件对象,需要使用如下函数来等待这些事件结束:
void wait_group_events(int num_evetns, event_t *event_list)
在等待完成后释放event_list列表中指定的事件对象。也是需要工作组内所有工作项都执行这个函数,否则结果未知。
除了把数据在全局存储器和局部存储器间来回复制,还可以把全局存储器中的数据预取到全局缓存中,当需要使用这些数据时直接从全局缓冲中读取。
void prefectech(const global gentype *p , size_t num_gentypes)
gentype类型与函数async_work_group_copy()参数类型一样。