我正在研究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 cache但性能更差)。此外,我需要将一些未更改的数据发送到常量内存,我再也不知道如何。
是否有任何有效的方法将此想法应用于OpenACC?我非常感谢你的帮助。
非常感谢, Behzad
答案 0 :(得分:0)
我认为你正在寻找的是声明D阵列是帮派私有的。如果你在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并行性。如果将其分成两个双嵌套循环,则可以将i
和j
循环折叠在一起并获得更多并行性。这看起来像这样。
!$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上的缓存性能会受到影响。虽然看看是否值得进行权衡,但值得尝试。
答案 1 :(得分: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。