2

「大きな」配列(それぞれ100 000フロートの2つの配列)を使用してMPI派生データ型を送信しようとすると、プログラムが失敗します。ただし、通常は小さいアレイで実行されます。

以下は、再現可能な小さな例です。この小さなプログラムは、次のMPI実装でsegfaultします:  IntelMPIBullXMPIOpenMPIおよびPlatformMPIで正常に動作します。例のバックトレースを含むログは次のとおりです:http://pastebin.com/FMBpCuj2

に変更mpi_sendしてmpi_ssendも効果はありません。ただし、mpi_send2 * 100 000フロートの単一の大きな配列では、正常に機能します。私の意見では、これは派生データ型の問題を示しています。

program struct 
include 'mpif.h' 

type Data
  integer :: id
  real, allocatable :: ratio(:)
  real, allocatable :: winds(:)
end type 

type (Data) :: test
integer :: datatype, oldtypes(3), blockcounts(3) 
integer :: offsets(3)
integer :: numtasks, rank, i,  ierr 
integer :: n, status(mpi_status_size)

call mpi_init(ierr) 
call mpi_comm_rank(mpi_comm_world, rank, ierr) 
call mpi_comm_size(mpi_comm_world, numtasks, ierr) 

if (numtasks /= 2) then
  write (*,*) "Needs 2 procs"
  call exit(1)
endif

n = 100000
allocate(test%ratio(n))
allocate(test%winds(n))
if (rank == 0) then
  test%ratio = 6
  test%winds = 7
  test%id = 2
else
  test%id = 0
  test%ratio = 0
  test%winds = 0
endif

call mpi_get_address(test%id, offsets(1), ierr)
call mpi_get_address(test%ratio, offsets(2), ierr)
call mpi_get_address(test%winds, offsets(3), ierr)

do i = 2, size(offsets)
  offsets(i) = offsets(i) - offsets(1)
end do
offsets(1) = 0

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

call mpi_type_struct(3, blockcounts, offsets, oldtypes, datatype, ierr) 
call mpi_type_commit(datatype, ierr) 

if (rank == 0) then 
  !call mpi_ssend(test, 1, datatype, 1, 0,  mpi_comm_world, ierr) 
  call mpi_send(test, 1, datatype, 1, 0,  mpi_comm_world, ierr) 
else
  call mpi_recv(test, 1, datatype, 0, 0,  mpi_comm_world, status, ierr) 
end if

print *, 'rank= ',rank
print *, 'data= ',test%ratio(1:5),test%winds(1:5)

deallocate (test%ratio)
deallocate (test%winds)
call mpi_finalize(ierr) 


end 

注:テストがすべて同じマシン上で行われたわけではないため、異なるMPIの実装間の比較は客観的ではありません(一部はスーパーコンピューターです)。それでも、私はそれが違いを生むべきではないと思います。

編集:コードは静的配列で機能します。そしてこれはFortran90です。

4

1 に答える 1

6

デバッガーの使用を提案できますか? Allinea DDTであなたの例を試してみたところ、2 分で問題が発生しました。デバッガーを使用する必要があります-コードは「正しく見える」ため、実際にどのように動作するかを確認します。

クリックしてメモリ デバッグをオンにすると (非表示のエラーを強制的に表示する方法)、例は毎回 OpenMPI でクラッシュしました。クラッシュは送信側にありました。

そこで、DDT のメモリ デバッグをオンにして、DDT の手順を実行し始めました。

まず、MPI_Get_address を呼び出して、オフセットの配列を埋めます。それらのオフセットを見てください!整数のアドレスは正であり、割り当て可能な配列のオフセットは負です: 悪い兆候です。アドレスがオーバーフローしました。

割り当てられたデータのアドレスは、静的に割り当てられた整数とはまったく異なるメモリ領域にあります。32 ビット演算を使用して 64 ビット ポインターを操作すると、すべての賭けが無効になります (MPI_Get_address はこれについて警告します)。静的配列では、オーバーフローしないようにアドレスが整数のアドレスに十分近いため、クラッシュしませんでした。

この誤ったオフセット配列を MPI_Send に送信すると、データが読み取られない場所から読み取られ (オフセット バッファーをもう一度見て納得してください)、セグメンテーション違反が発生します。

ここでの本当の修正は -

  1. MPI_Get_address では、オフセットの宣言に INTEGER(KIND=MPI_ADDRESS_KIND) を使用して、64 ビット コードが 64 ビット整数を確実に取得するようにします。

  2. MPI_type_struct は MPI_type_create_struct に置き換える必要があります。前者は非推奨であり、MPI_ADDRESS_KIND 整数の形式でオフセットを使用せず、4 バイト整数のみを使用するため、欠陥があります。

これらの変更により、コードが実行されます。

幸運を!

于 2012-11-03T23:09:10.760 に答える