MPI使用C ++中的vector属性发送struct

时间:2016-03-15 20:18:06

标签: c++ vector struct mpi stdvector

我想发送一个具有vector属性的结构。

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

我知道我必须像this answer一样创建一个MPI派生数据类型,但我不知道如何在我的情况下这样做,我在结构中有一个向量。

3 个答案:

答案 0 :(得分:1)

如果你想保持高水平并发送物体,那么Boost.MPI是一个不错的选择。使用Boost.MPI,您可以为结构指定高级序列化。

您无法(正确)静态地确定向量的数据成员的偏移量。当然可以拼凑出一种有效的类型。但这也是射击自己的好方法。您将在代码中引入假设(例如,矢量大小不会改变),一旦违反将会产生微妙的错误。因此,在这种情况下,只需在id中单独发送neighbours::data()MPI_Send,而不是使用不适合此用例的MPI类型,它似乎更简洁,更不容易出错

答案 1 :(得分:1)

我不喜欢导入库只是为了做这件简单的事情。所以这就是我所做的:

我认为没有理由让MPI知道对象的底层结构。所以我可以手动将其转换为缓冲区数组,并且由于接收方知道期望Node结构,因此可以在另一侧重新创建对象。所以最初我定义了一个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); 

这是一个更通用的解决方案并且有效。

但是由于结构包含intvector<int>,我决定创建一个int缓冲区,其中第一个元素为node.id,重置为node.neighbors 。另一方面,使用MPI_Iprobe(或同步MPI_Probe)和MPI_Get_count我可以重新创建Node结构。这是代码:

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;
}

我认为必须有一种更有效/更快的方式将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

}

现在它按预期工作。

答案 2 :(得分:0)

请注意,vector<int>内部看起来像这样:

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

因此,如果您尝试将结构作为puelo建议发送,它将无法访问向量底层的实际数据,而是发送size字段,data指针和任何数据跟随这些项在内存中,这很可能会导致无效的内存访问。矢量中的实际数据不会像这样发送。

通常,MPI不适用于发送包含指向更多数据的指针的结构。相反,您应该尝试考虑如何发送实际的基础数据本身。

如果您能够以连续的方式表示数据,那么MPI通信将更容易,更有效。

您的struct Node看起来像是在尝试在图表中表示节点。例如,您可以以邻接数组格式表示图形数据,其中所有邻居ID都在一个单一的大向量中表示。可以把它想象成以前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时,为数据寻找良好的数据布局是实施算法时最重要的步骤之一。