C中的结构序列化并通过MPI传输
Posted
技术标签:
【中文标题】C中的结构序列化并通过MPI传输【英文标题】:struct serialization in C and transfer over MPI 【发布时间】:2012-04-09 12:10:09 【问题描述】:我已经定义了一个自定义struct
,我需要将它发送给另一个
MPI 进程使用MPI_Bsend
(或MPI_Send
)。
这是结构:
struct car
int shifts;
int topSpeed;
myCar;
问题在于,除了原始类型之外,MPI 似乎不支持像上面显示的结构那样直接“传输”复杂数据类型。我听说我可能不得不使用“序列化”。
我应该如何处理这个问题并成功地将myCar
发送到进程 5?
【问题讨论】:
【参考方案1】: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;
【讨论】:
感谢您非常全面和快速的回复。对此,我真的非常感激。你已经完全覆盖了我。 (但是我认为您忘记将shifts
,是一个数组,应该如何更改你的示例? (int shifts[2];
)【参考方案2】:
虽然 Jonathan Dursi 的答案是正确的,但它过于复杂。 MPI 提供了更简单、更通用的类型构造函数,更适合您的问题。 MPI_Type_create_struct
仅在您具有不同的基本类型(例如,int 和 float)时才需要。
对于您的示例,存在几个更好的解决方案:
假设这两个整数在一个连续的内存区域中对齐(即,就像一个整数数组),您根本不需要派生数据类型。只需使用car
类型变量的地址发送/接收两个MPI_INT
类型的元素,用作发送/接收缓冲区:
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);
虽然另一种解决方案在语义上是正确的,但它更难阅读,并且可能会导致很大的性能损失。
【讨论】:
【参考方案3】:查看MPI_Type_create_struct
为您的对象构建自定义 MPI 数据类型。使用它的一个例子是http://beige.ucs.indiana.edu/I590/node100.html。
【讨论】:
我还是有点迷茫..!假设我定义了 MPI 结构,现在想使用它。您提供的链接状态: MPI_Type_create_struct(5, array_of_block_lengths, array_of_displacements, array_of_types, &new_type);我现在应该做类似 myCar=&new_type 的事情吗? 还有更重要的...请给我一个创建和传输特定结构的简单但具体的例子吗? 问题已解决。您提供的链接提供了所有“理论”,但由于位移和低级细节可能很容易使业余程序员感到困惑。然而,它似乎准确地描述了其背后的机制。 链接已损坏。你能在你的回答中举个例子吗?【参考方案4】:int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
OpenMPI 将发送从 buf
开始的连续字节 count * sizeof(datatype)
以允许发送诸如 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);
【讨论】:
这个解决方案的缺点是什么?如果 MPI 知道数据的结构,它会以不同的方式处理数据吗?如果是,那么如何处理? 我不知道 MPI 如何发送 MPI 创建的结构类型,但一个合理的实现是发送整个结构,就像我使用仅用于计算偏移量的自定义类型一样。发送较小的片段会增加粒度,这可能会也可能不会加快速度,具体取决于每条消息的开销以及缓冲区大小等因素。以上是关于C中的结构序列化并通过MPI传输的主要内容,如果未能解决你的问题,请参考以下文章
C ++ MPI创建并发送具有字段char [16]和整数的结构数组
使用 MPI_Type_create_struct() 在 C 中传输包含动态数组的结构