在课程实现过程中,我必须编写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。什么解决方案会更快更正确?如何在整个程序中使用像dataTmp这样的4D数组呢?或者,也许,我应该创建5个具有不同displacemet的特殊mpi_types。
答案 0 :(得分:1)
如果你有内存空间,使用dataTmp很好。您的MPI_FILE_WRITE_ALL调用将是此代码中最昂贵的部分。
您已经完成了困难的部分,设置了MPI-IO文件视图。如果你想摆脱dataTmp,你可以创建一个MPI数据类型来描述数组(可能使用MPI_Type_hindexed和MPI_Get_address)),然后使用MPI_BOTTOM作为内存缓冲区。
答案 1 :(得分:0)
如果I / O速度是一个问题并且您可以选择,我建议更改文件格式 - 或者更改,数据在内存中的布局方式 - 更紧密地排列:在序列中代码,以这种转置和交错方式写入数据将非常缓慢:
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
正如Rob Latham所知道的那样,对于这个东西不仅仅知道一些事情,你对并行版本的转换很好 - 它在内存中明确地进行交错和转置,它的速度要快得多,然后把它炸成磁盘。它的速度和IO一样快。
通过写入一个或五个单独的数据类型,您可以通过MPI_File_write_all例程在转出磁盘的过程中为您进行转置/交错,从而避免使用dataTmp数组。这将在内存使用和性能方面为您提供更多的平衡。你不会明确地定义一个大的3-D数组,但是MPI-IO代码通过做一些缓冲来提高在单个元素上循环的性能,这意味着一定量的内存被放在一边有效地写作。好消息是通过在Info变量中设置MPI-IO提示可以调整余额;坏消息是代码可能不如你现在的那么清晰。