OpenCL中本地内存的优势是什么?

时间:2014-02-19 06:31:37

标签: image-processing local-storage opencl

我想知道本地内存的优势。由于全局内存可以单独和自由地获取项目。我们不能只使用全局内存吗?

例如,我们有一个1000 * 1000的图像,我们想要添加每个像素值1.我们可以使用1000 * 1000的全局内存吗?

如果我们使用本地内存并将1000 * 1000图像转换为100个100 * 100个部分,我们会更快吗?

如果你给我一个简单的本地记忆代码,我会非常感谢你。

4 个答案:

答案 0 :(得分:6)

  

我们不能只使用全局记忆吗?

当然可以。首先写一个实际的工作代码。然后优化。

  

由于全局内存可以单独自由地获取项目

我不确定所有架构是否都具有广播能力。 但我确定如果所有线程都随机访问内存,它会变得太慢。 光线跟踪就是一个例子。每个像素折射/反射到不同的距离和不同的存储区域。这是一个性能打击。如果每个线程都以统一的方式访问全局内存,那么速度会快得多。

  

我们可以使用1000 * 1000的全局内存吗?

最大缓冲区大小最小值,它可以是128MB或1/4的设备内存。所有缓冲区的组合大小可能因平台/设备而异,范围为几GB。

  

如果我们使用本地内存并转换1000 * 1000,它会更快吗?   图像分为100 100 * 100个部分?

这取决于数据重用率和访问模式的合并性。对本地内存的随机(非合并)访问比对全局内存的随机(非合并)访问快得多。如果你使用太多的本地内存/私有文件,那么它可能会更慢,因为更多的本地内存消耗会导致更少的占用和更少的内存延迟隐藏以及更多的寄存器溢出到全局内存。尝试使用私有寄存器来平衡它。或者,您可以使用压缩技术将更多数据放入本地内存中。

如果重复使用每个数据256次,那么本地内存的速度将比全局内存访问速度快10-20倍。

这是一个非常简单的用于力计算的2D nbody代码:

// global memory access is only 257 times per item, 1 for private save
//                                                  256 for global broadcast
//                                                  for global-to-local copy
// unoptimized version accesses 65537 times per item.
__kernel void nBodyF(__global float *x, __global float *y,
                     __global float *vx, __global float *vy,
                     __global float *fx, __global float *fy)
{
     int N=65536; // this is total number of masses for this example
     int LN=256;  // this is length of each chunk in local memory, 
                  // means 256 masses per compute unit
    int i=get_global_id(0);  // global thread id keys 0....65535
    int L=get_local_id(0);   // local thread id keys 0...255 for each group
    float2 Fi=(float2)(0,0); // init
    float xi=x[i]; float yi=y[i]; // re-use for 65536 times
    __local xL[256]; __local yL[256]; //declare local mem array with constant length


    for(int k=0;k<N/LN;k++) // number of chunks to fetch from global to local
    {
        barrier(CLK_LOCAL_MEM_FENCE);  //synchronization
        xL[L]=x[k*LN+L]; yL[L]=y[k*LN+L]; //get 256-element chunks into local mem
        barrier(CLK_LOCAL_MEM_FENCE);  //synchronization
        for(int j=0;j<LN;j++)          //start processing local/private variables
        {
            float2 F=(float2)(0,0);          // private force vector init
            float2 r1=(float2)(xi,yi);       // private vector
            float2 r2=(float2)(xL[j],yL[j]); // use local mem to get r2 vector
            float2 dr=r1-r2;                 // private displacement
            F=dr/(0.01f+dot(dr,dr));         // private force calc.
            Fi.x-=F.x; Fi.y-=F.y;            // private force add to private
        }
     }
     fx[i]=Fi.x; fy[i]=Fi.y; //write result to global mem only once
}

上面的示例在本地内存重用率方面较差。但是有一半的变量存在于私有内存中,并且重复使用了64k次。

最坏情况:

  1)Big portion of items cannot fit GPU cache.
  2)Only global memory accesses are done
  3)Data is not re-used
  4)Memory is accessed in a very non-uniform way.
  This will make it very slow.
  When data doesnt fit cache and not re-used, you should use __read_only for
  necessary buffers(__write_only for writing).

如果进行卷积(或一些抗锯齿或边缘检测),重复使用的数据将是4到20,本地内存优化至少可以提供3-4倍的性能。

如果您的GPU具有300GB / s的全局内存带宽,那么其本地内存带宽将约为3-4 TB / s。您也可以优化私人寄存器!那么它可能是15-20 TB / s。但是这种类型的使用区域较少。

编辑:如果您正在读取单个字节,并且这些字节之间只有一个较小的值(例如,最大值为16),那么您可以将多个变量打包成单个字节并在本地存储器中解密它们。例如:

  Global memory(copied to local mem): 
  Reference_byte   Byte0  byte1        byte2         byte3  
  128              +3,-5  +24,+50      -25,-63      0, +2

  Unpacking in local memory:
  Reference_byte   Byte0  byte1 byte2 byte3 Byte4  byte5 byte6 byte7      
  128              131    126   150   200   175    112   112   114

  Computing results on the array
  Reference_byte   Byte0  byte1 byte2 byte3 Byte4  byte5 byte6 byte7 
  128              120    130   140   150   150    150   100   110

  Packing results in local memory:
  Reference_byte   Byte0  byte1        byte2         byte3  
  128              -8,+10 +10,+10      0,0           -50, +10

  Global memory(copied from local mem): 
  Reference_byte   Byte0  byte1        byte2         byte3  
  128              -8,+10 +10,+10      0,0           -50, +10

  //Maybe a coordinate compression for a voxel rendering.

使用可为您提供缓存行使用信息的分析器。

答案 1 :(得分:4)

TL; DR:本地内存要快得多。当您需要多次访问数据或想要在同一工作组中的工作项之间共享数据时,请使用它。

本地存储器通常位于处理器本身内部,并以或接近芯片的时钟速度运行。当您使用cpu进行opencl时,实际上在使用本地数据结构时指的是高速缓存。由于本地存储器占用了许多片内晶体管,因此有很多可用的存储器很昂贵。今天的cpu实际上利用了最多三级缓存,并且仍然只能产生8到24MB(有时在专用处理器中更多)。同样的概念适用于视频卡 - 本地内存有限,存在于GPU内部,运行速度极快。

全局存储器作为cpu / gpu外部的存储器芯片存在,并通过存储器控制器连接到处理器。现代系统的内存控制器内置于处理器中,但过去它们可能是计算机主板上的附加芯片。内存控制器实际上是全局内存必须比本地(缓存)内存慢得多的原因。任何核心(或opencl计算单元)请求的每一位数据都必须被请求,排队并传输到处理器。存储器和控制器的时钟速度极大地影响信号完整性。

然而,与本地存储器相比,全局存储器便宜。我们今天可以购买8GB的系统内存,价格低于100美元。显卡通常不会有不到1GB的全局内存,2-4GB的数量正在变得普遍。

本地存储器比全局存储器快一个数量级,并且比全局存储器贵2到3个数量级。 (将RAM与硬盘容量,速度和成本进行比较时也可以这样说)

什么时候应该使用本地内存? 当您的算法需要从相同的内存地址重复读取时,可以看到最大的好处。因为本地内存比全局内存快得多,所以即使单次重用数据也可以产生改进。重新读取内存的次数越多,您对全局内存控制器性能的依赖程度就越小,而算术逻辑单元可以在更多时钟周期内处理数据(称为ALU饱和)。

如果算法需要在同一组中的两个工作单元之间进行通信,则还需要使用本地内存。这是通过opencl实现的,具有本地读/写和障碍。这也可以通过使用全局内存来实现,但速度只是本地内存可以提供的速度的一小部分。可以这样想:组中的工作项需要共享一个简单的数据结构,也许是想要累积总和的浮点数。您可以1)将其存储在gpu / cpu中的本地工作组共享内存位置,并将以下读取同步到变量,或者2)将32位浮点数发送到全局内存控制器,将其排队以进行写入,将值写入全局内存,通过内存控制器将数据请求返回到另一个工作项,然后返回处理器。

只有几个受益于本地工作组共享内存的算法示例包括矩阵 - 矩阵乘法,矢量矩阵乘法,n体系统,一些图像滤波器以及完全适合本地存储器的小型集合。

对于将1000 ^ 2像素分解为100 ^ 3像素的具体示例,没有任何好处。这些值只读取一次,修改和写入一次。如果你正在执行诸如模糊之类的过滤器,那么将其分解会有一个好处,并且必须读取周围的像素来计算给定像素的新值。在决定制作这些片段的小小时,您必须要小心碎片化的数据结构可以适合目标opencl设备的本地内存。

答案 2 :(得分:2)

1)为什么要使用本地内存:    全局存储器的大小很大,但在数据检索/数据访问方面较慢,而本地存储器的尺寸要小得多,但与全局存储器相比却非常快。

简单来说,如果使用正确,本地内存将充当全局内存的缓存(实际上并非如此)。

2)对本地内存的使用没有限制,它取决于应用程序需求和程序员对性能的满意度。

3)何时使用本地内存      如果你看一下缓存,它会将数据保存在内存中,而这些数据正在被高度重用,以避免高数据访问成本。对于本地存储器,可以遵循类似的方法,如果工作组中的线程一次又一次地访问相同的数据(或数据段),则可以将这些数据(或数据段)拉到本地存储器,这样就可以将数据访问权限丢弃到每次重复使用从全局到本地的成本。

您可以通过简单的矩阵乘法here

看到性能提升

答案 3 :(得分:2)

本地内存比全局内存具有更高的带宽和更低的延迟。因此,如果同一工作组中的线程在本地内存中共享数据,则与在全局内存中进行共享相比,它们可以更快地访问它。这里需要指出的关键是,同一工作组中的线程必须具有相互共享的数据,否则本地内存没有任何好处。在您的示例中,每个像素仅使用一次,因此本地内存对此没有帮助。

本地存储器有用的两个例子是卷积(例如高斯模糊图像)和并行缩减。在并行缩减中,线程生成需要在工作组中共享的中间结果。只有最终结果才会被该工作组中的单个线程写入全局内存。利用卷积,在计算其相邻像素的模糊时,每个像素被重新使用,因此本地存储器可用于存储图像的补丁以供相邻线程重用。如果其中任何一个有用,请在评论中告诉我你想要一个例子。