【MPI】 六大金刚:六个最基本、最常用的函数
首先,安装MPI看这里。
这6个函数API如下:
/* Initialize and Finalize */
int MPI_Init(int *argc, char ***argv)
int MPI_Finalize()
/* MPI comm size and rank */
int MPI_Comm_size(MPI_Comm comm, int *size)
int MPI_Comm_rank(MPI_Comm comm, int *rank)
/* MPI send and recv */
int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest,
int tag, MPI_Comm comm)
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag,
MPI_Comm comm, MPI_Status *status)
先简要介绍一下 rank
(进程序号)和 communicator
(通信域):
Rank
- 定义:在 MPI 中,
rank
是一个整数值,用来唯一标识一个进程在特定通信子中的位置。 - 作用:rank 使得进程能够被唯一地识别,从而可以在通信时指定具体的发送者或接收者。
- 获取方式:通过
MPI_Comm_rank
函数可以获得当前进程在某个通信子中的 rank。
Communicator
- 定义:
communicator
是一组进程的集合,这些进程共享一个通信上下文,可以相互通信。 - 作用:定义了哪些进程可以相互通信,并提供了这些进程间通信的环境。
- 默认通信子:
MPI_COMM_WORLD
是最常见的默认通信子,它包含了启动 MPI 程序的所有进程。 - 获取大小:通过
MPI_Comm_size
函数可以知道某个通信子中包含了多少个进程。
简而言之,rank 是进程在通信子中的编号,而 communicator 是一组进程及其通信规则的集合。这两个概念是 MPI 编程中进程间通信的基础。
MPI_Init
MPI_Init
用于初始化 MPI 环境,是 MPI 程序开始执行的第一步。它负责设置 MPI 库,使进程能够参与到 MPI 通信中来。
函数原型
int MPI_Init(int *argc, char ***argv);
参数
argc
:指向命令行参数计数的指针。通常,这是传给主函数main
的第一个参数。argv
:指向命令行参数数组的指针。通常,这是传给主函数main
的第二个参数。
返回值
- 成功时返回
MPI_SUCCESS
。 - 发生错误时返回一个负的错误码。
说明
MPI_Init
可能会修改 argc
和 argv
的值。在 MPI_Init
调用后,argc
和 argv
应被视为未定义,除非它们在 MPI_Init
内部没有被改变。
MPI_Finalize
MPI_Finalize
用于关闭 MPI 环境,释放由 MPI 库分配的所有资源,并确保所有未完成的通信请求都已完成。
函数原型
int MPI_Finalize();
返回值
- 成功时返回
MPI_SUCCESS
。 - 发生错误时返回一个负的错误码。
说明
在 MPI_Finalize
被调用之后,除了 MPI_Get_processor_name
和 MPI_Finalized
之外,不应再调用任何其他 MPI 函数。一旦 MPI_Finalize
被调用,MPI 环境就无法再次初始化。
MPI_Comm_size
MPI_Comm_size
用于查询给定通信子中的进程数量。
函数原型
int MPI_Comm_size(MPI_Comm comm, int *size);
参数
comm
:要查询的通信子。size
:一个指向整数的指针,用于存储通信子中的进程数量。
返回值
- 成功时返回
MPI_SUCCESS
。 - 发生错误时返回一个负的错误码。
MPI_Comm_rank
MPI_Comm_rank
用于查询调用进程在给定通信子中的标识符(rank)。
函数原型
int MPI_Comm_rank(MPI_Comm comm, int *rank);
参数
comm
:要查询的通信子。rank
:一个指向整数的指针,用于存储调用进程在通信子中的标识符。
返回值
- 成功时返回
MPI_SUCCESS
。 - 发生错误时返回一个负的错误码。
MPI_Send
MPI_Send
用于将数据从一个进程发送到另一个进程。其函数原型如下:
int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);
参数
buf
:指向要发送的数据缓冲区的指针。count
:要发送的数据项数量。datatype
:每个数据项的类型(例如MPI_INT
对应整型)。dest
:目标进程的 rank(在通信子中的标识符)。tag
:消息标签,用于区分不同类型的消息。comm
:通信子,定义了发送和接收进程之间的上下文。
返回值
- 成功时返回
MPI_SUCCESS
。 - 如果出现错误,则返回一个负的错误码
MPI_Recv
MPI_Recv
用于从另一个进程接收数据。其函数原型如下:
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status);
参数
buf
:指向用于存放接收到的数据的缓冲区的指针。count
:接收缓冲区能够容纳的数据项的最大数量。datatype
:每个数据项的类型。source
:发送进程的 rank。如果设置为MPI_ANY_SOURCE
,则可以从任意源接收消息。tag
:期望的消息标签。如果设置为MPI_ANY_TAG
,则可以接收任何标签的消息。comm
:通信子。status
:一个指向MPI_Status
结构体的指针,用来提供关于接收到的消息的信息(如源进程rank和实际接收到的数据项数量)。如果不关心这些信息,可以传递MPI_STATUS_IGNORE
。
返回值
- 成功时返回
MPI_SUCCESS
。 - 如果出现错误,则返回一个负的错误码。
刚开始不用太在意细节,知道大概怎么用,什么效果即可。
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
int main(int argc, char** argv) {
/*
// 初始化
MPI_Init(NULL, NULL);
int rank;
// 获取进程标识,但是PID(进程ID)是运行态概念,而MPI进程则是个静态概念,所以MPI把标识抽象为0~np-1
// MPI_COMM_WORLD表示默认通信域
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
long long sum = 0;
for (long long i = 0; i < 10000000000; i++) sum += i;
int size;
// 获取通信域中包含多少进程
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("I'm rank #%d total %d :Hello world! %lld\n",rank,size,sum);
// 结束
MPI_Finalize();
*/
// ---------------通信----------------
MPI_Init(NULL, NULL);
int tag = 10086;
int count = 1;
MPI_Status status;
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
int* sendbuff = (int*)malloc(sizeof(int) * count);
sendbuff[0] = 1;
MPI_Send(sendbuff, count, MPI_INT, 1, tag, MPI_COMM_WORLD);
printf("I'm rank #%d, sendbuff[0] = %d\n", rank, sendbuff[0]);
} else if (rank == 1) {
int* recvbuff = (int*)malloc(sizeof(int) * count);
recvbuff[0] = 200;
printf("I'm rank #%d , before recv, recvbuff[0] = %d\n", rank, recvbuff[0]);
MPI_Recv(recvbuff, count, MPI_INT, 0, tag, MPI_COMM_WORLD, &status);
printf("I'm rank #%d , after recv, recvbuff[0] = %d\n", rank, recvbuff[0]);
}
MPI_Finalize();
return 0;
}