0

最终编辑:

silverfrost 论坛上的一些用户非常有帮助地指导我简化代码和解决方案。

可以使用以下代码复制该问题:

      PROGRAM ML14ERROR   
        INTEGER :: origzn, destzn
        INTEGER,PARAMETER :: MXZMA = 1713, LXTZN = 1714, MXAV = 182
        INTEGER,PARAMETER :: JTMPREL = 1003, av = 1
        REAL(KIND=2) :: RANDOM@
        REAL,dimension (1:mxav,lxtzn,lxtzn,JTMPREL:JTMPREL):: znzndaav

        DO origzn=1,lxtzn
          DO destzn=1,lxtzn
            znzndaav(av,origzn,destzn,JTMPREL) = RANDOM@()
          END DO
        END DO

        DO origzn=1,mxzma
          DO destzn=1,mxzma
            ! This is where the error occurs
            znzndaav(av,origzn,lxtzn,JTMPREL)=
     $         znzndaav(av,origzn,lxtzn,JTMPREL)+
     $         znzndaav(av,origzn,destzn,JTMPREL)

          ENDDO
        ENDDO

        WRITE(6,*)'No errors'
      END PROGRAM 

该问题仅在MXAV>182 时出现,这表明存在内存问题。实际上,将尺寸相乘:183 * 1714 * 1714 * 4 会产生 >2GB,超过堆栈大小。

一种解决方案是按如下方式使用堆(Fortan 95):

PROGRAM ML14ERROR    
  INTEGER :: origzn, destzn
  INTEGER,PARAMETER :: MXZMA = 1713, LXTZN = 1714, MXAV = 191
  INTEGER,PARAMETER :: JTMPREL = 1003, av = 1
  REAL(KIND=2) :: RANDOM@
  REAL,allocatable :: znzndaav(:,:,:,:)

  ALLOCATE( znzndaav(1:mxav,lxtzn,lxtzn,JTMPREL:JTMPREL) )
  DO origzn=1,lxtzn
    DO destzn=1,lxtzn
      znzndaav(av,origzn,destzn,JTMPREL) = RANDOM@()
    END DO
  END DO

  DO origzn=1,mxzma
    DO destzn=1,mxzma
      ! This is where the error occurs
      znzndaav(av,origzn,lxtzn,JTMPREL)= &
  &         znzndaav(av,origzn,lxtzn,JTMPREL)+ &
  &         znzndaav(av,origzn,destzn,JTMPREL)

    ENDDO
  ENDDO
  DEALLOCATE(znzndaav)
  
  WRITE(6,*)'No errors'
END PROGRAM

完成此操作后,我们可以分配超过 2GB 的空间,并且数组可以正常工作。这段代码源自的程序已经有几年历史了,我们现在才遇到这个问题,因为我们构建的模型比以前大很多倍。由于 Fortran 77 不允许 ALLOCATABLE 数组,我们必须要么减少堆栈使用,要么移植代码 - 或寻求另一种优化。


编辑添加:

我现在整理了一个包含可重现代码的 git repo


概述

我有一个程序在编译为 32 位时运行良好,但在编译并以 64 位运行时出现访问冲突错误。

我正在使用 Silverfrost Fortran 编译器 FTN95 v8.51,尽管使用 v8.40 和 v8.50 会出现此问题。


示例代码

! .\relocmon.inc
      INTEGER JTMPREL
      PARAMETER(JTMPREL=1003)
      REAL znda(lxtzn,JTMPREL:JTMPREL)
      REAL zndaav(1:mxav,lxtzn,JTMPREL:JTMPREL)      
      REAL,dimension (lxtzn,lxtzn,JTMPREL:JTMPREL) :: znznda
      REAL mlrlsum(lxtzn,lxtzn)

      REAL,dimension (1:mxav,lxtzn,lxtzn,JTMPREL:JTMPREL):: znzndaav

      COMMON /DDMON/ znda, znznda, mlrlsum,znzndaav, zndaav
! EOF .\relocmon.inc

! .\relocmon.inc with values
      INTEGER JTMPREL
      PARAMETER(JTMPREL=1003)
      REAL znda(1714,JTMPREL:JTMPREL)
      REAL zndaav(1:191,1714,JTMPREL:JTMPREL)      
      REAL,dimension (1714,1714,JTMPREL:JTMPREL) :: znznda
      REAL mlrlsum(1714,1714)

      REAL,dimension (1:191,1714,1714,JTMPREL:JTMPREL):: znzndaav

      COMMON /DDMON/ znda, znznda, mlrlsum,znzndaav, zndaav
! EOF .\relocmon.inc

! .\main.for
        INCLUDE 'relocmon.inc'
        
        REAL,save,dimension(lxtzn,lxtzn,mxav) :: ddfuncval
        
        DO origzn=1,mxzma
          IF( zonedef(origzn,JZUSE) )THEN
            DO destzn=1,mxzma
              IF (zonedef(destzn,JZUSE)) THEN
                znznda(origzn,destzn,JTMPREL)=znda(destzn,JTMPREL)*
     $                                       ddfuncval(origzn,destzn,av)            

               znznda(origzn,lxtzn,JTMPREL)=znznda(origzn,lxtzn,JTMPREL)
     $               +znznda(origzn,destzn,JTMPREL)
     
         znzndaav(av,origzn,destzn,JTMPREL)=zndaav(av,destzn,JTMPREL)*
     $                                    ddfuncval(origzn,destzn,av)           

         ! LINE 309 -- where error occurs
         znzndaav(av,origzn,lxtzn,JTMPREL)=
     $               znzndaav(av,origzn,lxtzn,JTMPREL)
     $             +znzndaav(av,origzn,destzn,JTMPREL)
     
              ENDIF
            ENDDO
          ENDIF
        ENDDO

! EOF .\main.for

注意该函数zonedef只是检查一个区域对于我们想要进行的计算是否有效。此函数返回一个logical.


调试

正如我最初提到的,这个程序的 32 位编译版本运行良好。尝试运行 64 位版本时,第一个循环的输出如下:

来自 sdbg64.exe:

Error: Access Violation reading address
0x00000002071E05A0

main.for: 309

将异常写入文件:

Access violation (c0000005) at address 43a1f4

Within file ml14.exe
in main in line 309, at address 2b84

RAX = 0000000000000001   RBX = 000000027fff704c   RCX = 000000000285e6b8   RDX = 00000002802296cc
RBP = 0000000000400000   RSI = 000000029ba3ad6c   RDI = 0000000307695374   RSP = 000000000285be70
R8  = 0000000307695374   R9  = 00000002ffff5040   R10 = 000000029ba3ad6c   R11 = 000000030731f0dc
R12 = 000000027fff5584   R13 = 00000002802296cc   R14 = 000000028169f3ec   R15 = 0000000281660928

43a1f4) addss       XMM11,[85b401b4++R14]

剩下的……请多多包涵。无论如何,我都不是受过训练的软件工程师或 fortran 开发人员,所以我在黑暗中刺了一下以进行故障排除。

的值为ZNZNDAAV(1,337,337,1003)2.241640,并且正在添加到ZNZNDAAV(1,337,1714,1003). 这与异常输出中详述的寄存器 XMM11 相符。该值位于 address 000000029BA3BD60。另一个值在 address 00000003071E05A0

IIUC,在 relocmon.inc 中,我们设置COMMON /DDMON/为包含维度数组znzndaav,因此如果软件名义上工作,则相关值的地址将在/DDMON/块内。的地址范围/DDMON/z'000000027FFF6040' - z'0000000307421150'。如果我的逻辑是正确的,则违规发生此块之外。

在我看来,该程序正在尝试写入00000002071E05A0它应该使用的时间00000003071E05A0

谁能帮我确定为什么会这样?似乎有一些系统性的东西——这仅仅是巧合吗?

4

0 回答 0