MPI коллективный вывод 5 несмежных 3D массивов в специальной форме
В ходе выполнения курсовой работы мне нужно написать программу MPI для решения механики континуума PDE. (FORTRAN)
В программном файле последовательности написано следующее:
do i=1,XX
do j=1,YY
do k=1,ZZ
write(ifile) R(i,j,k)
write(ifile) U(i,j,k)
write(ifile) V(i,j,k)
write(ifile) W(i,j,k)
write(ifile) P(i,j,k)
end do
end do
end do
В параллельной программе я пишу так же, как показано ниже: / распараллеливание происходит только вдоль оси X /
call MPI_TYPE_CREATE_SUBARRAY(4, [INT(5), INT(ZZ),INT(YY), INT(XX)], [5,ZZ,YY,PDB(iam).Xelements], [0, 0, 0, PDB(iam).Xoffset], MPI_ORDER_FORTRAN, MPI_FLOAT, slice, ierr)
call MPI_TYPE_COMMIT(slice, ierr)
call MPI_FILE_OPEN(MPI_COMM_WORLD, cFileName, IOR(MPI_MODE_CREATE, MPI_MODE_WRONLY), MPI_INFO_NULL, ifile, ierr)
do i = 1,PDB(iam).Xelements
do j = 1,YY
do k = 1,ZZ
dataTmp(1,k,j,i) = R(i,j,k)
dataTmp(2,k,j,i) = U(i,j,k)
dataTmp(3,k,j,i) = V(i,j,k)
dataTmp(4,k,j,i) = W(i,j,k)
dataTmp(5,k,j,i) = P(i,j,k)
end do
end do
end do
call MPI_FILE_SET_VIEW(ifile, offset, MPI_FLOAT, slice, 'native', MPI_INFO_NULL, ierr)
call MPI_FILE_WRITE_ALL(ifile, dataTmp, 5*PDB(iam).Xelements*YY*ZZ, MPI_FLOAT, wstatus, ierr)
call MPI_BARRIER(MPI_COMM_WORLD, ierr)
Это работает хорошо. Но я не уверен насчет использования массива dataTmp. Какое решение будет быстрее и правильнее? Как насчет использования 4D-массива, такого как dataTmp, во всей программе? Или, может быть, я должен создать 5 специальных mpi_types с различным displacemet.
2 ответа
Если скорость ввода-вывода является проблемой, и у вас есть возможность, я бы предложил изменить формат файла - или, альтернативно, способ размещения данных в памяти - чтобы быть более тесно выстроенными: в последовательном коде, запись данных в этот транспонированный и чередующийся путь будет очень медленным:
program testoutput
implicit none
integer, parameter :: XX=512, YY=512, ZZ=512
real, dimension(:,:,:), allocatable :: R, U, V, W, P
integer :: timer
integer :: ifile
real :: elapsed
integer :: i,j,k
allocate(R(XX,YY,ZZ), P(XX,YY,ZZ))
allocate(U(XX,YY,ZZ), V(XX,YY,ZZ), W(XX,YY,ZZ))
R = 1.; U = 2.; V = 3.; W = 4.; P = 5.
open(newunit=ifile, file='interleaved.dat', form='unformatted', status='new')
call tick(timer)
do i=1,XX
do j=1,YY
do k=1,ZZ
write(ifile) R(i,j,k)
write(ifile) U(i,j,k)
write(ifile) V(i,j,k)
write(ifile) W(i,j,k)
write(ifile) P(i,j,k)
end do
end do
end do
elapsed=tock(timer)
close(ifile)
print *,'Elapsed time for interleaved: ', elapsed
open(newunit=ifile, file='noninterleaved.dat', form='unformatted',status='new')
call tick(timer)
write(ifile) R
write(ifile) U
write(ifile) V
write(ifile) W
write(ifile) P
elapsed=tock(timer)
close(ifile)
print *,'Elapsed time for noninterleaved: ', elapsed
deallocate(R,U,V,W,P)
contains
subroutine tick(t)
integer, intent(OUT) :: t
call system_clock(t)
end subroutine tick
! returns time in seconds from now to time described by t
real function tock(t)
integer, intent(in) :: t
integer :: now, clock_rate
call system_clock(now,clock_rate)
tock = real(now - t)/real(clock_rate)
end function tock
end program testoutput
Бег дает
$ gfortran -Wall io-serial.f90 -o io-serial
$ ./io-serial
Elapsed time for interleaved: 225.755005
Elapsed time for noninterleaved: 4.01700020
Как говорит Роб Лэтхэм, который знает об этом больше, чем о нескольких вещах, ваше преобразование для параллельной версии прекрасно - оно выполняет чередование и транспонирование явно в памяти, где это происходит намного быстрее, а затем записывает его на диск. Это примерно так же быстро, как IO собирается получить.
Вы можете определенно избежать массива dataTmp, написав один или пять отдельных типов данных, чтобы выполнить преобразование / перемежение для вас при выходе на диск с помощью процедуры MPI_File_write_all. Это даст вам немного больше баланса между использованием памяти и производительностью. Вы не будете явно определять большой трехмерный массив, но код MPI-IO улучшит производительность по сравнению с зацикливанием отдельных элементов, выполняя немало буферизации, а это означает, что для выполнения операции выделяется определенный объем памяти. писать эффективно. Хорошей новостью является то, что баланс будет настраиваться путем установки подсказок MPI-IO в переменной Info; Плохая новость в том, что код, вероятно, будет менее понятным, чем тот, который у вас есть сейчас.
Использование dataTmp хорошо, если у вас есть место в памяти. Ваш вызов MPI_FILE_WRITE_ALL будет самой дорогой частью этого кода.
Вы выполнили сложную часть, настроив вид файла MPI-IO. если вы хотите избавиться от dataTmp, вы можете создать тип данных MPI для описания массивов (возможно, используя MPI_Type_hindexed и MPI_Get_address)), а затем использовать MPI_BOTTOM в качестве буфера памяти.