C++ 使用OpenCL的英特尔HD 6000本地内存带宽

C++ 使用OpenCL的英特尔HD 6000本地内存带宽,c++,c,memory-management,opencl,gpgpu,C++,C,Memory Management,Opencl,Gpgpu,我正在OpenCL中进行一些局部/全局内存优化;从两年前的情况来看,我认为我做错了什么,因为本地内存IO似乎比它应该的慢很多。我的GPU是Intel HD 6000 这是我的测试设置,内核源代码: __kernel void vecAdd(__global float* results, const unsigned int n, __local float* loc) { int id = get_global_id(0); if(id < n) { float

我正在OpenCL中进行一些局部/全局内存优化;从两年前的情况来看,我认为我做错了什么,因为本地内存IO似乎比它应该的慢很多。我的GPU是Intel HD 6000

这是我的测试设置,内核源代码:

__kernel void vecAdd(__global float* results, const unsigned int n, __local float* loc)
{
   int id = get_global_id(0);
   if(id < n) {
      float rtemp = 0;
      loc[23] = 34;
      for(int i = 0; i < 1024; i ++) {
         rtemp += loc[(i * 445) % 1024];
      }
      results[id] = rtemp;
   }
}
\uuuuu内核void vecAdd(\uuuu全局浮点*结果,常量unsigned int n,\uuuuu局部浮点*loc)
{
int id=获取全局id(0);
if(id
内核所做的就是获取本地浮点数组loc并将其随机值添加到全局输出向量中。片段“(i*445)%1024”用于确保随机访问本地内存;性能比最后提到的没有随机化的数字稍微好一点(加速约30%)

  • 我将内核排队等待16777216/16M次迭代,工作组大小为256,本地缓冲区为1024个浮点数,除l[23]外都是零

  • 总的来说,这使得对本地内存的总写入量为16M*1=16M,读取量为16M*1024=16G

  • 还有大约16M*1024*2的浮点运算,可能更多地取决于模的计算方式,但HD 6000的浮点性能大约为768千兆次,这不应该成为瓶颈

  • 16G浮点值读取导致64G内存被读取;内核的执行耗时453945μs,估计本地内存带宽151gb/s

参考问题中的数字表明,现代图形卡(从2014年开始)的内存带宽可能比我在机器上测得的要高得多;文章中引用的数字(可能是一个随机比较的例子)为3-4 TB/s;虽然我的卡是集成卡,而不是专用卡,但考虑到它在2015年的发布,这似乎仍然是一个缓慢的数字

更让人困惑的是,我在一些专用的中端GPU上的性能越来越差:AMD R9 m370x和Nvidia GT 750m都需要700-800毫秒。这些卡比Intel的HD 6000稍旧一些,因此可能与此有关


是否有任何可能的方法从本地内存中挤出更多的性能,或者我是否尽可能有效地利用本地内存?

答案位于edit2答案末尾的部分

如果专用gpu计时不好,您可以尝试流水线读取+计算+写入操作,如

从左到右,它在第二步开始重叠操作,因此计算延迟被隐藏,然后第三步也隐藏写入延迟。这是一个将独立作品分成4部分的示例。也许更多的部件会产生较慢的结果,每个设备都应该进行基准测试。内核执行只是一个“添加”,所以它总是隐藏的,但较重的可能不是。如果图形卡可以同时执行读写操作,这将减少I/O延迟。图中还显示了空闲(垂直空)的时间线,因为冗余的同步使其比打包但更快的版本更可读

您的igpu 151 GB/s带宽可能是cpu缓存。它没有可寻址的寄存器空间,所以即使使用_私有寄存器也可以使它从缓存中获取。每个cpu或gpu的缓存线宽度也不同

loc[23]=34

具有多个线程的争用条件并被序列化

而且有可能

对于(int i=0;i<1024;i++){ rtemp+=loc[(i*445)%1024]; }

自动展开并对指令缓存和缓存/内存施加压力。您可以尝试不同级别的展开

您确定每个igpu执行单元使用8个内核吗?可能每个EU只使用1个内核,这可能不足以充分强调缓存/内存(例如,使用所有第一个内核而不使用其他内核导致缓存线冲突)?尝试使用float8版本,而不是仅仅使用float。最新的英特尔CPU每秒超过1 TB

GFLOPS限值很少接近。大约有%50个代码经过优化,75个代码无法读取,90个代码没有意义


编辑:下面的代码是在AMD-R7-240卡上以900MHz(不超过30Gb/s内存和600GFLOPS)运行的,用于16M个结果元素

        __kernel void vecAdd(__global float* results )
        {
           int id = get_global_id(0);
           __local float loc[1024]; // some devices may slow with this
           if(id < (4096*4096)) {
              float rtemp = 0;
              loc[23] = 34;
              for(int i = 0; i < 1024; i ++) {
                 rtemp += loc[(i * 445) % 1024];
              }
              results[id] = rtemp;
           }
        }
\uuuuu内核void vecAdd(\uuuu全局浮点*结果)
{
int id=获取全局id(0);
__本地浮点loc[1024];//某些设备可能会因此而变慢
如果(id<(4096*4096)){
浮动rtemp=0;
loc[23]=34;
对于(int i=0;i<1024;i++){
rtemp+=loc[(i*445)%1024];
}
结果[id]=rtemp;
}
}
花了

  • 写入+计算+读取575毫秒(无管道)
  • 写入+计算+读取530毫秒(两部分流水线)
  • 写入+计算+读取510毫秒(8部分流水线)
  • 计算时间为455毫秒(140 GB/s本地内存带宽)
Edit2:优化缓存线利用率,简化计算,减少着色器核心中的气泡:

        __kernel void vecAdd(__global float* results )
        {
           int id = get_global_id(0);
           int idL = get_local_id(0);
           __local float loc[1024];
           float rtemp = 0;
           if(id < (4096*4096)) {

              loc[23] = 34;
           }

           barrier (CLK_LOCAL_MEM_FENCE);

           if(id < (4096*4096)) {
              for(int i = 0; i < 1024; i ++) {
                 rtemp += loc[(i * 445+ idL) & 1023];
              }
              results[id] = rtemp;
           }
        }
\uuuuu内核void vecAdd(\uuuu全局浮点*结果)
{
int id=获取全局id(0);
int idL=获取本地id(0);
__本地浮点数[1024];
浮动rtemp=0;
如果(id<(4096*4096)){
loc[23]=34;
}
屏障(CLK_本地_MEM_围栏);
如果(id<(4096*4096)){
对于(int i=0;i<1024;i++){
rtemp+=loc[(i*445+idL)和1023];
}
结果[id]=rtemp;
}
}
  • 写入+计算+读取325毫秒(16部分流水线)
  • 270mil
            __kernel void vecAdd(__global float* results )
            {
               int id = get_global_id(0);
               int idL = get_local_id(0);
               __local float loc[1024];
               float rtemp = 0;
               float rtemp2 = 0;
               float rtemp3 = 0;
               float rtemp4 = 0;
               if(id < (4096*4096)) {
    
                  loc[23] = 34;
               }
    
               barrier (CLK_LOCAL_MEM_FENCE);
    
               if(id < (4096*4096)) {
                  int higherLimitOfI=1024*445+idL;
                  int lowerLimitOfI=idL;
                  int stepSize=445*4;
                  for(int i = lowerLimitOfI; i < higherLimitOfI; i+=stepSize) {
                     rtemp += loc[i & 1023];
                     rtemp2 += loc[(i+445) & 1023];
                     rtemp3 += loc[(i+445*2) & 1023];
                     rtemp4 += loc[(i+445*3) & 1023];
                  }
                  results[id] = rtemp+rtemp2+rtemp3+rtemp4;
               }
            }