MPI 入门

MPI代表Message Passing Interface(消息传递接口),是一种用于在并行计算中进行通信的标准。MPI是一组库和规范,允许多个处理单元(通常是计算集群或超级计算机中的节点)在并行执行的程序之间进行通信和数据传输。

​ MPI的主要目标是提供一个标准的、可移植的编程接口,使得开发者能够方便地编写并行程序,而不用担心底层硬件和通信机制的细节。MPI支持点对点通信和集体通信,使得并行程序可以在不同的处理单元之间交换信息,协同完成任务。

​ 总结一下,mpi的任务:

  • 消息传递

  • 并行计算

  • 标准化接口

  • 分布式计算

  • 科学和工程计算


    MPI常用函数

    一以下代码在Ubuntu下进行的操作

MPI_Init 和 MPI_Finalize

​ 初始化MPI 环境和结束MPI 环境,也就是MPI代码需要在这2个期间使用,实例代码如下:

//001.c
#include <stdio.h>
#include <mpi.h>
int main(int argc, char **argv)
{
	int numprocs;
	MPI_Init(&argc, &argv);

	//your code here
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	//end of your code

	printf("Hello World! The number of processes is %d\n",numprocs);

	MPI_Finalize();
	return 0;
}
mpicc -o a 001.c #编译
mpicc -np 4 ./a 开启4核,并且运行

结果如下:

在这里插入图片描述


MPI_Comm_rank 和 MPI_Comm_size

MPI_Comm_rank :用于获取调用进程在指定通信组(MPI_Comm)中的秩(rank)。秩是一个在通信组内唯一标识进程的整数值。每个进程都可以调用 MPI_Comm_rank 函数来获得自己在通信组中的秩。

​ 函数原型如下:

int MPI_Comm_rank(MPI_Comm comm, int *rank);

​ 其中,comm 表示 一个通信组,一般用MPI_COMM_WORLD ,它表示所有MPI进程的通信组。

rank :是一个指向整数的指针,用于存储调用进程在通信组中的秩。

MPI_Comm_size : 是 MPI(Message Passing Interface)库中的一个函数,用于获取指定通信组(MPI_Comm)中的进程总数。这个函数返回通信组中的进程数量。

​ 函数原型如下:

int MPI_Comm_size(MPI_Comm comm, int *size);

示例如下:

//002.c
#include <stdio.h>
#include <mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	MPI_Init(&argc, &argv);

    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    
	//your code here
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
	//end of your code

	printf("Hello World!I'm rank %d of %d\n", myid, numprocs);

	MPI_Finalize();
	return 0;
}
	

结果如下:

在这里插入图片描述


MPI_SendMPI 和 MPI_Recv

MPI_Send : 点对点,从 MPI 中一个进程,发送到另一个进程中。原型如下:

int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);
  • buf:发送数据的缓冲区的起始地址。
  • count:发送的数据项数量。
  • datatype:发送数据项的数据类型。
  • dest:目标进程的秩(rank)。
  • tag:消息标签,用于标识消息的类型。
  • comm:通信组,通常使用 MPI_COMM_WORLD,表示所有 MPI 进程的默认通信组。

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:通信组,通常使用 MPI_COMM_WORLD,表示所有 MPI 进程的默认通信组。
  • status:一个包含接收操作的状态信息的结构体,可以设置为 MPI_STATUS_IGNORE,表示不关心状态信息。

以下是一个简单的例子:

//003.c
#include <mpi.h>
#include <stdio.h>

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);

    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    if (my_rank == 0) {
        int data = 42;
        MPI_Send(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
    } else if (my_rank == 1) {
        int received_data;
        MPI_Recv(&received_data, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        printf("Process 1 received data: %d\n", received_data);
    }

    MPI_Finalize();
    return 0;
}

​ 在003.c 代码中,rank(0) 会将43发送到rank(1)中的received_data 中

结果如下:

origin_url=C%3A%5CUsers%5C17001%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20231210211724448.png&pos_id=img-9O0s754I-1702218603328)


MPI_Bcast 和 MPI_Scatter

MPI_Bcast:是一种集体通信操作,用于向通信组中的所有进程广播数据,也就是将一个进程的数据传递给所有其他进程,使得每个进程都能获得相同的数据。

函数原型如下:

int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm);
  • buffer:要广播的数据的缓冲区的起始地址。
  • count:广播的数据项数量。
  • datatype:广播的数据项的数据类型。
  • root:广播操作的根进程的秩(rank),该进程的数据将被广播到其他所有进程。
  • comm:通信组,通常使用 MPI_COMM_WORLD,表示所有 MPI 进程的默认通信组。

以下是一个简单的例子:

//004.c
#include <mpi.h>
#include <stdio.h>

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);

    int my_rank, data;

    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    if (my_rank == 0) {
        data = 42;
    }

    // 使用 MPI_Bcast 将进程0中的 data 广播到所有进程
    MPI_Bcast(&data, 1, MPI_INT, 0, MPI_COMM_WORLD);

    printf("Process %d has data: %d\n", my_rank, data);

    MPI_Finalize();
    return 0;
}

结果如下:
在这里插入图片描述

​ 但并不需要显式调用 MPI_Bcast 函数来接收数据,因为这个函数会在所有进程中同步执行。

在 MPI_Bcast 中,广播源(根进程)的数据会被复制到所有其他进程的指定缓冲区,因此所有进程都有相同的数据。

​ 注意:在这个例子中,每个进程在调用 MPI_Bcast 后都可以直接使用 data 变量,因为 MPI_Bcast 会将根进程的数据复制到每个进程的 data 变量中。在这里,每个进程的 data 变量都将包含值为 42 的数据。

所以,接收进程不需要额外的步骤来获取数据,它们可以直接使用 MPI_Bcast 同步到的数据。


MPI_Gather

MPI_Gather:一种集体通信操作,用于收集通信组中所有进程的数据到一个进程中。通常,这些数据是在每个进程中生成或计算的,然后通过 MPI_Gather 被收集到根进程中。

int MPI_Gather(const void *sendbuf, int sendcount, MPI_Datatype sendtype,
               void *recvbuf, int recvcount, MPI_Datatype recvtype,
               int root, MPI_Comm comm);

  • sendbuf:要发送的数据的缓冲区的起始地址。
  • sendcount:每个发送进程中发送块中的数据项数量。
  • sendtype:发送进程中发送数据项的数据类型。
  • recvbuf:接收数据的缓冲区的起始地址(只在根进程中有效)。
  • recvcount:根进程中接收块中的数据项数量。
  • recvtype:根进程中接收的数据项的数据类型。
  • root:进行收集的根进程的秩(rank)。
  • comm:通信组,通常使用 MPI_COMM_WORLD,表示所有 MPI 进程的默认通信组。

以下是一个简单的例子:

//005.c
#include <mpi.h>
#include <stdio.h>

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);

    int my_rank, data;
    int gathered_data[4]; // 4进程,每个进程有一个数据项

    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    data = my_rank + 1;

    // 使用 MPI_Gather 将每个进程中的数据收集到根进程中
    MPI_Gather(&data, 1, MPI_INT, gathered_data, 1, MPI_INT, 0, MPI_COMM_WORLD);

    if (my_rank == 0) {
        printf("Root process received data: ");
        for (int i = 0; i < 4; ++i) {
            printf("%d ", gathered_data[i]);
        }
        printf("\n");
    }

    MPI_Finalize();
    return 0;
}

​ 结果…很奇怪,似乎 MPI_Gather 有提前同步的功能…

在这里插入图片描述


MPI_Reduce

MPI_Reduce用于在通信组中的所有进程之间进行全局归约(reduction)操作。归约操作是指将多个进程中的局部数据合并成一个全局结果。

函数原型如下:

int MPI_Reduce(const void *sendbuf, void *recvbuf, int count,
               MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm);

  • sendbuf:要归约的局部数据的缓冲区的起始地址,每个进程的数据。
  • recvbuf:接收归约结果的缓冲区的起始地址,只在根进程中有效。
  • count:每个进程中的数据项数量。
  • datatype:数据项的数据类型。
  • op:归约操作,例如 MPI_SUM 表示求和,MPI_MAX 表示求最大值,等等。可以使用其他的 MPI_Op 常量,也可以自定义归约操作。
  • root:进行归约操作的根进程的秩(rank)。
  • comm:通信组,通常使用 MPI_COMM_WORLD,表示所有 MPI 进程的默认通信组。

以下是一个简单的例子,演示如何使用 MPI_Reduce 进行全局求和:

//006.c
#include <mpi.h>
#include <stdio.h>

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);

    int my_rank, data;
    int global_sum;

    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // 假设每个进程有一个整数数据项
    data = my_rank + 1;

    // 使用 MPI_Reduce 将所有进程中的数据项进行求和
    MPI_Reduce(&data, &global_sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);

    // 根进程输出全局求和结果
    if (my_rank == 0) {
        printf("Root process received global sum: %d\n", global_sum);
    }

    MPI_Finalize();
    return 0;
}

结果如下:

在这里插入图片描述


MPI_Barrier

MPI_Barrier:一种同步操作,用于在通信组中的所有进程之间创建同步点。它确保所有调用该函数的进程都达到了同步点,然后继续执行。

函数原型如下:

int MPI_Barrier(MPI_Comm comm);

comm:通信组,通常使用 MPI_COMM_WORLD,表示所有 MPI 进程的默认通信组。

注意MPI_Barrier 在调用它的所有进程中创建一个同步点。当一个进程调用 MPI_Barrier 时,它会等待所有其他进程也调用了 MPI_Barrier,然后所有进程同时继续执行。

以下是一个简单的例子,演示 MPI_Barrier 的用法:

//007.c
#include <mpi.h>
#include <stdio.h>

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);

    int my_rank;

    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // 所有进程在此同步点
    MPI_Barrier(MPI_COMM_WORLD);

    printf("Process %d reached the barrier\n", my_rank);

    // 所有进程在此同步点
    MPI_Barrier(MPI_COMM_WORLD);

    printf("Process %d passed the barrier\n", my_rank);

    MPI_Finalize();
    return 0;
}

在这里插入图片描述

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dai _ tu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值