C语言中的struct serialization和通过MPI传输

时间:2012-03-25 22:04:22

标签: struct mpi

我已定义了一个自定义结构,并希望将其发送给另一个结构 MPI进程使用MPI_Bsend(或MPI_Send)。

这是我的结构:

struct car{
  int shifts;
  int topSpeed;
}myCar;

然而,除了原始类型之外,MPI似乎不支持复杂数据类型的直接“传输”,如上面的结构。我听说我可能不得不使用“序列化”。 我该如何处理它并将“myCar”发送到流程5?

4 个答案:

答案 0 :(得分:56)

Jeremiah是对的 - MPI_Type_create_struct是去这里的方式。

重要的是要记住MPI是一个没有内置于该语言中的库;所以它无法“看到”结构本身序列化的样子。因此,要发送复杂的数据类型,您必须明确定义其布局。在一种对序列化有本机支持的语言中,一组MPI包装器可以巧妙地利用它;例如mpi4py使用python的pickle来透明地发送复杂的数据类型;但是在C中,你必须卷起袖子自己动手。

对于您的结构,它看起来像这样:

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <stddef.h>

typedef struct car_s {
        int shifts;
        int topSpeed;
} car;

int main(int argc, char **argv) {

    const int tag = 13;
    int size, rank;

    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    if (size < 2) {
        fprintf(stderr,"Requires at least two processes.\n");
        exit(-1);
    }

    /* create a type for struct car */
    const int nitems=2;
    int          blocklengths[2] = {1,1};
    MPI_Datatype types[2] = {MPI_INT, MPI_INT};
    MPI_Datatype mpi_car_type;
    MPI_Aint     offsets[2];

    offsets[0] = offsetof(car, shifts);
    offsets[1] = offsetof(car, topSpeed);

    MPI_Type_create_struct(nitems, blocklengths, offsets, types, &mpi_car_type);
    MPI_Type_commit(&mpi_car_type);

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    if (rank == 0) {
        car send;
        send.shifts = 4;
        send.topSpeed = 100;

        const int dest = 1;
        MPI_Send(&send,   1, mpi_car_type, dest, tag, MPI_COMM_WORLD);

        printf("Rank %d: sent structure car\n", rank);
    }
    if (rank == 1) {
        MPI_Status status;
        const int src=0;

        car recv;

        MPI_Recv(&recv,   1, mpi_car_type, src, tag, MPI_COMM_WORLD, &status);
        printf("Rank %d: Received: shifts = %d topSpeed = %d\n", rank,
                 recv.shifts, recv.topSpeed);
    }

    MPI_Type_free(&mpi_car_type);
    MPI_Finalize();

    return 0;
}

答案 1 :(得分:11)

尽管Jonathan Dursi的回答是正确的,但它过于复杂了。 MPI提供更简单,更不通用的类型构造函数,更适合您的问题。当你有不同的基类型(例如,int和float)时,只需要MPI_Type_create_struct

对于您的示例,存在几种更好的解决方案:

  • 假设两个整数在一个连续的内存区域(即整数数组)中对齐,您根本不需要派生的数据类型。只需发送/接收类型为MPI_INT的两个元素,并使用类型为car的变量的地址作为发送/接收缓冲区:

    MPI_Send(&send, 2, MPI_INT, dest, tag, MPI_COMM_WORLD);
    MPI_Recv(&recv, 2, MPI_INT, src, tag, MPI_COMM_WORLD, &status);
    
  • 如果您想使用派生数据类型(例如,为了便于阅读或使用它),您可以使用与数组相对应的MPI_Type_contiguous

    MPI_Type_contiguous(2, MPI_INT, &mpi_car_type);
    
  • 如果两个整数的排列方式不同(很可能不是这种情况,但它依赖于机器并且许多不同平台都存在MPI实现),您可以使用MPI_Type_indexed_block:它需要一个位移数组(如MPI_Type_create_struct),但根据定义,只有一个oldtype参数和每个块的块长度为1:

    MPI_Aint offsets[2];
    offsets[0] = offsetof(car, shifts) ; //most likely going to be 0 
    offsets[1] = offsetof(car, topSpeed);
    MPI_Type_indexed_block(2, offsets, MPI_INT);
    

虽然其他解决方案在语义上是正确的,但是阅读起来要困难得多,并且可能会导致很大的性能损失。

答案 2 :(得分:6)

查看MPI_Type_create_struct为您的对象构建自定义MPI数据类型。使用它的一个例子是http://beige.ucs.indiana.edu/I590/node100.html

答案 3 :(得分:0)

int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)

OpenMPI将从count * sizeof(datatype)开始发送buf个连续字节,以允许发送诸如int数组之类的东西。例如,如果声明一个10 int数组int arr[10],则可以使用

发送
MPI_Send(arr, 10, MPI_INT, 1, 0, MPI_COMM_WORLD);

并收到类似的消息。由于buf是一个空指针,我们可以通过发送sizeof(my_struct)字节并在接收端将其强制转换为结构来滥用此结构。这是一个示例:

#include "mpi.h"
#include <stdio.h>

typedef struct 
{
    char a;
    int b;
    short c;
} my_struct;


int main (int argc, char *argv[])
{
    int  numtasks, taskid;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &taskid);
    MPI_Comm_size(MPI_COMM_WORLD, &numtasks);


    if (taskid == 0) 
    {
        my_struct m;
        m.a = '!';
        m.b = 1234;
        m.c = 5678;

        MPI_Send(&m, sizeof(my_struct), MPI_CHAR, 1, 0, MPI_COMM_WORLD);
    }
    else 
    {
        my_struct m;
        MPI_Recv(&m, sizeof(my_struct), MPI_CHAR, 0, 0, MPI_COMM_WORLD, 
                 MPI_STATUS_IGNORE);
        printf("%c %d %d\n", m.a, m.b, m.c); 
    }

    MPI_Finalize();
}

由于C数组连续存储数据,所以我们甚至可以像malloc an array of structs一样发送结构数组。因此,如果您有my_struct m_array[10],则可以使用

进行发送(和接收)
MPI_Send(m_array, sizeof(my_struct) * 10, MPI_CHAR, 1, 0, MPI_COMM_WORLD);