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 в качестве буфера памяти.

Другие вопросы по тегам