3

我有一个关于通过 MPI 传递结构的 std::vector 的问题。

首先,细节。我在 gcc 中使用 OpenMPI 1.4.3(符合 MPI-2)。请注意,我不能使用 boost 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;

我有一个 DeltaLine,我想通过 MPI 向所有节点广播。

我可以安全便携地执行以下操作吗?这在我的测试用例中对我有用。我只是想确保它在不同平台上以及根据 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

C++ 标准保证 的元素std::vector连续存储在内存中,并std::vector::reserve()在调用时在必要时(重新)分配内存,因此从内存管理的角度来看,您的解决方案是完全有效的。虽然,正如 Solkar 所指出的,std::vector::reserve()它只保留内存空间,但向量对象并不知道有数据直接写入该内存中,因此保留了先前的元素计数(新创建的向量为零)。这可以通过std::vector::resize()在第二次广播操作之前调用来解决。

一条评论虽然适用于使用构造的 MPI 数据类型发送数组的所有情况 - 您应该注意连续数组元素之间可能的填充。换句话说,由于末尾可能有填充,以下内容可能会保留struct

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

其中mpi_extentof是由 . 返回的 MPI 数据类型的范围MPI_Type_get_extent()。由于 MPI 使用范围来确定每个数组元素的开始位置,因此建议为用于发送多个元素的任何结构类型显式设置它。在 MPI-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,它仍然是一个零大小的向量,不管它是否capacity相等deltaLineSize

这在代码中还不是问题,但我假设您打算使用接收到的数据进行一些处理。

我还将检查(至少)第一个的返回值MPI_BCast,因为如果由于某种原因在一个进程上失败,则向量的大小将为 0,并且如果它响应第二个广播边界将被违反。

于 2013-05-24T17:39:12.600 回答