我有一个关于通过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);
答案 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_extentof
是MPI_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,如果它响应第二个广播界限将被违反。