arrays - salto - producto vectorial fortran
Asignación automática de matrices tras la asignación en Fortran (2)
Recientemente descubrimos que estábamos haciendo una asignación a una matriz no asignada en Fortran. El compilador GNU gfortran no detectó el error, y el código se ejecuta tanto en OSX como en Linux. Sin embargo, la misma segmentación de código falla en una PC IBM Power.
Mi pregunta es, ¿es correcto el siguiente código?
Parece que la matriz asignada a la
array
está asignando memoria automáticamente, en algunas arquitecturas, pero no en otras.
¿Hay detalles específicos de implementación en el trabajo aquí?
El código es un código mixto C / Fortran:
#include <stdlib.h>
void assign_array_(double x[], int* n);
void print_array_();
int main()
{
int n,i;
double *x;
n = 5;
x = (double*) malloc(sizeof(double)*n);
for (i = 0; i < n; i++)
x[i] = (double) i;
assign_array_(x,&n);
print_array_();
return 0;
}
Y el código Fortran:
MODULE test_mod
DOUBLE PRECISION, ALLOCATABLE, DIMENSION(:) :: array
integer :: nsize
END MODULE test_mod
SUBROUTINE assign_array(x,n)
USE test_mod
IMPLICIT NONE
INTEGER :: n
DOUBLE PRECISION :: x(n)
CALL test_allocated()
array = x
CALL test_allocated()
nsize = n
END SUBROUTINE assign_array
SUBROUTINE print_array()
USE test_mod, ONLY: nsize, array
IMPLICIT NONE
INTEGER :: i
DO i = 1,nsize
WRITE(6,''(F24.16)'') array(i)
END DO
END SUBROUTINE print_array
SUBROUTINE test_allocated()
USE test_mod
IMPLICIT NONE
IF (ALLOCATED(array)) THEN
WRITE(6,*) ''Array is allocated''
WRITE(6,*) ''size is '', SIZE(array)
ELSE
WRITE(6,*) ''Array is NOT allocated''
END IF
END SUBROUTINE test_allocated
La salida (cuando se ejecuta) es:
Array is NOT allocated
Array is allocated
size is 5
0.0000000000000000
1.0000000000000000
2.0000000000000000
3.0000000000000000
4.0000000000000000
Y aquí está la salida en el Power PC:
Array is NOT allocated
Segmentation fault (core dumped)
En resumen: se ejecuta cuando se compila bajo GNU (GNU Fortran (MacPorts gcc5 5.4.0_0) 5.4.0) gfortran en OSX (arch: x86_64h) y Linux (en una máquina virtual alojada en OSX, GNU Fortran (Ubuntu 4.9.4- 2ubuntu1 ~ 14.04.1) 4.9.4), pero no se ejecuta cuando se compila en Power PC (arch: ppc64) compilado con GNU Fortran (GCC) 4.4.7 20120313 (Red Hat 4.4.7-17). En nuestro código original, la implementación de Power PC solo se produjo mucho más tarde en el código, donde se hizo referencia a la matriz de entradas asignadas, lo que hace que nuestro ''error'' (si de hecho es un error) sea realmente difícil de rastrear.
¿Cuál es el comportamiento correcto para el código anterior?
La validez del código como
integer, allocatable :: array(:)
array = (/1,2,3/)
end
depende del estándar de Fortran utilizado para interpretarlo.
Fortran 2003 introdujo el concepto de asignación automática en la asignación intrínseca. Antes de Fortran 2003, la matriz en el lado izquierdo de dicha declaración de asignación debe asignarse y tener la misma forma que la matriz en el lado derecho.
Desde Fortran 2003 solo el rango necesita ser igualado. Si hay una falta de coincidencia de forma, la matriz primero se desasignará y luego se reasignará a la forma correcta. Si no se asigna inicialmente, se asignaría.
Entonces, el programa anterior no es válido para Fortran 90, pero es válido para Fortran 2003.
La diferencia en el código del mundo real, entonces, es la sintaxis de idioma que admiten los compiladores.
Para gfortran, la asignación de Fortran 2003 a una matriz asignable se introdujo en 4.6, 2011-01-28 .
Como también se comentó, la opción de línea de comando
-fno-realloc-lhs
1
deshabilita esta (re) asignación automática, haciendo que el compilador no sea compatible con Fortran 2003+.
1 Otros compiladores tienen un comportamiento similar: agregar una verificación requerida para determinar si la reasignación es necesaria es un problema de rendimiento que es redundante en el código compatible con Fortran 90 y puede ser una característica que muchos no usan incluso en el código moderno. Para el compilador de Intel , por ejemplo, en algunas versiones que admiten F2003, el valor predeterminado es ignorar esto.
Siempre se puede suprimir las comprobaciones / acciones de (re) asignación de una matriz en código moderno mediante el uso de una sección de matriz
array(:) = (/1,2,3/)
En este caso, la
array
(si se puede asignar) debe asignarse, de rango 1 y de tamaño 3 para que la instrucción de asignación sea válida.
Esto es como sería bajo una interpretación Fortran 90 de la asignación con toda la matriz de
array=(/1,2,3/)
.
La razón de esto es que con la sección de matriz de esta nota al pie, el lado izquierdo no es asignable, aunque la matriz sí lo es.
Resulta que solo GNU gfortran 4.6 y superior permiten la reasignación automática de matrices LHS en F90.
El uso del indicador del compilador
-fno-realloc-lhs
deshabilita esta característica y desencadena seg.
fallas en todos los casos que describí anteriormente (OSX, Linux, PPC) ¡Misterio resuelto!
Gracias al cartel de misterio cuyo comentario desapareció misteriosamente.
Ver GCC 4.6 Wiki