2015-11-05 65 views
1

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

我正在使用ubuntu os和gfortran编译器,至于并行API,我使用的是OpeMP。在以前的(串行)版本中,我使用了许多模块来共享数据,但是在openmp版本中,我使用变量threadprivate属性,并且其中一些变量具有可分配属性。在之前的版本中,我在do循环之前为变量分配空间,但在openmp版本中,如果我这样做,程序将报告错误为无效的内存引用,尽管我给它指定了threadprivate属性。所以我在循环中分配变量并在循环中释放它。我在并行区域做了do循环。它没有错误,程序可以运行。但还有另一个问题。由于它运行大约800分钟的CPU时间,并且我使用ps -ux命令查看该并行程序的状态,其状态从R1改变为S1。我搜索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 
+0

你为什么不只是初始化了''和'B'一劳永逸内专用'平行区域?像:'!$ omp parallel''调用alloc_var''!$ omp end parallel' – Gilles

回答

2

要么分裂组合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 

个或使用专用并行区域按照吉尔评论:

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 
+0

如果我分割'parallel do'指令,还有另一个问题,因为在并行区域中,有一些情况需要'循环'循环,我忘了添加在示例代码中。另外,不仅有一个循环。我将更改示例代码,并请再次查看。 – zmwang

1

有了更新的代码,我认为你必须探索提高性能的两种不同的路径:

  • 内存分配:正如前面提到alloc_varfree_var的调用只需要在parallel区域进行,但绝对不一定在do循环内。通过将parallel do拆分为parallel,然后do,它为您提供了进入循环前呼叫alloc_var的空间,并在退出循环之后调用free_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 
+0

我仍然有问题,如果我删除'schedule'子句,在某个特定时间进度会进入睡眠状态。但是如果我添加这个子句,程序会跳转一些循环,如从j = 0到j = 6,j = 1到5循环不执行,你知道这是为什么吗? – zmwang