我的目标是离开 MATLAB,转而使用 Fortran 完成我的大部分工作。其中一项努力是通过 MATLAB 的 parfor 循环将并行化替换为 Fortran openMP 指令。这总是更快,但由于某种原因,使用 openMP 的 CPU 利用率(由 taskmgr 测量)低于 parfor(特别是对于较小的问题)。我的假设是,这是由于通信开销造成的,如果 CPU 利用率接近 100%(如 MATLAB),那么对于小问题,代码会快得多。我的问题有两个:
- 有没有办法提高以下代码的效率(使用 openMP 指令)?
- 如果不是,那么效率低下的根源是什么?您建议如何补救?
尝试(不成功)的解决方案:
- 添加子句 collapse(5)(用于 5 个嵌套循环)
- 明确声明一切(即,不使用默认(共享))
- KMP_SET_BLOCKTIME(1000) 保持线程打开直到下一个 omp 并行执行
CPU 利用率数据(Windows 7 64 位,双四核英特尔至强 3Ghz):
小问题 (*_pts = 5):
Fortran (openMP), 时间: 40s, CPU util.: 60%
MATLAB (parfor), 时间: 45s, CPU util.: 90%
-> MATLAB 耗时 1.125 倍中等问题 (*_pts = 6):
Fortran (openMP), 时间: 78s, CPU util.: 75%
MATLAB (parfor), 时间: 96s, CPU util.: 90%
-> MATLAB 耗时 1.231 倍大问题(*_pts = 7):
Fortran(openMP),时间:150s,CPU util.:100%
MATLAB(parfor),时间:205s,CPU util.:100%
-> MATLAB 耗时 1.367 倍
例子:
do while (converged == -1)
istart = omp_get_wtime() ! Iteration timer start
!$omp parallel do default(shared) private(start,state,argzero)
do i5 = 1,Oepsr_pts
do i4 = 1,Ozeta_pts
do i3 = 1,Oz_pts
do i2 = 1,Or_pts
do i1 = 1,Opd_pts
start(1,1) = pfn(i1,i2,i3,i4,i5)
start(2,1) = pfx1(i1,i2,i3,i4,i5)
start(3,1) = pfx2(i1,i2,i3,i4,i5)
state = [Gpd_grid(i1),Gr_grid(i2),Gz_grid(i3),Gzeta_grid(i4),Gepsr_grid(i5)];
! Find optimal policy functions on each node
argzero = 0.d0
call csolve(start,nstate,npf,nshock,Opd_pts,Or_pts,Oz_pts,Ozeta_pts,Oepsr_pts,Omono_pts,state, &
Smu,Schi,Sr,Sy,Pomega,Ptheta,Psigma,Peta,Pzbar, &
Prhor,Ppi,Pphipi,Pphiy,Prhoz,Pzetabar,Prhozeta,Pbeta, &
Gpd_grid,Gr_grid,Gz_grid,Gzeta_grid,Gepsr_grid,Gmono_nodes,Gmono_weight, &
pfn,pfx1,pfx2,argzero)
! Store updated policy functions
pfn_up(i1,i2,i3,i4,i5) = argzero(1,1)
pfx1_up(i1,i2,i3,i4,i5) = argzero(2,1)
pfx2_up(i1,i2,i3,i4,i5) = argzero(3,1)
end do
end do
end do
end do
end do
!$omp end parallel do
! Policy function distances
dist_n = abs(pfn_up - pfn);
dist_x1 = abs(pfx1_up - pfx1);
dist_x2 = abs(pfx2_up - pfx2);
! Maximum distance
dist_max(it) = max(maxval(dist_n),maxval(dist_x1),maxval(dist_x2));
! Update policy functions
pfn = pfn_up;
pfx1 = pfx1_up;
pfx2 = pfx2_up;
! Check convergence criterion
if ((it > 11) .AND. all(dist_max(it-10:it) < Ptol)) then
converged = 1;
else if (dist_max(it) > 10 .OR. it > 2500) then
converged = 0;
end if
! Iteration Information
iend = omp_get_wtime()
if (mod(it,3) == 1 .OR. converged == 1 .OR. converged == 0) then
call itinfo(tstart,istart,iend,it,dist_max(it));
else
it = it + 1
end if
end do