将2D或3D数据复制到本地存储器的最佳方法

时间:2013-08-08 20:21:38

标签: opencl

我开始在3D中为我的OpenCL内核进行过滤。 是否有最佳方法将2D或3D子集从全局内存复制到本地或私有内存?

这可以用于获取3D数据集并应用3D内核(或在3D内核占用的空间上操作)。每个线程将查看一个像素,在3个维度中裁剪像素周围的数据,这是内核的大小(比如1,3,5等),将此数据子集复制到本地或私有内存,然后例如,计算数据子集的标准偏差。

最简单,效率最低的方法就是蛮力:

__kernel void Filter_3D_StdDev(__global float *Data_3D_In,
                               int KernelSize){
//Note: KernelSize is always ODD

int k = get_global_id(0); //also z
int j = get_global_id(1); //also y
int i = get_global_id(2); //also x

//Convert 3D to 1D
int linear_coord = i + get_global_size(0)*j + get_global_size(0)*get_global_size(1)*k;

//private memory
float Subset[KernelSize*KernelSize*KernelSize];

int HalfKernel = (KernelSize - 1)/2; //compute the pixel radius

for(int z = -HalfKernel ; z < HalfKernel; z++){
     for(int y = -HalfKernel ; y < HalfKernel; y++){
          for(int x = -HalfKernel ; z < HalfKernel; x++){
               int index = (i + x) + get_global_size(0)*(j + y) + \            
                                  get_global_size(0)*get_global_size(1)*(k + z);
               Subset[x + HalfKernel + (y + HalfKernel)*KernelSize + (z + HalfKernel)*KernelSize*KernelSize] = Data_3D_In[index];
          }

     }
}

//Filter subset here

}

由于对全局内存进行了如此多的调用,因此效率非常高。有没有办法改善这个?

我的第一个想法是使用vload来减少循环次数,例如:

__kernel void Filter_3D_StdDev(__global float *Data_3D_In,
                               int KernelSize){
//Note: KernelSize is always ODD

int k = get_global_id(0); //also z
int j = get_global_id(1); //also y
int i = get_global_id(2); //also x

//Convert 3D to 1D
int linear_coord = i + get_global_size(0)*j + get_global_size(0)*get_global_size(1)*k;

//private memory
float Subset[KernelSize*KernelSize];

int HalfKernel = (KernelSize - 1)/2; //compute the pixel radius

for(int z = -HalfKernel ; z < HalfKernel; z++){
     for(int y = -HalfKernel ; y < HalfKernel; y++){
          //##TODO##
          //Automatically determine which vload to use based on Kernel Size
          //for now, use vload3
               int index = (i + -HalfKernel) + get_global_size(0)*(j + y) + \            
                                  get_global_size(0)*get_global_size(1)*(k + z);
               int subset_index = (z + HalfKernel)*KernelSize*KernelSize
               float3 temp = vload3(index, Data_3D_In);
               vstore3(temp, subset_index, Subset);

     }
}

//Filter subset here

}

还有更好的方法吗?

提前致谢!

1 个答案:

答案 0 :(得分:1)

首先你需要展开这些循环。在编译之前,您必须制作函数的多个副本或进行字符串替换,或者首先展开循环,但就像测试一样:

#define HALF_KERNEL_SIZE = 2
#pragma unroll HALF_KERNEL_SIZE * 2 + 1
for(int z = -HALF_KERNEL_SIZE ; z < HALF_KERNEL_SIZE ; z++){
    #pragma unroll HALF_KERNEL_SIZE * 2 + 1
    for(int y = -HALF_KERNEL_SIZE ; y < HALF_KERNEL_SIZE ; y++){

对于GPU,您应该将其读入本地内存(特别是对于5x5x5的内存,因为您正在读回全局内存A LOT,当您已经拥有数据并且您不想回去获取它时。)对于CPU来说,它不是一个大问题。

这样做就像你对卷积一样,但有一个额外的维度:

1. Read in a block (or cube) of memory into local memory for a number of threads.
2. Create a barrier to make sure all data is read before you continue.
3. Sample into your local memory using your local id as an offset.
4. Test various local workgroup sizes until you get best performance

其他一切都是一样的。对于具有更大重叠的较大内核,这将是更快的manatudes订单。