fortran - Escribir varias matrices distribuidas con MPI IO
fortran90 mpi-io (1)
Estoy reescribiendo un código de simulación numérica que se paraleliza utilizando MPI en una dirección. Hasta ahora, las matrices que contienen los datos fueron guardadas por el proceso maestro MPI, que implicó transferir los datos de todos los procesos MPI a uno y asignar enormes matrices para almacenar todo. No es muy eficiente ni elegante, y es un problema para resoluciones grandes.
Por lo tanto, estoy tratando de usar MPI-IO para escribir directamente el archivo de las matrices distribuidas. Una de las limitaciones que tengo es que el archivo escrito debe respetar el formato fortran "no formateado" (es decir, un entero de 4 bytes antes y después de cada campo que indique su tamaño).
Escribí un programa de prueba simple, que funciona cuando escribo solo una matriz distribuida en el archivo. Sin embargo, cuando escribo varias matrices, el tamaño total del archivo es incorrecto y cuando se compara con el archivo fortran ''no formateado'' equivalente, los archivos son diferentes.
Aquí está el código de ejemplo :
module arrays_dim
implicit none
INTEGER, PARAMETER :: dp = kind(0.d0)
integer, parameter :: imax = 500
integer, parameter :: jmax = 50
integer, parameter :: kmax = 10
end module arrays_dim
module mpi_vars
use mpi
implicit none
integer, save :: ierr, myID, numprocs
integer, save :: i_start, i_end, i_mean, i_loc
integer, save :: subArray, fileH
integer(MPI_OFFSET_KIND), save :: offset, currPos
end module mpi_vars
program test
use mpi
use arrays_dim
use mpi_vars
real(dp), dimension(0:imax,0:jmax+1,0:kmax+1) :: v, w
real(dp), dimension(:,:,:), allocatable :: v_loc, w_loc
integer :: i, j, k
call MPI_INIT(ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, myID, ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr)
i_mean = (imax+1)/numprocs
i_start = myID*i_mean
i_end = i_start+i_mean-1
if(i_mean*numprocs<imax+1) then
if(myID == numprocs-1) i_end = imax
endif
i_loc = i_end - i_start + 1
allocate(v_loc(i_start:i_end,0:jmax+1,0:kmax+1))
allocate(w_loc(i_start:i_end,0:jmax+1,0:kmax+1))
print*, ''I am:'', myID, i_start, i_end, i_loc
do k=0,kmax+1
do j=0,jmax+1
do i=0,imax
v(i,j,k) = i+j+k
w(i,j,k) = i*j*k
enddo
enddo
enddo
if(myID==0) then
open(10,form=''unformatted'')
write(10) v
!write(10) w
close(10)
endif
do k=0,kmax+1
do j=0,jmax+1
do i=i_start,i_end
v_loc(i,j,k) = i+j+k
w_loc(i,j,k) = i*j*k
enddo
enddo
enddo
call MPI_Type_create_subarray (3, [imax+1, jmax+2, kmax+2], [i_loc, jmax+2, kmax+2], &
[i_start, 0, 0], &
MPI_ORDER_FORTRAN, MPI_DOUBLE_PRECISION, subArray, ierr)
call MPI_Type_commit(subArray, ierr)
call MPI_File_open(MPI_COMM_WORLD, ''mpi.dat'', &
MPI_MODE_WRONLY + MPI_MODE_CREATE + MPI_MODE_APPEND, &
MPI_INFO_NULL, fileH, ierr )
call saveMPI(v_loc, (i_loc)*(jmax+2)*(kmax+2))
!call saveMPI(w_loc, (i_loc)*(jmax+2)*(kmax+2))
call MPI_File_close(fileH, ierr)
deallocate(v_loc,w_loc)
call MPI_FINALIZE(ierr)
end program test
!
subroutine saveMPI(array, n)
use mpi
use arrays_dim
use mpi_vars
implicit none
real(dp), dimension(n) :: array
integer :: n
offset = (imax+1)*(jmax+2)*(kmax+2)*8
if(myID==0) then
call MPI_File_seek(fileH, int(0,MPI_OFFSET_KIND), MPI_SEEK_CUR, ierr)
call MPI_File_write(fileH, [(imax+1)*(jmax+2)*(kmax+2)*8], 1, MPI_INTEGER, MPI_STATUS_IGNORE, ierr)
call MPI_File_seek(fileH, offset, MPI_SEEK_CUR, ierr)
call MPI_File_write(fileH, [(imax+1)*(jmax+2)*(kmax+2)*8], 1, MPI_INTEGER, MPI_STATUS_IGNORE, ierr)
endif
call MPI_File_set_view(fileH, int(4,MPI_OFFSET_KIND), MPI_DOUBLE_PRECISION, subArray, ''native'', MPI_INFO_NULL, ierr)
call MPI_File_write_all(fileH, array, (i_loc)*(jmax+2)*(kmax+2), MPI_DOUBLE_PRECISION, MPI_STATUS_IGNORE, ierr)
end subroutine saveMPI
cuando se !write(10) w
las líneas !write(10) w
!call saveMPI(w_loc, (i_loc)*(jmax+2)*(kmax+2))
(es decir, solo escribo la matriz v), el código funciona bien:
mpif90.openmpi -O3 -o prog main.f90
mpirun.openmpi -np 4 ./prog
cmp mpi.dat fort.10
cmp no genera una salida, por lo que los archivos son idénticos. Sin embargo, si descomenta estas líneas, los archivos resultantes (mpi.dat y fort.10) son diferentes. Estoy seguro de que el problema radica en la forma en que defino el desplazamiento que utilizo para escribir los datos en la posición correcta en el archivo, pero no sé cómo indicar a la segunda llamada de saveMPI que la posición inicial debería ser el final del archivo. Qué me estoy perdiendo ?
Solo la primera llamada a saveMPI
está funcionando como esperabas. Todo se estropea desde la segunda llamada. Aquí hay algunas indicaciones de lo que está sucediendo:
-
MPI_File_set_view
restablece los punteros de archivo independientes y el puntero de archivo compartido a cero. Ver MPI_File_set_view para más detalles. Así que en realidad está sobrescribiendo datosv
con datosw
cuando llama aMPI_File_set_view
ensaveMPI
. - con
MPI_File_write
, los datos se escriben en las partes del archivo especificadas por la vista actual. Esto significa que la forma en que agrega la información de tamaño en el archivo no es realmente compatible con la vista previamente configurada parav
. - llamando a
MPI_File_seek
conMPI_SEEK_CUR
establece la posición relativa a la posición actual del puntero individual. Entonces, para la segunda llamada, es relativa al puntero individual del proceso0
No uso tanto el IO paralelo, así que no puedo evitarlo a menos que entre en los documentos, a los que no tengo tiempo. La pista que puedo dar es:
- agregue un parámetro adicional a
saveMPI
que contendrá el desplazamiento absoluto de los datos para escribir; esto puede ser un[in out]
arg. Para la primera llamada, será cero y para llamadas posteriores, será el tamaño de todos los datos ya escritos en el archivo, incluida la información de tamaño. Se puede actualizar ensaveMPI
. - antes de escribir la información de tamaño (por proceso 0), llame a
MPI_File_set_view
para restablecer la vista a la transmisión lineal de bytes como originalmente fue dada porMPI_File_open
. Esto se puede hacer estableciendo eletype
filetype
y elfiletype
defiletype
enMPI_BYTE
al llamar aMPI_File_set_view
. mira en el documento deMPI_File_open
para más información. A continuación, tendrá que realizar llamadas aMPI_File_set_view
ensaveMPI
.
Su subrutina saveMPI
podría verse como
subroutine saveMPI(array, n, disp)
use mpi
use arrays_dim
use mpi_vars
implicit none
real(dp), dimension(n) :: array
integer :: n, disp
offset = (imax+1)*(jmax+2)*(kmax+2)*8
call MPI_File_set_view(fileH, int(disp,MPI_OFFSET_KIND), MPI_BYTE, MPI_BYTE, ''native'', MPI_INFO_NULL, ierr)
if(myID==0) then
call MPI_File_seek(fileH, int(0,MPI_OFFSET_KIND), MPI_SEEK_END, ierr)
call MPI_File_write(fileH, [(imax+1)*(jmax+2)*(kmax+2)*8], 1, MPI_INTEGER, MPI_STATUS_IGNORE, ierr)
call MPI_File_seek(fileH, int(offset,MPI_OFFSET_KIND), MPI_SEEK_CUR, ierr)
call MPI_File_write(fileH, [(imax+1)*(jmax+2)*(kmax+2)*8], 1, MPI_INTEGER, MPI_STATUS_IGNORE, ierr)
endif
call MPI_File_set_view(fileH, int(disp+4,MPI_OFFSET_KIND), MPI_DOUBLE_PRECISION, subArray, ''native'', MPI_INFO_NULL, ierr)
call MPI_File_write_all(fileH, array, (i_loc)*(jmax+2)*(kmax+2), MPI_DOUBLE_PRECISION, MPI_STATUS_IGNORE, ierr)
disp = disp+offset+8
end subroutine saveMPI
y llamado como:
disp = 0
call saveMPI(v_loc, (i_loc)*(jmax+2)*(kmax+2), disp)
call saveMPI(w_loc, (i_loc)*(jmax+2)*(kmax+2), disp)
Finalmente, asegúrese de eliminar el archivo entre dos llamadas porque está usando MPI_MODE_APPEND
.