MPI 在Windows11 和 VS2019 安装、测试、基本使用

MPI 在Windows11 上安装,使用VS2019编译生成可执行程序

MPI安装与测试

系统环境:
  Windows11
  64bit
  VS2019

1. 下载并安装MPICH For Windows
进入http://www.mpich.org/downloads/站点根据操作系统下载。由于我们使用的是Windows,拉到下载网页最底部,最新的MPICH实现已经由微软官网托管,我们直接进去下载。

在这里插入图片描述
然后,选择最新的V8下载,包含两个文件:msmpisdk.msi和MSMpiSetup.exe。
在这里插入图片描述

下载完毕直接分别安装这两个程序 msmpisdk.msi 和 MSMpiSetup.exe
我安装在了D盘:
在这里插入图片描述
在这里插入图片描述

2. VS配置以及demo演示

新建一个VC++项目
在这里插入图片描述在项目属性中配置修改如下:
  包含目录里面添加:C:\Program Files (x86)\Microsoft SDKs\MPI\Include;
  库目录的里面添加:C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x64;
 在这里插入图片描述
其他必要配置:
  配置管理器,选择x64编译平台;
  C/C++ -> 预处理器,添加:MPICH_SKIP_MPICXX;
  C/C++ -> 代码生成 -> 运行库,选择:多线程调试(/MTd);
  链接器 -> 输入 -> 附加依赖项,添加:msmpi.lib;
  在这里插入图片描述
 demo测试,新建一个C++文件,命名为main.cpp

#include<stdio.h>
#include<mpi.h>
#include<stdlib.h>
#include<time.h>

int main(int argc, char* argv[])
{
    int myid, numprocs, namelen;
    char processor_name[MPI_MAX_PROCESSOR_NAME];

    MPI_Init(&argc, &argv);        // starts MPI
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);  // get current process id
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);      // get number of processes
    MPI_Get_processor_name(processor_name, &namelen);

    if (myid == 0) printf("number of processes: %d\n...", numprocs);
    printf("%s: Hello world from process %d \n", processor_name, myid);

    MPI_Finalize();

    return 0;
}

编译整个项目,将编译(1.项目右键重新生成 或者 2.使用编辑器编译得到)得到的 exe文件(debug文件夹下)放在安装的bin目录(默认为:C:\Program Files\Microsoft MPI\Bin 我安装在了D盘)下,在这个Bin目录下按住shift键于空白处右键单击,打开命令行窗口,输入 mpiexec -n 10 Project1.exe 得到运行结果,如下图:
在这里插入图片描述
在这里插入图片描述

MPI 基本使用

说到并行计算,我们有一个不可绕开的话题——MPI编程。MPI是一个跨语言的通讯协议,用于编写并行计算机。支持点对点和广播。

MPI基本函数

MPI调用借口的总数虽然庞大, 但根据实际编写MPI的经验, 常用的MPI调用的个数确什么有限。 下面是6个最基本的MPI函数。
1.  MPI_Init(…);
2.  MPI_Comm_size(…);
3.  MPI_Comm_rank(…);
4.  MPI_Send(…);
5.  MPI_Recv(…);
6.  MPI_Finalize();
我们在此通过一个简单的例子来说明这6个MPI函数的基本用处。

函数介绍
1. int MPI_Init (int* argc ,char** argv[] )

该函数通常应该是第一个被调用的MPI函数用于并行环境初始化,其后面的代码到 MPI_Finalize()函数之前的代码在每个进程中都会被执行一次。
–  除MPI_Initialized()外, 其余所有的MPI函数应该在其后被调用。
–  MPI系统将通过argc,argv得到命令行参数(也就是说main函数必须带参数,否则会出错)。

2. int MPI_Finalize (void)

–  退出MPI系统, 所有进程正常退出都必须调用。 表明并行代码的结束,结束除主进程外其它进程。
–  串行代码仍可在主进程(rank = 0)上运行, 但不能再有MPI函数(包括MPI_Init())。

3. int MPI_Comm_size (MPI_Comm comm ,int* size )

–  获得进程个数 size。
–  指定一个通信子,也指定了一组共享该空间的进程, 这些进程组成该通信子的group(组)。
–  获得通信子comm中规定的group包含的进程的数量。

4. int MPI_Comm_rank (MPI_Comm comm ,int* rank)

–  得到本进程在通信空间中的rank值,即在组中的逻辑编号(该 rank值为0到p-1间的整数,相当于进程的ID。)

5. int 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:消息标签,接收方需要有相同的消息标签才能接收该消息。
–MPI_Comm comm:通讯域。表示你要向哪个组发送消息。

在这里插入图片描述

6. int MPI_Recv( void *buff, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)

–void *buff:你接收到的消息要保存到哪个变量里。
–int count:你接收消息的消息的个数(注意:不是长度,例如你要发送一个int整数,这里就填写1,如要是发送“hello”字符串,这里就填写6(C语言中字符串未有一个结束符,需要多一位))。它是接收数据长度的上界. 具体接收到的数据长度可通过调用MPI_Get_count 函数得到。
–MPI_Datatype datatype:你要接收的数据类型,这里需要用MPI定义的数据类型,可在网上找到,在此不再罗列。
–int dest:接收端进程号,你要需要哪个进程接收消息就填写接收进程的进程号。
–int tag:消息标签,需要与发送方的tag值相同的消息标签才能接收该消息。
–MPI_Comm comm:通讯域。
–MPI_Status *status:消息状态。接收函数返回时,将在这个参数指示的变量中存放实际接收消息的状态信息,包括消息的源进程标识,消息标签,包含的数据项个数等。

示例

基本函数都已经介绍完,现在我们来用一个示例来加强对这些基本函数的理解。

#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);//初始化,必须通过argc和argv初始化,也就是说main函数必须带参数
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);//得到进程在进程组中的编号
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);//获得进程个数

    if (myid != 0) {  //非0号进程发送消息
        strcpy(message, "Hello World!");
        MPI_Send(
            message, strlen(message) + 1, MPI_CHAR, 
            0, //接收端进程号
            99, //消息标签
            MPI_COMM_WORLD //通信域
        );
    }
    else {   // myid == 0,即0号进程接收消息
        for (source = 1; source < numprocs; source++) //遍历所有非0主进程的进程
        {
            MPI_Recv(
                message, 
                100, 
                MPI_CHAR, 
                source, //接收端进程号
                99,//消息标签,需要与发送方的tag值相同的消息标签才能接收该消息
                MPI_COMM_WORLD, //通讯域
                &status//接收函数返回时,将在这个参数指示的变量中存放实际接收消息的状态信息,包括消息的源进程标识,消息标签,包含的数据项个数等
            );
            printf("接收到第%d号进程发送的消息:%s\n", source, message);
        }
    }
    MPI_Finalize();
} /* end main */

运行结果如下图所示
在这里插入图片描述
可以看到,当笔者开启四线程运行时,1-3号进程发送消息,0号进程接收到消息并打印;当笔者开启八线程运行时,1-7号进程发送消息,0号进程接收到消息并打印。在这里插入图片描述
本文使用的是标准阻塞接收发送的方式。消息传递是MPI的特性,也是我们学习的难点。这我们学习MPI必须掌握的。

消息发送与接收函数的参数的一些重要说明。

1.MPI标识一条消息的信息包含四个域:
–  源:发送进程隐式确定,进程rank值唯一标识.
–  目的:Send函数参数确定.
–  Tag:Send函数参数确定, (0,UB) 232-1.
–  通信子:缺省MPI_COMM_WORLD
•  Group:有限/N, 有序/Rank [0,1,2,…N-1]
•  Contex:Super_tag,用于标识该通讯空间.

2. buffer的使用
buffer必须至少可以容纳count个由datatype指明类型的数据. 如果接收buf太小, 将导致溢出、 出错

3. 消息匹配
–  参数匹配source,tag,comm/dest,tag,comm.
–  Source == MPI_ANY_SOURCE: 接收任意处理器来的数据(任意消息来源).
–  Tag == MPI_ANY_TAG: 匹配任意tag值的消息(任意tag消息).

4. 在阻塞式消息传送中不允许Source == dest,否则会导致死锁.
5. 消息传送被限制在同一个通信域内。
6. 在send函数中必须指定唯一的接收者。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AI_潜行者

赐予我力量吧

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

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

打赏作者

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

抵扣说明:

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

余额充值