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 リポジトリをまとめました。


概要

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。もう 1 つの値は address にあります00000003071E05A0

IIUC では、relocmon.inc でCOMMON /DDMON/次元配列を含むように設定しているznzndaavため、ソフトウェアが正常に機能していた場合、問題の値のアドレスは/DDMON/ブロック内にあります。のアドレス範囲は/DDMON/ですz'000000027FFF6040' - z'0000000307421150'。私のロジックが正しい場合、違反はこのブロックの外で発生します。

00000002071E05A0プログラムがを使用すべきときにに書き込もうとしているように私には思え00000003071E05A0ます。

なぜこれが当てはまるのかを判断するのを手伝ってくれる人はいますか? それには何か体系的なものがあるように見えます - それは単なる偶然でしょうか?

4

0 に答える 0