MPI BCast (трансляция) std:: вектор структур
У меня есть вопрос относительно прохождения std::вектора структур через MPI.
Во-первых, детали. Я использую OpenMPI 1.4.3 (MPI-2 совместимый) с gcc. Обратите внимание, что я не могу использовать 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 ответа:
Стандарт C++ гарантирует, что элементы
std::vector
хранятся последовательно в памяти и чтоstd::vector::reserve()
(re-)выделяет память, если это необходимо во время вызова, поэтому ваше решение совершенно корректно с точки зрения управления памятью. Хотя, как отметил Солкар,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-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.
std::vector::reserve(N)
не влияет наsize
, но (если вообще влияет)capacity
(и, возможно, местоположение), поэтому для принимающих контейнеровdeltaLine
все равно будет вектор нулевого размера, независимо от егоcapacity
равногоdeltaLineSize
.Это еще не проблема в коде как есть, но я предполагаю, что вы намерены выполнить некоторую обработку, используя полученные данные.
Я бы также проверил возвращаемое значение (по крайней мере) первого
MPI_BCast
, потому что если это по какой-либо причине не удается на одном процессе, размер вектора будет равен 0, и если он реагирует на вторую широковещательную передачу, границы которой будут нарушены.