【问题标题】:Fortran subroutine forgets allocatable array element value after many iterationsFortran 子例程在多次迭代后忘记了可分配的数组元素值
【发布时间】:2019-09-12 03:39:04
【问题描述】:

我有一个结构如下的 fortran 程序。该程序因“分段错误”错误而崩溃,因此我对根本原因进行了很多挖掘。

原来,在子程序SUB1中,当DO循环计数器变量I达到187606时,原来从外部文件中读取为28.29的Z(608673)的值,莫名其妙莫名其妙 更改为 3.9809702045652999E-309,即使在从外部文件读取后没有任何计算会更改任何 X、Y 或 Z 数组的值。

我怀疑许多其他数组元素值也被程序在执行的中途设置为任意数字(可能大约为零)-尽管鉴于数组的大小,不可能显式检查这一点-但在本质上,这就是导致程序崩溃的原因(这些值在程序的其他地方使用)。

所以主要问题是:为什么数组元素值被程序任意重置,尽管这些数组保存在 MODULE VARIABLES 中并且该模块被所有相关例程使用? SAVE 语句不是用来保存数组的值吗?

注意:我尝试在 [a] 具有 16 GB RAM 的 Ubuntu Linux Dell 笔记本电脑(没有运行其他需要大量内存的程序)和 [b] 具有 16 GB RAM 的 Windows 笔记本电脑上运行此代码。在这两台机器上,我都使用了 gfortran 编译器,并且出现了同样的问题。对于较小的 N 值,代码似乎运行良好 - 只有非常大的数组才有问题。

文件 vars.f90

MODULE VARIABLES

   ......
   ......
   INTEGER :: N
   REAL(8), DIMENSION(:), ALLOCATABLE :: X,Y,Z

   SAVE N
   SAVE X,Y,Z
   .....
   .....
   ... (several other variable declarations) .... 

END MODULE VARIABLES

文件分配.f90

MODULE ALLOCATE_ARRAYS

CONTAINS

SUBROUTINE ALLOC_ARR
   USE VARIABLES
   USE DEALLOCATE_ARRAYS

   .....

   ALLOCATE(X(N),Y(N),Z(N), STAT=IALLOC)
   IF (IALLOC /= 0) THEN
      WRITE(6,*)' ERROR while allocating arrays X Y Z'
      CALL DEALLOC_ARRAYS
      STOP
   ENDIF

   .....
END SUBROUTINE ALLOC_ARR

END MODULE ALLOCATE_ARRAYS

文件 deallocate.f90

MODULE DEALLOCATE_ARRAYS

CONTAINS

SUBROUTINE DEALLOC_ARR
   USE VARIABLES
   .....

   IF (ALLOCATED(X)) DEALLOCATE(X)
   IF (ALLOCATED(Y)) DEALLOCATE(Y)
   IF (ALLOCATED(X)) DEALLOCATE(X)

   ...
END SUBROUTINE DEALLOC_ARR

END MODULE DEALLOCATE_ARRAYS

文件 main.f90

PROGRAM PROG1

   USE VARIABLES
   USE ALLOCATE_ARRAYS
   USE DEALLOCATE_ARRAYS

   .....

   N = 18666800      ![in practice this is read from an external input file]
   CALL ALLOC_ARR    !Allocate X,Y,Z each to be of length N

   !Read all values of X(1:N), Y(1:N) and Z(1:N) from an external file] ...
   OPEN(11,FILE=datafile.txt,STATUS='OLD')

   DO I = 1,N
      READ(11,*,IOSTAT=ISTAT) X,Y,Z
         IF ( ISTAT /= 0 ) THEN
            WRITE(6,*)'   *** SERIOUS WARNING ***'
            WRITE(6,*)'   Something went wrong while trying to read numbers X Y Z at I = ',I
            READ(5,*)
         ENDIF
   ENDDO

   CLOSE(11)

   CALL SUB1

   .....
   CALL DEALLOC_ARRAYS

END PROGRAM PROG1


SUBROUTINE SUB1

   USE VARIABLES
   USE ALLOCATE_ARRAYS
   USE DEALLOCATE_ARRAYS

   .....

   DO I = 1,N
      write(6,*)' X(608673)  Y(608673)  Z(608673) = ',X(608673),Y(608673),Z(608673)  ![this statement only inserted for tracing the bug]
      CALL SUB2(I,X(I),Y(I),Z(I))
   ENDDO

END SUBROUTINE SUB1


SUBROUTINE SUB2(IPASS,XPASS,YPASS,ZPASS)

   USE VARIABLES
   USE ALLOCATE_ARRAYS
   USE DEALLOCATE_ARRAYS

   [carry out some calculations that use the values of IPASS, XPASS, YPASS, ZPASS, but never change their values]

END SUBROUTINE SUB2

文件datafile.txt(小样本):

.....
.....
10879.544935    1200.249974       28.290163
10914.193168     205.374638      236.847393
23872.837623    3634.498293    23721.923293
.....
.....

【问题讨论】:

  • 我经常遇到(旧的)Fortran 程序和大尺寸的分段错误,因为某处总是有一个堆栈分配的数组。在这种情况下(在 Linux 中),使用 ulimit -s unlimited 来避免对堆栈大小的限制会有所帮助。此外,使用 Valgrind 运行您的程序可能会指出一些事情。
  • @jacob 谢谢,但不幸的是,这并没有解决它。

标签: fortran save allocatable-array


【解决方案1】:

如果大数字溢出,那么可能类似于以下内容:

USE ISO_C_BINDING
INTEGER(KIND=C_Int32_t) :: N

或者首先添加一些调试语句,以确保您传递的数字实际上按预期接收。

如果 32 位不够,那么 C_Int64_t 可以容纳更大的数字。

REAL(8) 也可以使用 ISO_C_BINDING 和 REAL(KIND=C_DOUBLE) 一样,而 ISO_C_BINDING 是定义大小的好方法。

【讨论】:

  • 谢谢。我尝试了C_Int32_tC_Int64_t,但遗憾的是,他们都没有解决它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-05
  • 2020-09-22
  • 2012-10-15
  • 2013-08-22
  • 1970-01-01
  • 2017-07-19
  • 2013-05-28
相关资源
最近更新 更多