C中的结构序列化和MPI上的传输

C中的结构序列化和MPI上的传输,c,struct,mpi,C,Struct,Mpi,我已经定义了一个自定义的struct,我需要将它发送给另一个 使用MPI\u Bsend(或MPI\u Send)的MPI进程 以下是结构: struct car{ int shifts; int topSpeed; }myCar; 问题在于,除了基本类型之外,MPI似乎不支持像上面所示的结构那样直接“传输”复杂数据类型。我听说我可能不得不使用“序列化” 我应该如何处理这个问题并成功地将myCar发送到process 5?查看MPI\u Type\u create\u struct为您

我已经定义了一个自定义的
struct
,我需要将它发送给另一个 使用
MPI\u Bsend
(或
MPI\u Send
)的MPI进程

以下是结构:

struct car{
  int shifts;
  int topSpeed;
}myCar;
问题在于,除了基本类型之外,MPI似乎不支持像上面所示的结构那样直接“传输”复杂数据类型。我听说我可能不得不使用“序列化”


我应该如何处理这个问题并成功地将
myCar
发送到process 5?

查看
MPI\u Type\u create\u struct
为您的对象构建自定义MPI数据类型。使用它的一个例子是。

Jeremiah是对的-MPI_Type_create_struct是这里的方法

重要的是要记住,MPI是一个库,而不是内置在语言中;因此,它无法“看到”一个单独序列化的结构是什么样子。因此,要发送复杂的数据类型,必须显式定义其布局。在一种本机支持序列化的语言中,一组MPI包装器可以方便地利用它;例如,利用python透明地发送复杂的数据类型;但在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;
}
#包括
#包括
#包括
#包括
typedef结构车{
整数移位;
内上速度;
}汽车;
int main(int argc,字符**argv){
const int tag=13;
整数大小、等级;
MPI_Init(&argc,&argv);
MPI_通信大小(MPI_通信世界和大小);
如果(尺寸<2){
fprintf(stderr,“至少需要两个进程。\n”);
出口(-1);
}
/*为struct car创建一个类型*/
常数int nitems=2;
int blocklength[2]={1,1};
MPI_数据类型[2]={MPI_INT,MPI_INT};
MPI_数据类型MPI_车辆类型;
MPI_Aint偏移量[2];
偏移量[0]=偏移量(车辆、班次);
偏移量[1]=偏移量(轿厢、最高速度);
MPI类型创建结构(单位、块长度、偏移、类型和MPI类型);
MPI类型提交(&MPI类型);
MPI通信等级(MPI通信世界和等级);
如果(秩==0){
汽车发送;
send.shifts=4;
send.topSpeed=100;
常数int dest=1;
MPI_发送(发送和发送,1,MPI_车辆类型,目的地,标记,MPI_通信世界);
printf(“秩%d:已发送结构\n”,秩);
}
如果(秩==1){
MPI_状态;
常数int src=0;
汽车回收;
MPI_Recv(&Recv,1,MPI_车辆类型,src,标记,MPI_通信世界和状态);
printf(“秩%d:已接收:班次=%d上速度=%d\n”,秩,
调整班次,调整最高速度);
}
无MPI类型(&MPI车辆类型);
MPI_Finalize();
返回0;
}

尽管乔纳森·杜西的答案是正确的,但它过于复杂。MPI提供了更简单、不太通用的类型构造函数,更适合您的问题<只有当您有不同的基类型(例如int和float)时,才需要code>MPI\u Type\u create\u 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\u Type\u continuous

    MPI_Type_contiguous(2, MPI_INT, &mpi_car_type);
    
  • 如果两个整数的对齐方式不同(很可能不是这种情况,但它依赖于机器,并且MPI实现存在于许多不同的平台),您可以使用
    MPI类型索引块
    :它采用一个位移数组(如
    MPI类型创建结构
    ),但只有一个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);
    
虽然另一种解决方案在语义上是正确的,但它更难阅读,并且可能会导致很大的性能损失

int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
OpenMPI将发送从
buf
开始的
count*sizeof(数据类型)
连续字节,以允许发送int数组之类的内容。例如,如果您声明一个10 int数组
int arr[10]
,则可以使用

MPI_Send(arr, 10, MPI_INT, 1, 0, MPI_COMM_WORLD);
并且同样地接受。由于
buf
是一个空指针,我们可以通过发送
sizeof(my_struct)
字节并在接收端强制转换为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();
}

我还是有点困惑。。!假设我定义了MPI结构,现在想使用它。您提供的链接状态为:MPI类型创建结构(5、块长度数组、位移数组、类型数组和新类型);我现在应该做一些像myCar=&new_type这样的事情吗?更重要的是…你能给我一个简单但具体的例子来创建和传输特定的结构吗?问题解决了。您提供的链接提供了所有的“理论”,但由于位移和低级细节,可能很容易让业余程序员感到困惑。然而,它似乎准确地描述了其背后的机制。链接已断开。你能在你的回答中提供一个例子吗?谢谢你非常全面和快速的回答。我真的很感激。你把我完全掩护住了。(但是我认为您忘记在顶部包含,否则编译器会给出错误。)您是对的-offsetof()需要您。我已经适当地更新了代码。这个解决方案的缺点是什么?如果MPI知道数据的结构,它会以不同的方式处理数据吗?如果是,那么如何处理?我不知道MPI如何发送MPI创建的结构类型,但一个合理的实现是发送整个结构,就像我对仅用于计算偏移量的自定义类型所做的那样。发送较小的片段会增加粒度,这可能会加快速度,也可能不会,这取决于每条消息和内容的开销
MPI_Send(m_array, sizeof(my_struct) * 10, MPI_CHAR, 1, 0, MPI_COMM_WORLD);