我以前在相对简单的情况下使用openMP。由于我不是那种经验,我需要一些关于如何在我的下面的代码中最好地使用openMP的建议。
基本上我的代码通过使用Runge-Kutta四阶方案求解两个耦合微分方程,在数字上数字地积分两个向量(向上和 un ) - 即每个及时前进四个临时步骤:
#define DUDT_P(STEP) cmplx(0,1)*( d(rng_0,1)*CONJG(un(rng_p1,STEP))*un(rng_p2,STEP) + \ d(rng_0,2)*CONJG(un(rng_m1,STEP))*up(rng_p1,STEP) + \ d(rng_0,3)* un(rng_m2,STEP) *up(rng_m1,STEP) ) #define DUDT_N(STEP) cmplx(0,1)*( d(rng_0,1)*CONJG(up(rng_p1,STEP))*up(rng_p2,STEP) + \ d(rng_0,2)*CONJG(up(rng_m1,STEP))*un(rng_p1,STEP) + \ d(rng_0,3)* up(rng_m2,STEP) *un(rng_m1,STEP) ) [...] do ii = 2, nt+1 do jj = 1,nti ! ---------- STEP 1/4 ---------- THERE MUST BE A BARRIER/CONSISTENT MEMORY VIEW HERE dup(rng_0,1) = DUDT_P(1) up(:,2) = up(:,1) + dt2*dup(:,1) dun(rng_0,1) = DUDT_N(1) un(:,2) = un(:,1) + dt2*dun(:,1) ! ---------- STEP 2/4 ---------- THERE MUST BE A BARRIER/CONSISTENT MEMORY VIEW HERE dup(rng_0,2) = DUDT_P(2) up(:,3) = up(:,1) + dt2*dup(:,2) dun(rng_0,2) = DUDT_N(2) un(:,3) = un(:,1) + dt2*dun(:,2) ! ---------- STEP 3/4 ---------- THERE MUST BE A BARRIER/CONSISTENT MEMORY VIEW HERE dup(rng_0,3) = DUDT_P(3) up(:,4) = up(:,1) + dt*dup(:,3) dun(rng_0,3) = DUDT_N(3) un(:,4) = un(:,1) + dt*dun(:,3) ! ---------- STEP 4/4 ---------- THERE MUST BE A BARRIER/CONSISTENT MEMORY VIEW HERE dup(rng_0,4) = DUDT_P(4) up(:,1) = up(:,1) + dt6*dup(:,1) + dt3*dup(:,2) + dt3*dup(:,3) + dt6*dup(:,4) ! Full/actual time stepped solution (full RK4 solution) dun(rng_0,4) = DUDT_N(4) un(:,1) = un(:,1) + dt6*dun(:,1) + dt3*dun(:,2) + dt3*dun(:,3) + dt6*dun(:,4) ! Full/actual time stepped solution (full RK4 solution) end do saved(:,ii,1) = up(:,1) ! Save solution saved(:,ii,2) = un(:,1) end do
现在,由于每个向量大20个组件,每个临时时间步骤需要大量的算术运算。 您可以通过查看衍生的 DUDT_P()和 DUDT_N()(宏)函数来看到这一点,每个向量一个,并注意它们涉及元素方法算法向量之间的 rng _ * 条目(此处所有 rng _ * 列表指定了 up 和 un 向量)。
有一个重要的约束,我在代码中也评论过。由于每个后续临时时间步的解决方案都取决于之前的步骤,因此在计算下一个temp之前,线程必须在 up 和 un 向量上具有相同的更新视图。时间步骤解决方案(即共享)。
为了清楚变量,这是我用过的符号:
我想过几种方法可以解决这个问题,然而,我陷入了困境:
感谢您的任何评论!
答案 0 :(得分:1)
你有一个非常好的ADD / MUL平衡。在这种CPU绑定代码上,ifort或PGI可以提供比gfortran更好的性能。你使用这样的编译器吗?
您是否为CPU(-xAVX
,-xCORE-AVX2
,-xSSE4.2
等)激活了最佳编译器选项?这可能会对您的特定示例产生很大影响。
如果你明确地设置了循环边界:do k=1,20
,
编译器将知道最适合循环的内容。
你也可以玩内存对齐。有了英特尔编译器,你
可以把指令:
!DIR$ ATTRIBUTES ALIGN : 32 :: dup, up, dun, un
声明变量后。 PGI编译器也可以。这将改善内存访问和编译器的矢量化可能性,因为您只能使用对齐的数据进行矢量化
在32字节边界上(或Xeon Phi上的64字节,以及旧CPU上的16字节)。
当您的第一个维度为20时,数组的所有列也都是32字节
对齐。你可以使用指令!DIR$ VECTOR ALIGNED
告诉编译器
在最内圈之前。
请尝试-O2
而不是-O3
,因为-O3
有时会变慢。
尝试使用flush-to-zero编译器选项:使用非规范化数字会降低执行速度
如果你想为up
使用一个线程,为un
使用一个线程,你可以尝试以下方法,
但是我真的不确定你会因为openMP障碍而获益(或事件不会失败......)。
线程0正在执行up
,线程1正在执行un
。放allocate
很重要
openMP部分中的语句和初始化,以便尽可能地分配内存
线程(第一次触摸政策)。
在这里,您的代码已经修改了所有这些想法:
!DIR$ ATTRIBUTES ALIGN : 32 :: dup, up, dun, un
#define DUDT_P(STEP) cmplx(0,1)*( d(rng_0,1)*CONJG(un(rng_p1,STEP))*un(rng_p2,STEP) + \
d(rng_0,2)*CONJG(un(rng_m1,STEP))*up(rng_p1,STEP) + \
d(rng_0,3)* un(rng_m2,STEP) *up(rng_m1,STEP) )
#define DUDT_N(STEP) cmplx(0,1)*( d(rng_0,1)*CONJG(up(rng_p1,STEP))*up(rng_p2,STEP) + \
d(rng_0,2)*CONJG(up(rng_m1,STEP))*un(rng_p1,STEP) + \
d(rng_0,3)* up(rng_m2,STEP) *un(rng_m1,STEP) )
[...]
!$OMP PARALLEL SHARED( up,un,dup,dun, [...] ) PRIVATE( [...] ) num_threads(2)
ithread = omp_get_thread_num()
!DIR$ ATTRIBUTES ALIGN : 32 :: dup, up, dun, un
if (ithread == 0) then
allocate (up(1:20,4), dup(1:20,4))
! Initialization is important to pin the memory to the cores
up = 0.
dun = 0.
else
allocate (un(1:20,4), dun(1:20,4))
! Initialization is important to pin the memory to the cores
un = 0.
dun = 0.
endif
do ii = 2, nt+1
do jj = 1,nti
!$OMP BARRIER
! ---------- STEP 1/4 ---------- THERE MUST BE A BARRIER/CONSISTENT MEMORY VIEW HERE
if (ithread == 0) then
dup(rng_0,1) = DUDT_P(1)
!DIR$ VECTOR ALIGNED
do k=1,20
up(k,2) = up(k,1) + dt2*dup(k,1)
end do
else
dun(rng_0,1) = DUDT_N(1)
!DIR$ VECTOR ALIGNED
do k=1,20
un(k,2) = un(k,1) + dt2*dun(k,1)
end do
endif
!$OMP BARRIER
! ---------- STEP 2/4 ---------- THERE MUST BE A BARRIER/CONSISTENT MEMORY VIEW HERE
if (ithread == 0) then
dup(rng_0,2) = DUDT_P(2)
!DIR$ VECTOR ALIGNED
do k=1,20
up(k,3) = up(k,1) + dt2*dup(k,2)
end do
else
dun(rng_0,2) = DUDT_N(2)
!DIR$ VECTOR ALIGNED
do k=1,20
un(k,3) = un(k,1) + dt2*dun(k,2)
end do
endif
!$OMP BARRIER
! ---------- STEP 3/4 ---------- THERE MUST BE A BARRIER/CONSISTENT MEMORY VIEW HERE
if (ithread == 0) then
dup(rng_0,3) = DUDT_P(3)
!DIR$ VECTOR ALIGNED
do k=1,20
up(k,4) = up(k,1) + dt*dup(k,3)
end do
else
dun(rng_0,3) = DUDT_N(3)
!DIR$ VECTOR ALIGNED
do k=1,20
un(k,4) = un(k,1) + dt*dun(k,3)
end do
endif
!$OMP BARRIER
! ---------- STEP 4/4 ---------- THERE MUST BE A BARRIER/CONSISTENT MEMORY VIEW HERE
if (ithread == 0) then
dup(rng_0,4) = DUDT_P(4)
!DIR$ VECTOR ALIGNED
do k=1,20
up(k,1) = up(k,1) + dt6*dup(k,1) + dt3*dup(k,2) + dt3*dup(k,3) + dt6*dup(k,4) ! Full/actual time stepped solution (full RK4 solution)
enddo
else
dun(rng_0,4) = DUDT_N(4)
!DIR$ VECTOR ALIGNED
do k=1,20
un(k,1) = un(k,1) + dt6*dun(k,1) + dt3*dun(k,2) + dt3*dun(k,3) + dt6*dun(k,4) ! Full/actual time stepped solution (full RK4 solution)
enddo
endif
end do
if (ithread == 0) then
do k=1,20
saved(k,ii,1) = up(k,1) ! Save solution
end do
else
do k=1,20
saved(k,ii,2) = un(k,1) ! Save solution
end do
end if
end do
if (ithread == 0) then
deallocate(un,up,dun,dup)
end if
!$OMP END PARALLEL