programacion - fortran significado
Lectura escribiendo fortran acceso directo archivos no formateados con diferentes compiladores (1)
Ifort y gfortran no usan el mismo tamaño de bloque para la longitud de registro por defecto. En ifort, el valor de recl
en su instrucción open
está en bloques de 4 bytes, por lo que la longitud de su registro no es de 985.600 bytes, sino de 3.942.400 bytes. Eso significa que los registros se escriben a intervalos de 3.9 millones de bytes.
gfortran usa un tamaño de bloque recl
de 1 byte y su longitud de registro es 985,600 byes. Cuando lee el primer registro, todo funciona, pero cuando lee el segundo registro observa 985,600 bytes en el archivo, pero los datos están en 3,942,400 bytes en el archivo. Esto también significa que está desperdiciando una tonelada de datos en el archivo, ya que solo está usando 1/4 de su tamaño.
Hay un par de formas de solucionar esto:
- En ifort, especifique recl en bloques de 4 bytes, p
320*385*2
Ej.320*385*2
lugar de*8
- En ifort, utilice el indicador de compilación
-assume byterecl
para tener valoresrecl
interpretados como bytes. - En gfortran, compense el tamaño y use
recl=320*385*32
para que sus lecturas estén colocadas correctamente.
Sin embargo, una mejor manera es diseñar agnosticismo en el tamaño de la unidad recl
. Puede usar inquire
para descubrir el recl de una matriz. Por ejemplo:
real(kind=wp), allocatable, dimension(:,:) :: recltest
integer :: reclen
allocate(recltest(320,385))
inquire(iolength=reclen) recltest
deallocate(recltest)
...
open (53, file=filename, form=''unformatted'', status=''unknown'',
& access=''direct'',action=''write'',recl=reclen)
...
OPEN(53, FILE=fname, form="unformatted", status="unknown", &
access="direct", action="read", recl=reclen)
Esto configurará reclen
al valor necesario para almacenar una matriz de 320x385
basada en la unidad base de ese compilador para la longitud de registro. Si usa esto cuando tanto la escritura como la lectura de su código funcionarán con ambos compiladores sin tener que usar indicadores de tiempo de compilación en ifort
o compensar con diferencias recl codificadas entre compiladores.
Un ejemplo ilustrativo
Testcase 1
program test
use iso_fortran_env
implicit none
integer(kind=int64), dimension(5) :: array
integer :: io_output, reclen, i
reclen = 5*8 ! 5 elements of 8 byte integers.
open(newunit=io_output, file=''output'', form=''unformatted'', status=''new'', &
access=''direct'', action=''write'', recl=reclen)
array = [(i,i=1,5)]
write (io_output, rec=1) array
array = [(i,i=101,105)]
write (io_output, rec=2) array
array = [(i,i=1001,1005)]
write (io_output, rec=3) array
close(io_output)
end program test
Este programa escribe una matriz de 5 enteros de 8 bytes 3 veces en el archivo en los registros 1, 2 y 3. La matriz tiene 5 * 8 bytes y he codificado ese número como el valor recl.
Testcase 1 con gfortran 5.2
Recopilé este caso de prueba con la línea de comando:
gfortran -o write-gfortran write.f90
Esto produce el archivo de salida (interpretado con od -A d -t d8
):
0000000 1 2
0000016 3 4
0000032 5 101
0000048 102 103
0000064 104 105
0000080 1001 1002
0000096 1003 1004
0000112 1005
0000120
Las matrices de elementos de 5 8 bye están empaquetadas contiguamente en el archivo y el número de registro 2 ( 101 ... 105
) comienza donde esperaríamos que esté en offset 40, que es el valor recl en el archivo 5*8
.
Testcase 1 con ifort 16
Esto se compila de manera similar:
ifort -o write-ifort write.f90
Y esto, para el mismo código exacto, produce el archivo de salida (interpretado con od -A d -t d8
):
0000000 1 2
0000016 3 4
0000032 5 0
0000048 0 0
*
0000160 101 102
0000176 103 104
0000192 105 0
0000208 0 0
*
0000320 1001 1002
0000336 1003 1004
0000352 1005 0
0000368 0 0
*
0000480
Los datos están todos allí, pero el archivo está lleno de 0 elementos valiosos. Las líneas que comienzan con *
indican que cada línea entre los desplazamientos es 0. El número de registro 2 comienza en el desplazamiento 160 en lugar de 40. Observe que 160 es 40 * 4, donde 40 es nuestro recl especificado de 5*8
. Por defecto, ifort usa bloques de 4 bytes, por lo que un recl de 40 significa un tamaño de registro físico de 160 bytes.
Si el código compilado con gfortran fuera a leer esto, los registros 2,3 y 4 contendrían los 0 elementos y una lectura del registro 5 leería correctamente la matriz escrita como registro 2 por ifort. Una alternativa para que gfortran lea el registro 2 donde se encuentra en el archivo sería usar recl=160
(4 * 5 * 4) para que el tamaño del registro físico coincida con lo que escribió ifort.
Otra consecuencia de esto es el espacio desperdiciado. Si especifica demasiado el recl, significa que está usando 4 veces el espacio de disco necesario para almacenar sus registros.
Testcase 1 con ifort 16 y -assume byterecl
Esto fue compilado como:
ifort -assume byterecl -o write-ifort write.f90
Y produce el archivo de salida:
0000000 1 2
0000016 3 4
0000032 5 101
0000048 102 103
0000064 104 105
0000080 1001 1002
0000096 1003 1004
0000112 1005
0000120
Esto produce el archivo como se esperaba. El argumento de la línea de comando -assume byterecl
le dice a ifort que interprete cualquier valor recl
como bytes en lugar de palabras dobles (bloques de 4 bytes). Esto producirá escrituras y lecturas que coinciden con el código compilado con gfortran.
Testcase 2
program test
use iso_fortran_env
implicit none
integer(kind=int64), dimension(5) :: array
integer :: io_output, reclen, i
inquire(iolength=reclen) array
print *,''Using recl='',reclen
open(newunit=io_output, file=''output'', form=''unformatted'', status=''new'', &
access=''direct'', action=''write'', recl=reclen)
array = [(i,i=1,5)]
write (io_output, rec=1) array
array = [(i,i=101,105)]
write (io_output, rec=2) array
array = [(i,i=1001,1005)]
write (io_output, rec=3) array
close(io_output)
end program test
La única diferencia en este caso de prueba es que estoy investigando el recl apropiado para representar mi arreglo de 40 bytes (5 enteros de 8 bytes).
La salida
gfortran 5.2:
Using recl= 40
ifort 16, sin opciones:
Using recl= 10
ifort 16, -assume byterecl
:
Using recl= 40
Vemos que para los bloques de 1 byte utilizados por gfortran e ifort con la suposición byterecl de que recl es 40
, lo que equivale a nuestra matriz de 40 bytes. También vemos que, de forma predeterminada, ifort usa un recl de 10, lo que significa 10 bloques de 4 bytes o 10 palabras dobles, los cuales significan 40 bytes. Las tres cajas de prueba producen una salida de archivo idéntica y las lecturas / escrituras de cualquiera de los compiladores funcionarán correctamente.
Resumen
Para que los datos directos no formateados y basados en registros sean portátiles entre ifort y gfort, la opción más fácil es simplemente agregar -assume byterecl
a los indicadores utilizados por ifort. Deberías haber hecho esto ya que estás especificando longitudes de registro en bytes, por lo que este sería un cambio directo que probablemente no tenga consecuencias para ti.
La otra alternativa es no preocuparse por la opción y utilizar la inquire
intrínseca para consultar la iolength
de su matriz.
Tengo una sección en un programa que escribe un archivo binario de acceso directo de la siguiente manera:
open (53, file=filename, form=''unformatted'', status=''unknown'',
& access=''direct'',action=''write'',recl=320*385*8)
write (53,rec=1) ulat
write (53,rec=2) ulng
close(53)
Este programa está compilado con ifort. Sin embargo, no puedo reconstruir los datos correctamente si leo el archivo de datos de un programa diferente compilado con gfortran. Si el programa que lee los datos también se compila en ifort, entonces puedo reconstruir correctamente los datos. Aquí está el código que lee el archivo de datos:
OPEN(53, FILE=fname, form="unformatted", status="unknown", access="direct", action="read", recl=320*385*8)
READ(53,REC=2) DAT
No entiendo por qué está pasando esto? Puedo leer el primer registro correctamente con ambos compiladores, es el segundo registro que no puedo reconstruir correctamente si mezclo los compiladores.