2016-03-15 2 views
3

Я хочу отправить структуру с векторным свойством.MPI send struct с векторным свойством в C++

typedef struct { 
    int id; 
    vector<int> neighbors; 
} Node; 

Я знаю, что я должен создать MPI производного типа данных, как в this answer, но я не знаю, как это сделать в моем случае, когда у меня есть вектор в структурах.

+1

'vector :: data()' дает вам доступ к необработанному указателю со смежно сохраненными элементами. – ftynse

ответ

0

Мне не понравилась идея импортировать библиотеку, чтобы сделать это просто. Итак, вот что я сделал:

Я думал, что нет никакой причины, чтобы MPI знал что-либо о базовой структуре объекта. Поэтому я мог бы просто вручную преобразовать его в буферный массив, и поскольку приемник знает, что ожидает структуру узла, можно воссоздать объект с другой стороны.Таким образом, изначально я определил MPI_Contiguous тип данных и отправить его:

int size = (int) ((node.second.neighbors.size() + 1) * sizeof(int *)); 

MPI_Datatype datatype; 
MPI_Type_contiguous(size, MPI_BYTE, &datatype); 
MPI_Type_commit(&datatype); 

MPI_Isend(&buffer, 1, datatype, proc_rank, TAG_DATA, MPI_COMM_WORLD, &request); 

Это более общее решение и работал.

Но поскольку структура содержит int и vector<int>, я решил создать ИНТ буфер с первым элементом в качестве node.id и сброса в качестве node.neighbors. А с другой стороны, используя MPI_Iprobe (или синхронно MPI_Probe) и MPI_Get_count, я могу воссоздать структуру узла. Вот код:

int *seriealizeNode(Node node) { 
    //allocate buffer array 
    int *s = new int[node.neighbors.size() + 1]; 
    //set the first element = Node.id 
    s[0] = node.id; 
    //set the rest elements to be the vector elements 
    for (int i = 0; i < node.neighbors.size(); ++i) { 
     s[i + 1] = node.neighbors[i]; 
    } 
    return s; 
} 

Node deseriealizeNode(int buffer[], int size) { 
    Node node; 
    //get the Node.id 
    node.id = buffer[0]; 
    //get the vector elements 
    for (int i = 1; i < size; ++i) { 
     node.neighbors.push_back(buffer[i]); 
    } 
    return node; 
} 

Я думаю, что должно быть более эффективным/быстрым способом для преобразования узла в INT [], и наоборот. Я бы хотел, чтобы кто-то мог предложить несколько советов.

Тогда на стороне отправители:

while (some_condition){ 

    ... 

    //if there is a pending request wait for it to finish and then free the buffer 
    if (request != MPI_REQUEST_NULL) { 
     MPI_Wait(&request, &status); 
     free(send_buffer); 
    } 

    // now send the node data 
    send_buffer = seriealizeNode(node.second); 
    int buffer_size = (int) (node.second.neighbors.size() + 1); 
    MPI_Isend(send_buffer, buffer_size, MPI_INT, proc, TAG_DATA, MPI_COMM_WORLD, &request); 

    ... 
} 

А на стороне приемников:

int count = 0; 
MPI_Iprobe(MPI_ANY_SOURCE, TAG_DATA, MPI_COMM_WORLD, &flag, &status); 
if (flag) { 
    MPI_Get_count(&status, MPI_INT, &count); 
    int *s = new int[count]; 
    MPI_Recv(s, count, MPI_INT, MPI_ANY_SOURCE, TAG_DATA, MPI_COMM_WORLD, &status); 
    Node node = deseriealizeNode(s, count); 
    free(s); 
    //my logic 

} 

Теперь он работает, как ожидалось.

+0

Если вы хотите, чтобы это было эффективно, вы должны избегать копирования и отправлять данные 'id' и' neigbours :: data'. Если вы хотите быть наиболее эффективным, вы должны сначала поставить 'id' и' neighbours' в смежную память. Если вы просто хотите быть немного более эффективным, по крайней мере, запустите 'node.nieghbours.reserve (size-1)' при десериализации. И удалите лишнюю «новую». – Zulan

+0

ИМХО, что вы делаете ** не ** a * простая вещь *. Вы ассиметрично выделяете и освобождаете память. Это прямой способ ввести тонкие ошибки/утечки памяти. – Zulan

+0

@ Zulan Мне нужно представить узлы на графике следующим образом. Теперь я не могу изменить представление. Но я буду иметь это в виду в будущем. Также спасибо за подсказку. –

0

Обратите внимание, что Внутренне vector<int> выглядит примерно так:

struct vector { 
    size_t size; 
    size_t alloc_size; 
    int* data; 
}; 

Таким образом, если вы пытаетесь отправить, как-структуру Пуэло предложенного, он не будет иметь доступ фактические данные, лежащие в основе вектора, но вместо того, чтобы отправить size, указатель data и любые данные следуют этим элементам в памяти, что, скорее всего, приведет к недопустимому доступу к памяти. Фактические данные в векторе не будут отправляться так.

Как правило, MPI не работает для отправки структур, содержащих указатели, на большее количество данных. Вместо этого вы должны попытаться подумать о том, как отправлять фактические базовые данные.

MPI-связь будет проще и эффективнее, если вы можете представлять свои данные смежным образом.

Ваш struct Node выглядит так, будто вы пытаетесь представить узел в графе. Например, вы можете представить данные графа в формате массива смежности, где все идентификаторы соседей представлены в одном большом векторе. Подумайте об этом как о конкатенации всех векторов neighbors с вашего предыдущего struct Node. Для каждого узла вы сохраните смещение в новом векторе neighbors.

std::vector<int> node_ids(num_nodes); 
std::vector<int> nodes_offsets(num_nodes); 
std::vector<int> neighbors(num_edges); 

// neighbors for node i are accessible via: 
for (int j = node_offsets[i]; j <= node_offsets[i+1]-1; ++j) { 
    int neighbor = neighbors[j]; 
    // ... 
} 

Вы можете отправить/получить эту информацию с MPI легко:

MPI_Send(&neighbors[0], MPI_INT, neighbors.size(), ...); 

При работе с MPI, найти хорошую структуру данных для хранения данных является одним из наиболее важных шагов в процессе реализации ваших алгоритмов.

+1

Использование FYI most 'vector' использует [три указателя вместо указателя и двух размеров] (http://stackoverflow.com/q/30422205/620382). Конечно, это не влияет на ваши аргументы. – Zulan

+0

Вы правы, что моя структура представляет узел в графе, но мне нужно представлять данные графа в узлах структуры. Это должен быть способ сделать то, что я хочу ... –

+0

Спасибо, Зулан, это полезно знать! – Patrick

0

Если вы хотите оставаться на высоком уровне и отправлять предметы, то Boost.MPI - хороший выбор. С Boost.MPI вы указываете сериализацию высокого уровня для своих структур.

Вы не можете (правильно) статически определить смещение элемента данных вектора. Конечно, возможно скомпоновать тип, который работает. Но это также отличный способ стрелять в ногу. Вы вводите предположения в коде (например, размер вектора не изменяется), который однажды нарушил, создаст тонкие ошибки. Поэтому в этом случае кажется более чистым и менее подверженным ошибкам просто отправлять id и neighbours::data() отдельно в MPI_Send - вместо использования типов MPI, которые не подходят для этого прецедента.

Смежные вопросы