【问题标题】:Fortran Allocatable Array Member of a User-Defined Type用户定义类型的 Fortran 可分配数组成员
【发布时间】:2016-05-08 09:00:06
【问题描述】:

在以下简单程序中,我在派生类型的可分配数组成员处遇到分段错误。此分段错误仅发生在我尝试过的另一台机器(在 openSUSE 上使用 Intel Fortran 14.0.3)上,但不会出现在另一台机器上(在 Ubuntu 上使用 Intel Fortran 14.0.2)。此外,如果我更改程序中的整数参数之一,程序将正常结束。

有人能重现这个问题吗?谁能告诉我代码有什么问题?

以下是三个源代码文件。

main_dbg.f90 .. 是否发生分段错误取决于该文件中n1n2 的值。

PROGRAM dbg
  USE tktype
  USE mymodule, ONLY : MyClass, MyClass_constructor
  IMPLICIT NONE

  INTEGER(I4B)                :: n1,n2,n3
  TYPE(MyClass)               :: o_MyClass

  n1=23
  n2=32
  ! .. this does not work.
  ! n2=31 
  ! .. this works.
  n3 = n1*n2
  write(*,'(1X,A,I10)') 'n1=', n1
  write(*,'(1X,A,I10)') 'n2=', n2
  write(*,'(1X,A,I10)') 'n3=', n3

  o_MyClass = MyClass_constructor(n1, n2, n3) 

  call o_MyClass%destructor()
  write(*,*) '***************************'
  write(*,*) '   Normal End :)           '
  write(*,*) '***************************'

END PROGRAM dbg

strange.f90 .. 此文件中的forall 构造发生分段错误。

!*******************************************************************
MODULE mymodule
!*******************************************************************
  USE tktype
  IMPLICIT NONE
  PRIVATE

  PUBLIC MyClass
  PUBLIC MyClass_constructor

  TYPE :: MyClass
     PRIVATE
     REAL(DP),     DIMENSION(:),     ALLOCATABLE :: arrA
     COMPLEX(DPC), DIMENSION(:,:,:), ALLOCATABLE :: arrB
   CONTAINS
     PROCEDURE :: destructor
  END TYPE MyClass

! ================================================================
CONTAINS
! ================================================================

  ! ****************************************************************
  FUNCTION MyClass_constructor(n1, n2, n3) RESULT(this)
  ! ****************************************************************
    TYPE(MyClass)                :: this
    INTEGER(I4B),    INTENT(IN)  :: n1, n2, n3
    ! local variables
    INTEGER(I4B) :: j1, j2, j3

    write(*,'(1X,A)') 'entered constructor..'

    allocate(this%arrA(n2))
    allocate(this%arrB(n1, n2, n3))

    this%arrA = 1.0_dp

    write(*,*) 'size(this%arrB,1) =', size(this%arrB,1)
    write(*,*) 'n1                = ', n1
    write(*,*) 'size(this%arrB,2) =', size(this%arrB,2)
    write(*,*) 'n2                = ', n2
    write(*,*) 'size(this%arrB,3) =', size(this%arrB,3)
    write(*,*) 'n3                = ', n3

    forall(j1=1:n1, j2=1:n2, j3=1:n3)
       this%arrB(j1,j2,j3)  = this%arrA(j2) 
    end forall

    write(*,'(1X,A)') '..leaving constructor'

  END FUNCTION MyClass_constructor


  ! ****************************************************************
  SUBROUTINE destructor(this)
  ! ****************************************************************
    CLASS(MyClass),             INTENT(INOUT) :: this

    deallocate(this%arrA)
    deallocate(this%arrB)

  END SUBROUTINE destructor

END MODULE mymodule

tktype.f90

! ********************************************************************
MODULE tktype
! ********************************************************************
!   module tktype is an extraction of module nrtype in Numerical Recipes in 
!   Fortran 90.
! ********************************************************************
  !   Symbolic names for kind types of 4-, 2-, and 1-byte integers:
  INTEGER, PARAMETER :: I4B = SELECTED_INT_KIND(9)
  INTEGER, PARAMETER :: I2B = SELECTED_INT_KIND(4)
  INTEGER, PARAMETER :: I1B = SELECTED_INT_KIND(2)
  !   Symbolic names for kind types of single- and double-precision reals:
  INTEGER, PARAMETER :: SP = KIND(1.0)
  INTEGER, PARAMETER :: DP = KIND(1.0D0)
  !   Symbolic names for kind types of single- and double-precision complex:
  INTEGER, PARAMETER :: SPC = KIND((1.0,1.0))
  INTEGER, PARAMETER :: DPC = KIND((1.0D0,1.0D0))
  !   Symbolic name for kind type of default logical:
  INTEGER, PARAMETER :: LGT = KIND(.true.)
END MODULE tktype

下面是一个shell脚本,用于编译上面的源代码并运行生成的可执行文件。

compile_run.sh

#!/bin/bash

ifort -v 
echo "compiling.."
ifort -o tktype.o -c -check -g -stand f03 tktype.f90
ifort -o strange.o -c -check -g -stand f03 strange.f90
ifort -o main_dbg.o -c -check -g -stand f03 main_dbg.f90
ifort -o baabaa strange.o tktype.o main_dbg.o
echo "..done"
echo "running.."
./baabaa
echo "..done"

标准输出如下所示。

ifort version 14.0.3
compiling..
..done
running..
 n1=        23
 n2=        32
 n3=       736
 entered constructor..
 size(this%arrB,1) =          23
 n1                =           23
 size(this%arrB,2) =          32
 n2                =           32
 size(this%arrB,3) =         736
 n3                =          736
./compile_run.sh: line 11: 17096 Segmentation fault      ./baabaa
..done

编辑 2016-01-30

我发现添加 ulimit -s unlimitedcompile_run.sh 的开头(在#/bin/bash 之后)防止分段错误。 fortran 中的可分配数组是存储在栈中而不是堆中吗?

【问题讨论】:

    标签: memory-management segmentation-fault fortran allocatable-array


    【解决方案1】:

    这可能是类似问题(Segmentation fault on 2D array) 的重复,其中一些多维forall 循环会导致问题。链接问题的OP在英特尔论坛(ifort v 14.0 / 15.0 "-g" option causes segFault)问了这个问题,最新回复如下:

    解决方法 #1 是增加堆栈大小限制。我成功使用了你的测试用例:ulimit -s unlimited

    解决方法 #2 是使用 DO 循环而不是 FORALL,如下所示:

    另外,根据 casey 在链接问题中的评论,ifort16 不会出现此问题,所以我猜这可能是 ifort14/15 特有的编译器问题。


    更多信息(只是一些实验):

    通过将堆栈大小限制为ulimit -s 4000 并使用ifort14.0.1 在我的计算机上重现了相同的问题,并且使用-heap-arrays 选项消失了。所以我最初认为可能有一些自动数组或数组临时大小为n1 * n2 * n3,但原始代码中似乎没有这样的东西......附加-assume realloc_lhs-check -warn也没有帮助。

    所以我做了一个测试程序,使用doforall 执行相同的计算:

    program main
        implicit none
        integer, parameter :: dp  = KIND(1.0D0)
        integer, parameter :: dpc = KIND((1.0D0,1.0D0))
        type Mytype
            real(dp),     allocatable :: A(:)
            complex(dpc), allocatable :: B(:,:,:)
        endtype
        type(Mytype) :: t
        integer :: n1, n2, n3, j1, j2, j3
    
        n1 = 23
        n2 = 32
        n3 = n1 * n2   !! = 736
    
        allocate( t% A( n2 ), t% B( n1, n2, n3 ) )
    
        t% A(:) = 1.0_dp
    
        print *, "[1] do (3-dim)"
        do j3 = 1, n3
        do j2 = 1, n2
        do j1 = 1, n1
            t% B( j1, j2, j3 ) = t% A( j2 ) 
        enddo
        enddo
        enddo
    
        print *, "[2] do (1-dim)"
        do j2 = 1, n2
            t% B( :, j2, : ) = t% A( j2 ) 
        enddo
    
        print *, "[3] forall (1-dim)"
        forall( j2 = 1:n2 )
            t% B( :, j2, : ) = t% A( j2 ) 
        end forall
    
        print *, "[4] forall (3-dim)"   ! <-- taken from the original code
        forall( j1 = 1:n1, j2 = 1:n2, j3 = 1:n3 )
            t% B( j1, j2, j3 ) = t% A( j2 )
        end forall
    
        print *, "all passed."
    end program
    

    其中模式 [4] 对应于 OP 使用的模式。限制堆栈大小并在没有选项的情况下编译 (ulimit -s 4000 ; ifort test.f90) 会给出输出

     [1] do (3-dim)
     [2] do (1-dim)
     [3] forall (1-dim)
     [4] forall (3-dim)
    Segmentation fault
    

    这意味着当 -heap-arrays 未附加时,只有模式 [4] 失败。奇怪的是,当数组 AB 在派生类型之外声明时,问题就消失了,即以下程序在没有选项的情况下工作。

    program main
        implicit none
        integer, parameter :: dp  = KIND(1.0D0)
        integer, parameter :: dpc = KIND((1.0D0,1.0D0))
        real(dp),     allocatable :: A(:)
        complex(dpc), allocatable :: B(:,:,:)
        integer :: n1, n2, n3, j1, j2, j3
    
        n1 = 23
        n2 = 32
        n3 = n1 * n2   !! = 736
    
        allocate( A( n2 ), B( n1, n2, n3 ) )
    
        A(:) = 1.0_dp
    
        print *, "[1] do (3-dim)"
        do j3 = 1, n3
        do j2 = 1, n2
        do j1 = 1, n1
            B( j1, j2, j3 ) = A( j2 ) 
        enddo
        enddo
        enddo
    
        print *, "[2] do (1-dim)"
        do j2 = 1, n2
            B( :, j2, : ) = A( j2 ) 
        enddo
    
        print *, "[3] forall (1-dim)"
        forall( j2 = 1:n2 )
            B( :, j2, : ) = A( j2 ) 
        end forall
    
        print *, "[4] forall (3-dim)"
        forall( j1 = 1:n1, j2 = 1:n2, j3 = 1:n3 )
            B( j1, j2, j3 ) = A( j2 )
        end forall
    
        print *, "all passed."
    end program
    

    所以问题似乎只发生在多维forall 循环的某些特定情况下(即使没有-g 选项),这可能是在堆栈上使用内部临时数组(尽管-check -warn 选项给出没有消息)。仅供参考,以上所有模式都适用于 gfortran 4.8/5.2 和 Oracle fortran 12.4。

    【讨论】:

    • warn 是否产生了有关已创建临时数组的警告?
    • 没有来自 -check -warn 等的消息...这可能与链接的问题相同。
    • @roygvib 感谢您尝试重现问题并尝试更多以及指向相关帖子。很高兴知道这是一个已知问题,已在较新版本的 ifort 中解决并已解决。我现在感觉更安全了。
    • @norio 嗨没问题,很高兴为您提供帮助!
    猜你喜欢
    • 1970-01-01
    • 2020-03-17
    • 2017-07-20
    • 1970-01-01
    • 2012-07-17
    • 2014-10-18
    • 1970-01-01
    • 1970-01-01
    • 2023-01-22
    相关资源
    最近更新 更多