MPI BCast (трансляция) std :: вектор структур

У меня есть вопрос относительно передачи 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);

3

Решение

Стандарт 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.

3

Другие решения

std::vector::reserve(N) не влияет size но (если вообще) capacity (и, возможно, местоположение), так что для получения контейнеров deltaLine будет по-прежнему нулевым вектором, независимо от его capacity сравнявшись deltaLineSize,

Это еще не проблема в коде как есть, но я предполагаю, что вы намереваетесь выполнить некоторую обработку с использованием полученных данных.

Я также проверил бы возвращаемое значение (по крайней мере) первого MPI_BCast, потому что, если по какой-либо причине произойдет сбой в одном из процессов, размер вектора будет равен 0, и если он откликнется на второй диапазон, будет нарушен.

1

По вопросам рекламы [email protected]