MPI在C ++中发送具有向量属性的结构

Posted

技术标签:

【中文标题】MPI在C ++中发送具有向量属性的结构【英文标题】:MPI send struct with a vector property in C++ 【发布时间】:2016-07-01 11:37:43 【问题描述】:

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

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

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

【问题讨论】:

vector::data() 让您可以访问具有连续存储元素的原始指针 【参考方案1】:

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

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

【讨论】:

【参考方案2】:

我不喜欢仅仅为了做这个简单的事情而导入库的想法。所以这就是我所做的:

我认为没有理由让 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&lt;int&gt;,我决定创建一个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;

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


现在它可以按预期工作了。

【讨论】:

如果您希望它高效,您必须避免复制并发送idneigbours::data。如果您想最高效,您必须首先将idneighbours 放在连续内存中。如果您只是想提高一点效率,至少在反序列化时运行node.nieghbours.reserve(size-1)。并删除那里多余的new 恕我直言,您正在做的事情不是 简单的事情。您正在不对称地分配和释放内存。这是引入细微错误/内存泄漏的直接方法。 @Zulan 我需要像这样表示图中的节点。我现在无法更改表示。但我将来会牢记这一点。也感谢您的性能提示。【参考方案3】:

请注意,vector&lt;int&gt; 在内部看起来像这样:

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

因此,如果您尝试按照 puelo 的建议发送结构,它不会访问向量底层的实际数据,而是发送 size 字段、data 指针以及这些项目后面的任何数据内存,这很可能会导致无效的内存访问。向量中的实际数据不会这样发送。

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

如果您能够以连续的方式表示您的数据,MPI 通信将变得更加轻松和高效。

您的struct Node 看起来您正试图在图中表示一个节点。例如,您可以以邻接数组格式表示您的图形数据,其中所有邻居 id 都表示在一个大向量中。把它想象成你之前的struct Node 中所有neighbors 向量的串联。对于每个节点,您将偏移量保存到新的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 时,为您的数据找到一个好的数据布局是实现算法时最重要的步骤之一。

【讨论】:

仅供参考,大多数vector 实现使用three pointers instead of a pointer and two sizes。当然这对你的论点没有影响。 我的结构代表图中的一个节点是正确的,但我需要在结构节点中表示图数据。这一定是做我想做的事的一种方式......

以上是关于MPI在C ++中发送具有向量属性的结构的主要内容,如果未能解决你的问题,请参考以下文章

使用 MPI_Type_create_struct() 在 C 中传输包含动态数组的结构

目标 C:在非结构或联合的情况下请求成员 XXX。 (核心数据)

具有相同成员类型的 C 结构是不是保证在内存中具有相同的布局?

在 C 中的 MPI 中如何创建结构结构并将其发送到多个进程

谁能给我讲讲c的结构体啊

C语言自定义数据类型之结构体