Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Optimization 如何为邻居访问优化OpenCL代码?_Optimization_Opencl_Gpgpu_Memory Access - Fatal编程技术网

Optimization 如何为邻居访问优化OpenCL代码?

Optimization 如何为邻居访问优化OpenCL代码?,optimization,opencl,gpgpu,memory-access,Optimization,Opencl,Gpgpu,Memory Access,编辑:建议的解决方案结果添加到问题的末尾 我开始用OpenCL编程,我已经为我的问题创建了一个简单的实现 理论是:我有一个3D元素网格,每个元素都有一组信息(大约200字节)。每一步,每个元素都会访问其邻居的信息,并积累这些信息以准备自我更新。在这之后,有一个步骤,每个元素使用之前收集的信息更新自己。这个过程是迭代执行的 我的OpenCL实现是:我创建一个一维的OpenCL缓冲区,用表示元素的结构填充它,其中有一个“int邻居”,我在缓冲区中存储邻居的索引。我启动了一个内核,该内核咨询邻居并将其

编辑:建议的解决方案结果添加到问题的末尾

我开始用OpenCL编程,我已经为我的问题创建了一个简单的实现

理论是:我有一个3D元素网格,每个元素都有一组信息(大约200字节)。每一步,每个元素都会访问其邻居的信息,并积累这些信息以准备自我更新。在这之后,有一个步骤,每个元素使用之前收集的信息更新自己。这个过程是迭代执行的

我的OpenCL实现是:我创建一个一维的OpenCL缓冲区,用表示元素的结构填充它,其中有一个“int邻居”,我在缓冲区中存储邻居的索引。我启动了一个内核,该内核咨询邻居并将其信息累积到本步骤中未咨询的元素变量中,然后我启动了另一个内核,该内核使用这些变量来更新元素。这些内核只使用全局变量

示例代码:

typedef struct{
  float4 var1;
  float4 var2;
  float4 nextStepVar1;
  int neighbors[8];
  int var3;
  int nextStepVar2;
  bool var4;
} Element;

__kernel void step1(__global Element *elements, int nelements){
  int id = get_global_id(0);
  if (id >= nelements){
    return;
  }
  Element elem = elements[id];

  for (int i=0; i < 6; ++i){
    if (elem.neighbors[i] != -1){
      //Gather information of the neighbor and accumulate it in elem.nextStepVars
    }
  }
  elements[id] = elem;
}

__kernel void step2(__global Element *elements, int nelements){
  int id = get_global_id(0);
  if (id >= nelements){
    return;
  }
  Element elem = elements[id];

  //update elem variables by using elem.nextStepVariables
  //restart elem.nextStepVariables
}
typedef结构{
4个变量1;
4个var2;
float4 nextStepVar1;
int邻居[8];
int-var3;
int nextStepVar2;
布尔瓦尔4;
}元素;
__内核无效步骤1(uu全局元素*元素,整数元素){
int id=获取全局id(0);
如果(id>=元素){
返回;
}
元素elem=元素[id];
对于(int i=0;i<6;++i){
if(元素邻居[i]!=-1){
//收集邻居的信息并将其累积到elem.nextStepVars中
}
}
元素[id]=elem;
}
__内核无效步骤2(uu全局元素*元素,整数元素){
int id=获取全局id(0);
如果(id>=元素){
返回;
}
元素elem=元素[id];
//使用elem.nextStepVariables更新elem变量
//重新启动elem.nextStepVariables
}

现在,我的OpenCL实现与我的C++实现基本相同。 所以,问题是:你(专家:p)将如何解决这个问题? 我读过有关3D图像的文章,通过将NDRange更改为3D来存储信息并更改邻域访问模式。此外,我还读过关于_本地内存的内容,首先加载工作组中的所有邻居,与屏障同步,然后使用它们,这样可以减少对内存的访问

你能给我一些建议来优化我描述的流程吗?如果可能的话,给我一些片段

编辑:由提出的第三次和第五次优化已在代码中。如前所述,要使结构正常工作,它们需要满足一些限制,因此值得理解这一点以避免麻烦

编辑1:应用performance建议的第七个优化从7 fps增加到60 fps。在更一般的实验中,性能增益约为x8

编辑2:应用性能提出的第一个优化增加了约x1.2。我认为实际收益更高,但由于另一个尚未解决的瓶颈而隐藏

编辑3:应用由提出的第8次和第9次优化并没有改变性能,因为缺乏利用这些优化的重要代码,但值得在其他内核中尝试

编辑4:将不变参数(如n#u元素或workgroupsize)传递到内核(如#定义)而不是内核参数,如前所述,提高了x1.33左右的性能。正如文档中所解释的,这是因为编译器在编译时知道变量时可以进行积极的优化

编辑5:应用由提出的第二个优化,但每个邻居使用1位,并使用逐位操作检查邻居是否存在(因此,如果邻居&1!=0,顶部邻居存在,如果邻居&2!=0,机器人邻居存在,如果邻居&4!=0,右邻居存在,等等),性能提高了1.11倍。我认为这主要是因为数据传输减少,因为数据移动一直是我的瓶颈。不久,我将尝试摆脱用于向结构添加填充的伪变量


编辑6:通过消除我正在使用的结构,并为每个属性创建单独的缓冲区,我消除了填充变量,节省了空间,并且能够优化全局内存访问和本地内存分配。性能提高了1.25倍,非常好。尽管编程复杂且不可读,但这样做是值得的。

根据您的步骤1和步骤2,您没有让您的gpu内核努力工作。内核的复杂性是什么?你的gpu的用途是什么?你检查过加力之类的监控程序吗?中端桌面游戏卡每次执行10k迭代可以获得10k线程

由于您只与邻居一起工作,数据大小/计算大小太大,并且您的内核可能会受到vram bandiwdth的限制。您的主系统ram可能与pci-e带宽一样快,这可能是问题所在

1)使用专用缓存可以使线程的实际网格单元以最快的速度进入私有寄存器。然后邻接到___本地数组中,因此比较/计算仅在芯片中完成

将电流传感器加载到_专用

将邻居加载到本地

开始本地数组的循环

让下一个邻居从本地进入私人区域

计算

端环

(如果它有许多邻居,那么“loadneighborientinto__local”后面的行可以位于另一个通过补丁从主内存获取的循环中)

你的gpu是什么?很好,它是GTX660。每个计算单元应该有64kB的可控缓存。CPU只有1kB的寄存器,不能对阵列操作进行寻址

2)较短的索引可以使用单个字节作为
 0=neighbour from left
 1=neighbour from right
 2=neighbour from up
 3=neighbour from down
 4=neighbour from front
 5=neighbour from back
 6=neighbour from upper left
 ...
 ...
 Get maximum cycles of latency for instructions and global memory access.
 Then divide memory latency by instruction latency.
 Now you have the ratio of: arithmetic instruction number per memory access to hide latency. 
 If you have to use N instructions to hide mem latency and you have only M  instructions in your code, then you will need N/M warps(wavefronts?) to hide latency because a thread in gpu can do arithmetics while other thread getting things from mem.