Ошибка отладки нарушения доступа: запись в 2071E05A0 вместо 3071E05A0
Окончательное редактирование:
Некоторые пользователи на форумах 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 дает>2 ГБ, превышая размер стека.
Решением было бы использовать кучу следующим образом (Фортан 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
Как только мы это сделаем, мы можем выделить более 2 ГБ, и массив будет работать нормально. Программе, из которой создан этот небольшой фрагмент кода, несколько лет, и мы только сейчас столкнулись с проблемой, потому что построенная нами модель во много раз больше, чем любая другая. Поскольку 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
NB функция 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]
Для всего остального... пожалуйста, потерпите меня. Я ни в коем случае не обученный инженер-программист или фортран-разработчик, поэтому я пытаюсь устранить неполадки в темноте.
Значение для ZNZNDAAV(1,337,337,1003)
2.241640, и это добавляется к ZNZNDAAV(1,337,1714,1003)
, Это соответствует регистру XMM11, как описано в выходных данных исключения. Это значение по адресу 000000029BA3BD60
, Другое значение по адресу 00000003071E05A0
,
IIUC, в relocmon.inc мы устанавливаем COMMON /DDMON/
содержать размерный массив znzndaav
Таким образом, если бы программное обеспечение работало номинально, адрес рассматриваемой стоимости был бы в пределах /DDMON/
блок. Диапазон адресов для /DDMON/
является z'000000027FFF6040' - z'0000000307421150'
, Если моя логика верна, нарушение происходит за пределами этого блока.
Мне кажется, что программа пытается написать 00000002071E05A0
когда это следует использовать 00000003071E05A0
,
Может ли кто-нибудь помочь мне определить, почему это так? Кажется, в этом есть что-то систематическое - может ли это быть простым совпадением?