MPI BCast(广播)std ::向量结构

时间:2013-05-24 15:12:57

标签: c++ vector mpi openmpi

我有一个关于通过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);

2 个答案:

答案 0 :(得分: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_extentofMPI_Type_get_extent()返回的MPI数据类型的范围。因为MPI使用范围来确定每个数组元素的起始位置,所以建议为任何用于发送多个元素的结构类型显式设置它。对于MPI-1,这通常通过添加MPI_UB假型的一个特殊结构元素来完成,但在现代MPI代码中(或者通常在MPI-2中),应该使用MPI_Type_create_resized来实现此目的: / p>

//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合作。

答案 1 :(得分:1)

std::vector::reserve(N)不会影响size,但(如果有的话)capacity(可能还有位置),因此接收容器deltaLine仍然是零大小的矢量,无论其capacity等于deltaLineSize

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

我还会检查(至少)第一个MPI_BCast的返回值,因为如果那个,无论出于何种原因在一个进程上失败,那么向量的大小将为0,如果它响应第二个广播界限将被违反。