3

mpi 広告で割り当て可能な配列を使用して派生型データを送信しようとしていますが、seg fault が発生しました。

program test_type  

 use mpi

 implicit none

 type mytype
  real,allocatable::x(:)
  integer::a
 end type mytype

 type(mytype),allocatable::y(:)
 type(mytype)::z
 integer::n,i,ierr,myid,ntasks,status,request
 integer :: datatype, oldtypes(2), blockcounts(2) 
 integer(KIND=MPI_ADDRESS_KIND) :: offsets(2)

 call mpi_init(ierr)
 call mpi_comm_rank(mpi_comm_world,myid,ierr)
 call mpi_comm_size(mpi_comm_world,ntasks,ierr)

 n=2

 allocate(z%x(n))

 if(myid==0)then
  allocate(y(ntasks-1))
  do i=1,ntasks-1
   allocate(y(i)%x(n))
  enddo
 else
  call random_number(z%x)
  z%a=myid
  write(0,*) "z in process", myid, z%x, z%a
 endif

 call mpi_get_address(z%x,offsets(1),ierr)
 call mpi_get_address(z%a,offsets(2),ierr)
 offsets=offsets-offsets(1)

 oldtypes=(/ mpi_real,mpi_integer /)
 blockcounts=(/ n,1 /)

 write(0,*) "before commit",myid,offsets,blockcounts,oldtypes
 call mpi_type_create_struct(2,blockcounts,offsets,oldtypes,datatype,ierr) 
 call mpi_type_commit(datatype, ierr)
 write(0,*) "after commit",myid,datatype, ierr

 if(myid==0) then   
  do i=1,ntasks-1 
   call mpi_irecv(y(i),1,datatype,1,0,mpi_comm_world,request,ierr) 
   write(0,*) "received", y(i)%x,y(i)%a
  enddo
 else
  call mpi_isend(z,1,datatype,0,0,mpi_comm_world,request,ierr) 
  write(0,*) "sent"
  write(0,*) myid, z%x, z%a
 end if

 call mpi_finalize(ierr)

end program

そして、これは私が2つのプロセスで実行して印刷したものです:

before commit           0                     0             -14898056
           2           1          13           7
 after commit           0          73           0
 z in process           1  3.9208680E-07  2.5480442E-02           1
 before commit           1                     0            -491689432
           2           1          13           7
 after commit           1          73           0
 received  0.0000000E+00  0.0000000E+00           0
forrtl: severe (174): SIGSEGV, segmentation fault occurred

負のアドレス オフセットを取得するようです。助けてください。ありがとう。

4

1 に答える 1

2

このコードには複数の問題があります。

ほとんどの Fortran コンパイラでの割り当て可能な配列は、C/C++ のポインターのようなものです。配列名の背後にある実際のオブジェクトは、割り当てられたデータへのポインターを保持するものです。そのデータは通常、ヒープに割り当てられ、プロセスの仮想アドレス空間のどこにでもある可能性があるため、負のオフセットが説明されます。ところで、負のオフセットは MPI データ型で完全に受け入れられるので (これが符号付き整数の種類をMPI_ADDRESS_KIND指定する理由です)、ここでは大きな問題はありません。

より大きな問題は、動的に割り当てられたもの間のオフセットが通常、割り当てごとに異なることです。次のことを確認できます。

ADDR(y(1)%x) - ADDR(y(1)%a)

とは完全に異なります

ADDR(y(i)%x) - ADDR(y(i)%a), for i = 2..ntasks-1

(ADDRこれは、 によって返されるオブジェクト アドレスの簡略表記ですMPI_GET_ADDRESS)

オフセットが の一部の値と一致したとしてiも、それはルールというよりも偶然です。

つまり、変数からのオフセットを使用して作成した型を使用して、配列zの要素を送信することはできません。これを解決するには、可能な場合 (たとえば、事前にわかっている場合)の割り当て可能なyプロパティを削除します。mytype%xn

の値が小さい場合にうまく機能するもう 1 つのオプションは、配列ntasksの要素数と同じ数の MPI データ型を定義することです。y次に、 と のオフセットに基づく を使用してdatatype(i)、を送信します。y(i)%xy(i)%ay(i)

より重大な問題は、ノンブロッキング MPI 操作を使用しており、データ バッファーにアクセスする前にそれらの操作が完了するのを決して待たないという事実です。このコードは単に機能しません:

do i=1,ntasks-1 
 call mpi_irecv(y(i),1,datatype,1,0,mpi_comm_world,request,ierr) 
 write(0,*) "received", y(i)%x,y(i)%a
enddo

呼び出しMPI_IRECVにより、非同期受信操作が開始されます。操作はおそらくWRITEオペレーターが実行されるまで進行中であるため、完全にランダムなデータがアクセスされています (一部のメモリ アロケーターは、デバッグ モードで実際にデータをゼロにする場合があります)。との呼び出しの間に呼び出しを挿入するか、ブロッキング receive を使用しMPI_WAITます。MPI_ISENDWRITEMPI_RECV

ノンブロッキング send call の使用にも同様の問題が存在しますMPI_ISEND。要求やテストの完了を待つことはないため、MPI ライブラリは操作の実際の進行を無期限に延期することができ、送信が実際に行われない可能性があります。繰り返しますが、あなたのケースでは非ブロッキング送信を使用する正当な理由はまったくないため、 に置き換えMPI_ISENDてくださいMPI_SEND

最後になりましたが、ランク 0 はランク 1 からのみメッセージを受信して​​います。

call mpi_irecv(y(i),1,datatype,1,0,mpi_comm_world,request,ierr)
                              ^^^

同時に、他のすべてのプロセスはランク 0 に送信されます。したがって、プログラムは 2 つの MPI プロセスで実行された場合にのみ機能します。1受信呼び出しの下線を に置き換えることができますi

于 2015-07-24T22:46:56.343 に答える