0

我正在开发一个 OpenACC 计算流体动力学代码,通过将整体计算分解为一堆小操作来增加循环内计算的粒度。我的最终目标是通过将原始复杂任务拆分为 GPU 上更简单的一系列任务来减少每个威胁的寄存器数量。

例如,对于计算域的特定节点,我有许多公式要计算:

!$acc parallel loop ...
do i=1,n
  D1 = s(i+1,1) - s(i-1,1)
  D2 = s(i+1,2) - s(i-1,2)
  ...
  R = D1 + D2 + ...
enddo

如您所见,我可以将计算分散到一个块的线程中,最后将结果(通过归约)汇总到 R。因此,我定义了一个内部并行循环,如下所示:

!$acc parallel loop 
do i=1,n
  !$acc parallel loop ...
  do j=1,m  
    D[j] = s(i+1,j) - s(i-1,j)
  end
  !$acc parallel loop reduction(+:R)
  do j=1,m
    R = R + D[j]  
  enddo
enddo

但是,我需要将 D 定义为所有线程的共享内存,但实际上我不知道 OpenACC 的最佳方式是什么?(我使用了 !$acc 缓存,但性能更差)。此外,我需要将一些未更改的数据发送到常量内存,但我不知道该怎么做。

有没有什么有效的方法可以将这个想法实施到 OpenACC 中?我真的很感谢你的帮助。

非常感谢,贝扎德

4

2 回答 2

1

我认为您正在寻找的是将 D 数组声明为 gang-private。如果你在 C 中,我会说你可以在 i 循环中声明它。由于您在 Fortran 中,请尝试执行以下操作:

!$acc parallel loop private(D)
do i=1,n
  !$acc loop ...
  do j=1,m  
    D[j] = s(i+1,j) - s(i-1,j)
  end
  !$acc loop reduction(+:R)
  do j=1,m
    R = R + D[j]  
  enddo
enddo

一旦它对帮派来说是私有的,您实际上可能会发现该cache指令更有效。

今天早些时候我在看一个代码做类似的事情,你可能还想尝试将大小扩大D为 nxm,然后i在两个循环之间拆分j循环。现在编译器需要在两个j循环之间插入同步,另外它需要将i循环剥离成向量,所以你可能会失去 M/vector_length 并行性。如果将其分成两个双重嵌套循环,则可以将iandj循环折叠在一起并获得更多并行性。那看起来像这样。

!$acc parallel loop private(D)
do i=1,n collapse(2)
  do j=1,m  
    D(j,i) = s(i+1,j) - s(i-1,j)
  end
enddo
!$acc parallel loop private(D)
do i=1,n collapse(2) reduction(+:R)
  do j=1,m
    R = R + D(j,i)  
  enddo
enddo

折衷方案是 D 将需要更多存储空间,并且 CPU 上的缓存性能会受到影响。不过,也许值得尝试一下这种权衡是否值得。

于 2015-09-04T20:15:33.260 回答
0

以前的序列号版本是这样的:

do j=1,n
  do i=1,m
    a_x = s(i+1,j,1) - s(i-1,j,1)                   
    b_x = s(i+1,j,2) - s(i-1,j,2)                   
    c_x = s(i+1,j,3) - s(i-1,j,3)                  
    a_y = s(i,j+1,1) - s(i,j-1,1)                  
    b_y = s(i,j+1,2) - s(i,j-1,2)                  
    c_y = s(i,j+1,3) - s(i,j-1,3) 
    ...
    R1 = b_x + c_y
    R2 = a_x + a_y
    R3 = c_x + b_y
  enddo
enddo   

新方法是将计算拆分为 x 和 y 方向,如下所示(串行版本):

do j=1,n
  do i=1,m
    do k=1,2
      do m=1,3
        D(m) = s(i+dir(k,1),j+dir(k,2),m) - s(i-dir(k,1),j-dir(k,2),m)
      enddo             
      ...
      R_1(k) = a(k+1)
      R_2(k) = a(1)
      R_3(k) = a(3)
    enddo
    do k=1,2
      R1 = R1 + R_1(k)
      R2 = R2 + R_2(k)
      R3 = R3 + R_3(k)
    enddo
  enddo
enddo   

其中 dir(2,2) = {(1,0),(0,1)} 确定每个方向 (k) 的 2D 索引偏移。

现在我正在尝试通过 OpenACC 将此代码导入 GPU。

于 2015-09-08T21:57:57.117 回答