fortran - MPI-IO: escribir subcampo
(1)
Estoy comenzando a usar MPI-IO e intenté escribir un ejemplo muy simple de las cosas que me gustaría hacer con él; Sin embargo, a pesar de que es un código simple y me inspiré en los ejemplos que leo aquí y allá, recibo una falla de segmentación que no entiendo.
La lógica de la pieza de código es muy simple: cada hilo manejará una matriz local que es parte de una matriz globlal que quiero escribir. Creo un tipo de MPI_Type_Create_Subarray
usando MPI_Type_Create_Subarray
para hacerlo. Luego solo abro el archivo, establezco una vista e intento escribir los datos. Obtengo el error de segmentación durante MPI_File_Write_All
.
Aquí está el código:
program test
implicit none
include "mpif.h"
integer :: myrank, nproc, fhandle, ierr
integer :: xpos, ypos
integer, parameter :: loc_x=10, loc_y=10
integer :: loc_dim
integer :: nx=2, ny=2
real(8), dimension(loc_x, loc_y) :: data
integer :: written_arr
integer, dimension(2) :: wa_size, wa_subsize, wa_start
call MPI_Init(ierr)
call MPI_Comm_Rank(MPI_COMM_WORLD, myrank, ierr)
call MPI_Comm_Size(MPI_COMM_WORLD, nproc, ierr)
xpos = mod(myrank, nx)
ypos = mod(myrank/nx, ny)
data = myrank
loc_dim = loc_x*loc_y
wa_size = (/ nx*loc_x, ny*loc_y /)
wa_subsize = (/ loc_x, loc_y /)
wa_start = (/ xpos, ypos /)*wa_subsize
call MPI_Type_Create_Subarray(2, wa_size, wa_subsize, wa_start &
, MPI_ORDER_FORTRAN, MPI_DOUBLE_PRECISION, written_arr, ierr)
call MPI_Type_Commit(written_arr, ierr)
call MPI_File_Open(MPI_COMM_WORLD, "file.dat" &
& , MPI_MODE_WRONLY + MPI_MODE_CREATE, MPI_INFO_NULL, fhandle, ierr)
call MPI_File_Set_View(fhandle, 0, MPI_DOUBLE_PRECISION, written_arr &
, "native", MPI_INFO_NULL, ierr)
call MPI_File_Write_All(fhandle, data, loc_dim, MPI_DOUBLE_PRECISION &
, MPI_INFO_NULL, ierr)
call MPI_File_Close(fhandle, ierr)
call MPI_Finalize(ierr)
end program test
¡Cualquier ayuda sería muy apreciada!
El último argumento para MPI_FILE_WRITE_ALL
antes del argumento de salida de error es un objeto de estado MPI y no un objeto de información MPI. Hacer la llamada con MPI_INFO_NULL
es, por lo tanto, erróneo. Si no está interesado en el estado de la operación de escritura, debe pasar MPI_STATUS_IGNORE
en MPI_STATUS_IGNORE
lugar. Hacer la llamada con MPI_INFO_NULL
podría funcionar en algunas implementaciones de MPI debido a los detalles de cómo se definen ambas constantes, pero luego fallan en otras.
Por ejemplo, en Open MPI, MPI_INFO_NULL
se declara como:
parameter (MPI_INFO_NULL=0)
Cuando se pasa en lugar de MPI_STATUS_IGNORE
provoca que se MPI_STATUS_IGNORE
la implementación C de MPI_File_write_all
con el argumento de estado apuntando a una ubicación de memoria constante (de solo lectura) que contiene el valor de MPI_INFO_NULL
(que es como Fortran implementa las constantes de paso por dirección). Cuando la función C está a punto de finalizar, intenta llenar el objeto de estado, lo que da como resultado un intento de escribir en la memoria constante y, finalmente, conduce a la falla de segmentación.
Al escribir nuevos programas Fortran, es aconsejable no utilizar la antigua interfaz mpif.h
, ya que no proporciona ninguna comprobación de errores. Más bien, uno debería usar el módulo mpi
o incluso mpi_f08
cuando más implementaciones de MPI se vuelven compatibles con MPI-3.0. El comienzo de su programa debería por lo tanto verse así:
program test
use mpi
implicit none
...
end program test
Una vez que utiliza el módulo mpi
lugar de mpif.h
, el compilador puede realizar la comprobación del tipo de parámetro para algunas llamadas MPI, incluido MPI_FILE_SET_VIEW
, y detectar un error:
test.f90(34): error #6285: There is no matching specific subroutine for this generic subroutine call. [MPI_FILE_SET_VIEW]
call MPI_File_Set_View(fhandle, 0, MPI_DOUBLE_PRECISION, written_arr &
-------^
compilation aborted for test.f90 (code 1)
La razón es que el segundo argumento para MPI_FILE_SET_VIEW
es de tipo INTEGER(KIND=MPI_OFFSET_KIND)
, que es de 64 bits en la mayoría de las plataformas modernas. La constante 0
simplemente es del tipo INTEGER
y, por lo tanto, es de 32 bits en la mayoría de las plataformas. Lo que sucede es que con mpif.h
el compilador pasa un puntero a una constante INTEGER
con valor de 0
, pero la subrutina lo interpreta como un puntero a un entero más grande e interpreta los valores vecinos como parte del valor constante. Por lo tanto, el cero que pasa como un desplazamiento dentro del archivo termina siendo un valor distinto de cero.
Reemplace el 0
en la llamada MPI_FILE_SET_VIEW
con 0_MPI_OFFSET_KIND
o declare una constante de tipo INTEGER(KIND=MPI_OFFSET_KIND)
y un valor de cero y luego páselo.
call MPI_File_Set_View(fhandle, 0_MPI_OFFSET_KIND, MPI_DOUBLE_PRECISION, ...
o
integer(kind=MPI_OFFSET_KIND), parameter :: zero_off = 0
...
call MPI_File_Set_View(fhandle, zero_off, MPI_DOUBLE_PRECISION, ...
Ambos métodos conducen a un archivo de salida de 3200 bytes de tamaño (como se esperaba).