3

MPI を介した構造体の std::vector の受け渡しに関して質問があります。

まずは詳細から。gcc で OpenMPI 1.4.3 (MPI-2 準拠) を使用しています。ブースト MPI または OOMPI は使用できないことに注意してください。このバージョンを使用する必要があります。

いくつかのデータを集約するための構造体があります:

  struct Delta {
    Delta() : dX(0.0), dY(0.0), dZ(0.0) {};
    Delta(double dx, double dy, double dz) :
      dX(dx), dY(dy), dZ(dz) {};
    Delta(const Delta& rhs) :
      dX(rhs.dX), dY(rhs.dY), dZ(rhs.dZ) {};

    double dX;
    double dY;
    double dZ;
  };

  typedef std::vector<Delta> DeltaLine;

そして、MPI 経由ですべてのノードにブロードキャストしたい DeltaLine があります。

次の操作を安全かつポータブルに実行できますか? これは私のテストケースでうまくいきます。さまざまなプラットフォーム間で、C++ および MPI 標準に従って合法でコーシャであることを確認したいだけです。

ありがとう!マドレーヌ。

  //Create an MPI struct for the Delta class
  const int    nItems=3;
  int          blocklengths[nItems] = {1, 1, 1};
  MPI_Datatype types[nItems] = {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE};
  MPI_Datatype MPI_DeltaType;
  MPI_Aint     offsets[nItems];

  offsets[0] = offsetof(Delta, dX);
  offsets[1] = offsetof(Delta, dY);
  offsets[2] = offsetof(Delta, dZ);

  MPI_Type_create_struct(nItems, blocklengths, offsets, types, &MPI_DeltaType);
  MPI_Type_commit(&MPI_DeltaType);

  //This is the vector to be filled, and its size
  DeltaLine deltaLine;
  unsigned deltaLineSize;

  //If this is the master proc, get the DeltaLine and its size
  if(amMaster()) {
    deltaLine = getMasterDeltaLine();
    deltaLineSize = deltaLine.size();
  }

  //Send out the correct size
  MPI_Bcast(&deltaLineSize, 1, MPI_UNSIGNED, COMM_PROC, MPI_COMM_WORLD);

  //Size the delta line vector, and broadcast its contents
  deltaLine.reserve(deltaLineSize);
  MPI_Bcast(&deltaLine.front(), deltaLineSize, MPI_DeltaType, COMM_PROC, MPI_COMM_WORLD);

  //Free up the type
  MPI_Type_free(&MPI_DeltaType);
4

2 に答える 2

3

std::vectorC++ 標準では、 の要素がメモリに連続して格納され、呼び出し時に必要に応じてメモリが(再) 割り当てられることが保証されているstd::vector::reserve()ため、メモリ管理の観点からは、ソリューションは完全に有効です。ただし、Solkar が指摘したように、std::vector::reserve()メモリ空間のみを予約しますが、ベクトル オブジェクトはそのメモリに直接書き込まれているデータがあることを認識しないため、以前の要素数 (新しく作成されたベクトルの場合はゼロ) を保持します。std::vector::resize()これは、2 番目のブロードキャスト操作の前に呼び出すことで修正できます。

ただし、構築された MPI データ型を使用して配列を送信するすべてのケースに当てはまるコメントが 1 つあります。連続する配列要素間のパディングの可能性に注意する必要があります。つまり、 の最後にパディングが可能なため、次のものが保持される可能性がありますstruct

(char*)&deltaLine[1] - (char*)&deltaLine[0] != mpi_extentof(MPI_DeltaType)

ここmpi_extentofで、 は によって返される MPI データ型の範囲ですMPI_Type_get_extent()。MPI はエクステントを使用して各配列要素の開始位置を決定するため、複数の要素を送信するために使用される構造体型に対して明示的に設定することをお勧めします。MPI-1 では、これは通常、疑似型の特別な構造要素を 1 つ追加することによって行われMPI_UBますが、最新の MPI コード (または一般に MPI-2) ではMPI_Type_create_resized、その目的のために次を使用する必要があります。

//Create an MPI struct for the Delta class
const int    nItems=3;
int          blocklengths[nItems] = {1, 1, 1};
MPI_Datatype types[nItems] = {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE};
MPI_Datatype MPI_DeltaType_proto, MPI_DeltaType;
MPI_Aint     offsets[nItems];

offsets[0] = offsetof(Delta, dX);
offsets[1] = offsetof(Delta, dY);
offsets[2] = offsetof(Delta, dZ);

MPI_Type_create_struct(nItems, blocklengths, offsets, types, &MPI_DeltaType_proto);

// Resize the type so that its length matches the actual structure length

// Get the constructed type lower bound and extent
MPI_Aint lb, extent;
MPI_Type_get_extent(MPI_DeltaType_proto, &lb, &extent);

// Get the actual distance between to vector elements
// (this might not be the best way to do it - if so, substitute a better one)
extent = (char*)&deltaLine[1] - (char*)&deltaLine[0];

// Create a resized type whose extent matches the actual distance
MPI_Type_create_resized(MPI_DeltaType_proto, lb, extent, &MPI_DeltaType);
MPI_Type_commit(&MPI_DeltaType);

あなたの場合double、構造には要素しかなく、パディングは期待されていないため、これをすべて行う必要はありません。ただし、今後 MPI を使用する場合は、このことを念頭に置いてください。

于 2013-05-25T08:45:07.813 に答える
1

std::vector::reserve(N)影響はありませんsizeが、(もしあったとしても) capacity(そしておそらく場所)、そのため、受信コンテナーは、その等号deltaLineに関係なく、サイズがゼロのベクトルのままになります。capacitydeltaLineSize

そのままのコードではまだ問題ありませんが、受信したデータを使用して何らかの処理を行うつもりだと思います。

また、(少なくとも)最初の の戻り値もチェックしますMPI_BCast。これは、何らかの理由で 1 つのプロセスで失敗した場合、ベクトルのサイズが 0 になり、2 番目のブロードキャスト境界に応答すると違反するためです。

于 2013-05-24T17:39:12.600 に答える