MPI


MPI(Message passing Interface) 消息传递接口
利用MPI就可以再集群中实现大规模的数据交换来解决单节点计算力不足的问题

基本概念(Concept)

MPI中的基本名词概念

进程(Process)
一个程序(program),是由多个在相同或不同计算机节点上运行的进程(process)或线程(thread)构成,在MPI程序中,将一个能够独立参与通讯的个体称之为一个进程

进程组(Group of processes)
顾名思义,就是多个进程的集合,为了区分每个进程,在进程组中会用到序号(rank)来标识每一个进程,序号范围从0开始到进程的数量-1。 其中rank 0 一般被作为主程序(master)。

通信器(communicator, 在代码中为 MPI_Comm)
通信器是用来连接MPI中的进程组的,是由通信器为每一个进程提供独立的标识符,MPI所有通信必须在某个通信器中进行。
MPI所能理解的有两种通信方式单组内部通信操作(域内通信single group intracommunicator) 和 双边内部通信(域间bilateral intercommunicator communication),前者用于同一进程中进程间的通信,后者用于分属不同进程的进程间的通信。
MPI系统在一个MPI程序运行时会自动创建两个通讯器
MPI_COMM_WORLD,包含MPI程序中所有进程
MPI_COMM_SELF,指单个进程自己所构成的通信器

序号/秩(Rank)
进程的标识,用来区分不同的进程。MPI 的进程由进程组/序号或通信器/序号唯一确定。

消息(Message)
MPI 程序中在进程间传递的数据。它由通信器、源地址、目的地址、消息标签和数据构成。

通信(communication)
通信是指在进程之间进行消息的收发、同步等操作。

MPI基本函数/接口

总共有6个函数,分成3种类型

  1. 开始和结束MPI的接口: MPI_Init , MPI_Finalize

  2. 获取进程状态的接口: MPI_Comm_rank , MPI_Comm_size

  3. 传输数据的接口: MPI_Send , MPI_Recv

  4. MPI_Init(int* argc ,char** argv[] )
    初始化MPI执行环境通常也是第一个被调用的MPI函数,建立多个MPI进程之间的联系,为后续通信做准备。

  5. MPI_Finalize(void)
    结束MPI执行环境。 表明并行代码的结束,结束除主进程外其它进程。
    串行代码仍可在主进程(rank = 0)上运行, 但不能再有MPI函数(包括MPI_Init())。

  6. MPI_Comm_size(MPI_Comm communicator ,int* size)
    用来标识相应进程组中有多少个进程,获得进程个数size
    获得通信子communicator中规定的group包含的进程的数量

  7. MPI_Comm_rank(MPI_Comm communicator ,int* rank)
    用来标识各个MPI进程的,给出调用该函数的进程的进程号,返回整型的错误值。两个参数:MPI_Comm类型的通信域,标识参与计算的MPI进程组; &rank返回调用进程中的标识号相当于进程的ID。

  8. MPI_Send(void *buff, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)

  • void *buff:发送缓冲区的起始地址,可以是数组或结构指针,也就是要发送的变量只不过可能是个地址指向变量所在位置;
  • int count:非负整数,想要发送的消息个数(注意:不是长度,例如你要发送一个int整数,这里就填写1,如要是发送“hello”字符串,这里就填写6(C语言中字符串未有一个结束符,需要多一位))。;
  • MPI_Datatype datatype:发送数据的数据类型,只不过必须是MPI定义的数据类型;
  • int dest:整型,目的进程的进程号;
  • int tag:整型,消息标志,接收方需要有相同的信息标签才能接受该消息。 如果A需要发给B多种消息,B就可以通过tag定位特定信息,其余信息就会被缓存起来
  • MPI_Comm comm:MPI进程组所在的通信域, 表示要向哪个组发送信息

含义:向通信域中的dest进程发送数据,数据存放在buf中,类型是datatype,个数是count,这个消息的标志是tag,用以和本进程向同一目的进程发送的其它消息区别开来。
在这里插入图片描述

  1. MPI_Recv(void *buff, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
    source:整型,指定接收数据的来源,所以是发送数据进程的进程号;
    tag: 需要与发送方tag值相同才能接受该消息,接收方可以通过tag来筛选数据
    status:消息状态。MPI_Status结构指针,接收函数返回时,将在这个参数指示的变量中存放实际接收消息的状态信息,包括消息的源进程标识,消息标签,包含的数据项个数等。

MPI_Status 结构体
如果接收端status的参数不为MPI_STATUS_IGNORE的话,那么MPI_Status会储存以下三个数据
1. 发送端秩. 发送端的秩存储在结构体的 MPI_SOURCE 元素中。也就是说,如果我们声明一个 MPI_Status stat 变量,则可以通过 stat.MPI_SOURCE 访问秩。
2. 消息的标签. 消息的标签可以通过结构体的 MPI_TAG 元素访问(类似于 MPI_SOURCE
3. 消息的长度. 消息的长度在结构体中没有预定义的元素。相反,我们必须使用 MPI_Get_count 找出消息的长度

MPI_Get_count(
    MPI_Status* status,
    MPI_Datatype datatype,
    int* count)

MPI_Status的用处是,当接收端以MPI_ANY_SOURCEMPI_ANY_TAG作为参数的话,这种情况下只能通过MPI_Status来找出消息的实际发送方和标签。
此外,并不能保证 MPI_Recv 能够接收函数调用参数的全部元素。 相反,它只接收已发送给它的元素数量(如果发送的元素多于所需的接收数量,则返回错误。) MPI_Get_count 函数用于确定实际的接收量。

const int MAX_NUMBERS = 100;
int numbers[MAX_NUMBERS];
int number_amount;
if (world_rank == 0) {
   
    // 选择随机数量的数传给进程1
    srand(time(NULL));
    number_amount = (rand() / (float)RAND_MAX) * MAX_NUMBERS;
    MPI_Send(numbers, number_amount, MPI_INT, 1, 0, MPI_COMM_WORLD);
    printf("0 sent %d numbers to 1\n", number_amount);
} else if (world_rank == 1) {
   
    MPI_Status status;
    // 从进程0中说到MAX_NUMBERS个数,实际收到数量会小于MAX_NUMBERS的个数
    MPI_Recv(numbers, MAX_NUMBERS, MPI_INT, 0, 0, MPI_COMM_WORLD,
             &status);

    // 再收到信息后,通过status来判断实际收到的数量
    MPI_Get_count(&status, MPI_INT, &number_amount);

    // 打印收到的数量,和存储再status里的其他数据也就是消息来源和tag
    printf("1 received %d numbers from 0. Message source = %d, "
           "tag = %d\n",
           number_amount, status.MPI_SOURCE, status.MPI_TAG);
}
>>> cd tutorials
>>> ./run.py check_status
mpirun -n 2 ./check_status
0 sent 92 numbers to 1
1 received 92 numbers from 0. Message source = 0, tag = 0

MPI_Status的功能是在实际收到消息之后再查询消息大小,如果想要在实际接受消息之前就能查询大小我们就要用MPI_Probe

MPI_Probe(
    int source,
    int tag,
    MPI_Comm comm,
    MPI_Status* status)

MPI_Recv 类似,MPI_Probe 将阻塞具有匹配标签和发送端的消息。 当消息可用时,它将填充 status 结构体。 然后,用户可以使用 MPI_Recv 接收实际的消息

int number_amount;
if (world_rank == 0) {
   
    const int MAX_NUMBERS = 100;
    int numbers[MAX_NUMBERS];
    // 随机选择发送数据的数量
    srand(time(NULL));
    number_amount = (rand() / (float)RAND_MAX) * MAX_NUMBERS;
    MPI_Send(numbers, number_amount, MPI_INT, 1, 0, MPI_COMM_WORLD);
    printf("0 sent %d numbers to 1\n", number_amount);
} else if (world_rank == 1) {
   
    MPI_Status status;
    // 再接受前先调用MPI_Probe来获取发送端传的数据数量
    MPI_Probe(0, 0, MPI_COMM_WORLD, &status);

    // 当probe返回时,就可以调用status来判断数量
    MPI_Get_count(&status, MPI_INT, &number_amount);

    // 然后可以根据数量来动态的分配缓存区域大小
    int* number_buf = (int*)malloc(sizeof(int) * number_amount);

    // 现在就可以开始接受发送端的数据
    MPI_Recv(number_buf, number_amount, MPI_INT, 0, 0,
             MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    printf("1 dynamically received %d numbers from 0.\n",
           number_amount);
    free(number_buf);
}

在这里插入图片描述

MPI简单示例(打印每一个进程)

#include <stdio.h>
#include <string.h>
#include "mpi.h"
void main(int argc, char* argv[])
{
   
    int numprocs, myid, source;
    MPI_Status status;
    char message[100];
    MPI_Init(&argc, &argv); //初始化开始MPI,分配进程号
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);	
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);  
    if 
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值