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