2

当我尝试通过 OpenMP 在 Fortran90 中并行化我的程序时,我收到了分段错误错误。

    !$OMP PARALLEL DO NUM_THREADS(4) &
    !$OMP PRIVATE(numstrain, i)
    do irep = 1, nrep
        do i=1, 10
            PRINT *, numstrain(i)
        end do
    end do
    !$OMP END PARALLEL DO

我发现如果我注释掉“PRINT *, numstrain(i)”或删除 openmp 标志,它可以正常工作。我认为这是因为当我并行访问 numstrain(i) 时会发生内存访问冲突。我已经将 i 和 numstrain 声明为私有变量。有人可以告诉我为什么会这样吗?非常感谢。:)

更新:

我修改了以前的版本,这个版本可以打印出正确的结果。

integer, allocatable :: numstrain(:)
integer :: allocate_status
integer :: n
!$OMP PARALLEL DO NUM_THREADS(4) &
!$OMP PRIVATE(numstrain, i)
n = 1000000
do irep = 1, nrep
    allocate (numstrain(n), stat = allocate_status)
    do i=1, 10
        PRINT *, numstrain(i)
    end do
    deallocate (numstrain, stat = allocate_status)
end do
!$OMP END PARALLEL DO

但是,如果我将 numstrain 访问移动到由该子例程调用的另一个子例程(代码附在下面),1. 它总是在一个线程中处理。2. 在某个点(i=4 或 5),它返回 Segmentation Fault:11。当我有不同的 NUM_THREADS 时,它返回 Segmentation Fault:11 时的变量 i 是不同的。

integer, allocatable :: numstrain(:)
integer :: allocate_status
integer :: n
!$OMP PARALLEL DO NUM_THREADS(4) &
!$OMP PRIVATE(numstrain, i)
n = 1000000
do irep = 1, nrep
    allocate (numstrain(n), stat = allocate_status)
    call anotherSubroutine(numstrain)
    deallocate (numstrain, stat = allocate_status)
end do
!$OMP END PARALLEL DO

subroutine anotherSubroutine(numstrain)
    integer, allocatable   :: numstrain(:)
    do i=1, 10
        PRINT *, numstrain(i)
    end do
end subroutine anotherSubroutine

我还尝试在帮助子程序和主子程序中分配/解除分配,并且只在帮助子程序中分配/解除分配。什么都没有改变。

4

1 回答 1

2

最典型的原因是堆栈上没有足够的空间来保存numstrain. 计算并比较以下两个值:

  • 数组的大小(以字节为单位)
  • 堆栈大小限制

有两种堆栈大小限制。主线程的堆栈大小由 Unix 系统上的进程限制(用于ulimit -s检查和修改此限制)或在 Windows 上的链接时固定(需要重新编译或二进制编辑可执行文件以更改限制) )。附加 OpenMP 线程的堆栈大小由环境变量控制,例如标准OMP_STACKSIZE或特定于实现的GOMP_STACKSIZE(GNU/GCC OpenMP) 和KMP_STACKSIZE(Intel OpenMP)。

请注意,大多数 Fortran OpenMP 实现总是将私有数组放在堆栈上,无论您是否启用在堆上分配大型数组的编译器选项(使用 GNUgfortran和 Intel测试ifort)。

如果您注释掉该PRINT语句,您就有效地删除了对的引用,numstrain并且编译器可以自由地对其进行优化,例如,它不能简单地制作 的私有副本numstrain,因此不会超过堆栈限制。


在您提供的其他信息可以得出结论之后,堆栈大小不是罪魁祸首。在处理private ALLOCATABLE数组时,您应该知道:

  • 未分配数组的私有副本保持未分配;
  • 分配数组的私有副本分配有相同的界限。

如果您不在numstrain并行区域之外使用,则可以按照您在第一种情况下所做的操作,但需要进行一些修改:

integer, allocatable :: numstrain(:)
integer :: allocate_status
integer, parameter :: n = 1000000
interface
   subroutine anotherSubroutine(numstrain)
      integer, allocatable :: numstrain(:)
   end subroutine anotherSubroutine
end interface

!$OMP PARALLEL NUM_THREADS(4) PRIVATE(numstrain, allocate_status)
allocate (numstrain(n), stat = allocate_status)
!$OMP DO
do irep = 1, nrep
   call anotherSubroutine(numstrain)
end do
!$OMP END DO
deallocate (numstrain)
!$OMP END PARALLEL

如果您还在numstrain并行区域之外使用,则分配和释放将在外部进行:

allocate (numstrain(n), stat = allocate_status)
!$OMP PARALLEL DO NUM_THREADS(4) PRIVATE(numstrain)
do irep = 1, nrep
   call anotherSubroutine(numstrain)
end do
!$OMP END PARALLEL DO
deallocate (numstrain)

您还应该知道,当您调用将ALLOCATABLE数组作为参数的例程时,您必须为该例程提供显式接口。您可以编写一个INTERFACE块,也可以将被调用的例程放在一个模块中,然后放在该USE模块中——这两种情况都将提供显式接口。如果您不提供显式接口,编译器将无法正确传递数组,并且子例程将无法访问其内容。

于 2012-11-26T09:14:35.697 回答