Parallel processing 并行fortran程序将在特定时间休眠

Parallel processing 并行fortran程序将在特定时间休眠,parallel-processing,fortran,openmp,Parallel Processing,Fortran,Openmp,最近,我不得不将一个用fortran编写的串行程序改为并行版本,以便更快地得到结果。但是我遇到了一些问题 我使用的是ubuntu操作系统和gfortran编译器,至于并行API,我使用的是OpeMP。在以前的(串行)版本中,我使用了许多模块来共享数据,但在openmp版本中,我将变量设置为threadprivate属性,其中一些变量具有allocatable属性。在以前的版本中,我在do循环之前为变量分配空间,但在openmp版本中,如果我执行相同的操作,程序将报告一个错误为无效内存引用,尽管我

最近,我不得不将一个用fortran编写的串行程序改为并行版本,以便更快地得到结果。但是我遇到了一些问题

我使用的是ubuntu操作系统和gfortran编译器,至于并行API,我使用的是OpeMP。在以前的(串行)版本中,我使用了许多模块来共享数据,但在openmp版本中,我将变量设置为threadprivate属性,其中一些变量具有allocatable属性。在以前的版本中,我在do循环之前为变量分配空间,但在openmp版本中,如果我执行相同的操作,程序将报告一个错误为无效内存引用,尽管我给它赋予了threadprivate属性。因此,我在循环中分配变量,并在循环中取消分配它。我在一个平行区域内做了一个do循环。它没有错误,程序可以运行。但还有另一个问题。由于它运行大约800分钟的cpu时间,我使用ps-ux命令查看这个并行程序的状态,它的状态从Rl变为Sl。我搜索S的含义,它表示

可中断睡眠(等待事件完成)

那么为什么会出现这个问题呢?是因为我经常分配和释放空间吗?以下是示例代码:

module variables
real, dimension(:), allocatable, save :: a
real, dimension(:,:), allocatable, save :: b
!$omp threadprivate(a,b)
integer, parameter :: n=100
contains
   subroutine alloc_var
   integer :: status
   allocate(a(100),stat=status)
   allocate(b(100:100),stat=status)
   end subroutine
   subroutine free_var
   integer :: status
   deallocate(a,stat=status)
   deallocate(b,stat=status)
   end subroutine
end module
对于其他子例程,有一些使用变量a和b

subroutine cal_sth
use variables, only a
...
end subroutine
对于串行版本主程序

program main
implicit none   
external :: cal_sth
use variables, only alloc_var,free_var
integer :: i, j
call alloc_var
do j=1, count1
...
other expresion ...
do i=1, count2
   call cal_sth
end do
end do
call free_var
end program
对于平行区域

program main
implicit none  
external :: cal_sth 
use variables, only alloc_var, free_var
integer :: i,j
!$omp parallel do private(i,j)
do j=1, count1
...
other expression ...
do i=1, count2
   call alloc_var
   call cal_sth
   if (logical expression) then
       call free_var
       cycle
   end if
   call free_var
end do
end do
end program

拆分组合的
parallel do
指令并重写并行循环,以便:

!$omp parallel
call alloc_var
!$omp do
do i=1, count
   call cal_sth
end do
!$omp end do
call free_var
!$omp end parallel
或根据Gilles的意见使用专用平行区域:

program main
implicit none  
external :: cal_sth 
use variables, only alloc_var, free_var
integer :: i
!$omp parallel
call alloc_var
!$omp end parallel
...
!$omp parallel do
do i=1, count
   call cal_sth
end do
!$omp end parallel do
...
! other OpenMP regions
...
!$omp parallel
call free_var
!$omp end parallel
end program

有了更新的代码,我认为您可以探索两条不同的途径来提高性能:

  • 内存分配:如前所述,对
    alloc\u var
    free\u var
    的调用只需要在
    并行
    区域中进行,但肯定不一定要在
    do
    循环中进行。通过将
    parallel do
    拆分为
    parallel
    ,然后再拆分为
    do
    ,可以在进入循环之前调用
    alloc\u var
    ,在退出循环之后调用
    free\u var
    。而且可能提前退出内部循环,可能需要释放/重新分配内存,这本身并不是阻止您这样做的约束。(有关如何实现这一点的示例,请参见下面的代码)

  • 调度:一些内部迭代的早期终止可能会导致线程之间的负载不平衡。这可以解释你实验的等待时间。显式地将调度设置为
    dynamic
    ,可以减少这种影响并提高性能。这需要进行一些试验,以找到最佳的调度策略,但是
    dynamic
    似乎是一个很好的起点

因此,这是您的代码,因为一旦实现了这两个想法:

program main
    implicit none  
    external :: cal_sth 
    use variables, only alloc_var, free_var
    integer :: i,j

    !$omp parallel schedule(dynamic)
    call alloc_var
    !$omp do private(i,j)
    do j=1, count1
        ...
        other expression ...
        do i=1, count2
            call cal_sth
            if (logical expression) then
                !uncomment these only if needed for some reasons
                !call free_var
                !call alloc_var
                cycle
            end if
        end do
    end do
    !$omp end do
    call free_var
    !$omp end parallel
end program

为什么不在一个专用的
并行
区域内一次性初始化
a
b
?比如:
$omp并行
调用alloc\u var
$omp end parallel
如果拆分
parallel do
指令,则会出现另一个问题,因为在parallel区域中,有一些情况需要
循环
循环,而我忘了将其添加到示例代码中。此外,这里不仅有一个循环。我将更改示例代码,请再次查看。如果我删除
schedule
子句,我仍然存在一个问题,即在某个特定时间进度将进入休眠状态。但是如果我加上这个子句,程序会跳转一些循环,比如从j=0到j=6,j=1到5循环不执行,你知道为什么吗?