MPI并行编程——多进程程序设计

2023-10-29

MPI(Massage Passing Interface),它不是一种语言,而是一种库描述,是消息传递函数库的标准规范。MPI标准定义了一组具有可移植性的编程接口,在Fortran和C/C++中可以直接对相应的函数进行调用。

MPI有很多种实现。MPICH是最重要的MPI实现之一,它与其衍生产品构成了世界上使用最广泛的MPI实现,在超级计算机中也得到了广泛的应用。

MPI最基本的消息传递操作包括:发送消息send、接受消息receive、进程同步barrier、归约reduction等。本文对发送消息接受消息作简要的介绍,如要了解更多MPI的相关知识,请参考其他相关资料。

一、MPI基本概念与相关的函数解释

通信器(communicator)

所谓通信器,可以理解为一类进程的集合,也可以称之为一个进程组。一个进程组中的进程可以相互通信。所有MPI通信都必须在某个通信器内进行。MPI系统提供缺省的通信器MPI_COMM_WORLD,所有启动的MPI进程通过调用函数MPI_Init()包含在该通信器内。各个进程可以通过函数MPI_Comm_size()获取通信器所包含的的MPI进程个数。一个通信器内的所有进程都拥有一个该通信器内中唯一的序号(rank)用作自身的标识( 序号的取值范围是[0,通信器进程数-1]

int MPI_Comm_size(MPI_Comm comm, int* size)
//获取通信器的进程数,comm为通信器名称(也叫通信域),获取得到的进程个数结果被放在size中

进程序号(rank)

rank用来在一个通信器中标识一个进程,同一个进程在不同的通信器中可以有不同的序号,进程的序号是在通信器被创建时赋予的。

int MPI_Comm_rank(MPI_Comm comm, int* rank)
//获取进程在通信器中的标号,comm为通信器名称,获取得到的进程id被放在rank中

消息(message)

不同进程之间通过消息传递数据,因此MPI可以应用于分布存储系统。消息发送与接收函数的参数可以分为数据(data)和信封(envelope)两个部分。包装由进程序号、消息标号和通信器三部分组成;

int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
//消息发送函数,参数可以分为数据、信封
//数据:<地址(buffer的地址),数据个数(count),数据类型(datatype)>
//信封:<目的(dest),标识(tag),通信域(comm)>
//count指定数据类型的个数、datatype为数据类型、dest取值范围是0~(进程总数-1);tag 取值范围是0~MPI_TAG_UB,用来区分消息
    
int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source,int tag, MPI_Comm comm, MPI_Status *status)
//接收buffer必须至少可以容纳count个由datatype参数指明类型的数据. 如果接收buf太小, 将导致溢出。
//消息匹配机制:
//1.参数匹配 dest,tag,comm 分别与 source,tag,comm相匹配;
//2.若 Source 为 MPI_ANY_SOURCE:接收任意处理器来的数据。
//3.若Tag为MPI_ANY_TAG:则匹配任意tag值的消息
//在阻塞式消息传送中不允许Source==Dest,否则会导致死锁。
//消息传送被限制在同一个通信域中。
//在send函数中必须指定唯一的接收者

int MPI_Get_count(MPI_Status status, MPI_Datatype datatype, int*count)
//查询接收到的消息长度,该函数在count中返回数据类型的个数,即消息的长度(count属于MPI_Status结构的一个域,但不能被用户直接访问)

需要注意:

  1. 发送进程需指定一个有效的目标接收进程

  1. 接收进程需指定一个有效的源发送进程

  1. 接收和发送消息的进程要在同一个通信器内

  1. 接收和发送消息的tag要相同

  1. 接收缓存区要足够大

二、MPI环境配置

2.1 gcc/g++环境配置

Ubuntu默认没有安装gcc/g++等编译环境。为简化环境配置,可以使用build-essential软件包。该软件包内包含了gcc/g++/gfortran等编译器.

sudo apt install build-essential

2.2 mpich环境配置

可直接使用如下命令安装mpich。

sudo apt install mpich

2.3 版本信息查看

通过在命令行输入以下信息查看程序版本

gcc版本查看(C语言编译器)

gcc -v

g++版本查看(C++编译器)

g++ -v

mpicc版本查看(使用C语言编写的MPI程序,需要使用mpicc进行编译)

mpicc -v

mpic++版本查看(使用C++编写的MPI程序,需要使用mpic++进行编译)

mpic++ -v

三、MIP的编译与运行

3.1程序的编译

使用以下命令可以编译mpi程序:

使用C语言编写的MPI程序:
     mpicc -o XXX XXX.cpp

使用C++编写的MPI程序:
     mpic++ -o XXX XXX.cpp

3.2 程序的运行

mpi程序的运行:

mpirun -np Y ./XXX
//  Y表示的是一个数字,代表并行运行的进程数目。
//  XXX表示源程序文件

3.3 HelloWorld示例程序

#include <iostream>
#include "mpi.h"
//使用了MPI程序需要包含mpi.h头文件
using namespace std;

int main(int argc, char** argv)
{
    MPI_Init( &argc , &argv);
    cout<<"Hello world!"<<endl;
    MPI_Finalize();
    return 0;
}

该程序只是使每个进程输出hello world!运行结果如下:

四、MPI数据类型(与C语言对应)

MPI 数据类型

C语言数据类型

MPI_CHAR

char

MPI_SHORT

short int

MPI_INT

int

MPI_LONG

long

MPI_FLOAT

float

MPI_DOUBLE

double

MPI_LONG_DOUBLE

long double

MPI_BYTE

MPI_PACKED

五、MPI基本函数

int MPI_Init(int *argc, char **argv)
//初始化函数。MPI_INIT是MPI程序的第一个调用,完成MPI程序的所有初始化工作,启动MPI环境,标志并行代码的开始,因此要求main函数必须带参数运行

int MPI_Finalize(void)
//退出/结束函数。它是MPI程序的最后一个调用,结束MPI程序的运行,是MPI程序的最后一条可执行语句。它标志并行代码的结束,结束除主进程外其它进程
    
int MPI_Initialized(int* flag)
//允许在MPI_Init前使用的函数,检测MPI系统是否已经初始化


int MPI_Get_processor_name(char* name, int* resultlen)
//获取处理器的名称,在返回的name中存储所在处理器的名称,resultlen存放返回名字所占字节,应提供参数name不少于MPI_MAX_PRCESSOR_NAME个字节的存储空间

double MPI_Wtime(void)
//返回调用时刻的墙上时间,用浮点数表示秒数,墙上时钟时间wall clock time,也叫时钟时间,是指从进程从开始运行到结束,时钟走过的时间,这其中包含了进程在阻塞和等待状态的时间。用来计算程序运行时间。

六、几个简单示例程序

6.1 Print

输出每个进程的信息和运行时间

#include <iostream>
#include "mpi.h"
using namespace std;

int main(int argc, char** argv)
{
    int pid, pnum,namelen;
    double starttime;
    char processor_name[MPI_MAX_PROCESSOR_NAME];

    MPI_Init( &argc , &argv);
    //初始化
    starttime=MPI_Wtime();
    //开始时间
    MPI_Comm_size( MPI_COMM_WORLD , &pnum);
    //获取通信域内进程个数
    MPI_Comm_rank( MPI_COMM_WORLD , &pid);
    //获取本进程的rank
    MPI_Get_processor_name( processor_name , &namelen);
    //获取processor_name
    cout<<"this is "<<pid<<" of "<<pnum<<" and my name is "<< processor_name <<endl;
    cout<<"this is "<<pid<<" and spend ";
    printf("%.6lf s\n",MPI_Wtime()-starttime);
    //输出信息
    MPI_Finalize();
    //结束并退出

    return 0;
}

命令行:

mpic++ -o print.o print.cpp

mpirun -np 4 ./print.o

6.2 π的计算

#include <iostream>
#include "mpi.h"
using namespace std;



int main(int argc, char**argv)
{
    //mpirun -np 4 calculatePI.o 800   其中的800是以参数的形式传入的,位于argv[1]
    long double pi=0, answer=0, PI=3.141592653589793238462643383279;
    int size, id, namelen,n=1000;
    double time;
    char processor_name[MPI_MAX_PROCESSOR_NAME];
    MPI_Status status;
    //开始计时
    time=MPI_Wtime();

    //如果参数列表中制定了n的值,则将该值赋给n
    if(argc==2)sscanf(argv[1], "%d", &n);
    MPI_Init(&argc, &argv);
    //获取进程信息
    MPI_Comm_size( MPI_COMM_WORLD , &size);
    MPI_Comm_rank( MPI_COMM_WORLD , &id);
    
    //比较n和size大小,若n过小,则返回
    if(n<size)
    {
        cout<<"输入n值过小, 请重新输入"<<endl;
        MPI_Finalize();
        return 0;
    }

    for(int i=id; i<=n; i+=size)
    {
        long double tempans;
        tempans = (long double)1/(long double)(2*i+1);
        if(i%2==0)
        {
            answer+=tempans;
        }
        else
        {
            answer-=tempans;
        }
    }
    
    //如果是主进程
    if(id==0)
    {
        long double recvbuf;
        pi=answer;
        for(int i=1; i<size; i++)
        {
            MPI_Recv( &recvbuf , 1 , MPI_LONG_DOUBLE , MPI_ANY_SOURCE , 0 , MPI_COMM_WORLD , &status);
            pi+=recvbuf;
        }
    }
    else//发送消息并退出程序
    {
        MPI_Send( &answer , 1 , MPI_LONG_DOUBLE , 0 , 0 , MPI_COMM_WORLD);
        MPI_Finalize();
        return 0;
    }
    


    pi*=4;
    cout<<"主进程使用"<<MPI_Wtime()-time<<"秒, 最后得到PI的计算结果为: ";
    printf("%.20Lf\n",pi); 
    cout<<"n = "<<n<<" 使用了 "<<MPI_Wtime()-time <<"s pi = ";
    printf("%.20Lf  %.20Lf \n",pi,abs(PI-pi)); 
    MPI_Finalize();
    return 0;
}

命令行:

mpic++ -o calculatePI.o calculatePI.cpp

mpirun -np 1 calculatePI.o 200000000

mpirun -np 4 calculatePI.o 200000000

运行结果为:

如有不当或错误之处,恳请您的指正,谢谢!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MPI并行编程——多进程程序设计 的相关文章

  • 将复选框添加到 UniformGrid

    我正在尝试将复选框动态添加到 wpf 中的统一网格中 但看起来网格没有为它们分配足够的空间 所以它们都有点互相重叠 这就是我将它们添加到后面的代码中的方法 foreach string folder in subfolders PathCh
  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • 如何在列表框项目之间画一条线

    我希望能够用水平线分隔列表框中的每个项目 这只是我用于绘制项目的一些代码 private void symptomsList DrawItem object sender System Windows Forms DrawItemEvent
  • C++ 子字符串返回错误结果

    我有这个字符串 std string date 20121020 我正在做 std cout lt lt Date lt lt date lt lt n std cout lt lt Year lt lt date substr 0 4 l
  • Newtonsoft JSON PreserveReferences处理自定义等于用法

    我目前在使用 Newtonsoft Json 时遇到一些问题 我想要的很简单 将要序列化的对象与所有属性和子属性进行比较以确保相等 我现在尝试创建自己的 EqualityComparer 但它仅与父对象的属性进行比较 另外 我尝试编写自己的
  • WPF 中的调度程序和异步等待

    我正在尝试学习 WPF C 中的异步编程 但我陷入了异步编程和使用调度程序的困境 它们是不同的还是在相同的场景中使用 我愿意简短地回答这个问题 以免含糊不清 因为我知道我混淆了 WPF 中的概念和函数 但还不足以在功能上正确使用它 我在这里
  • C - 找到极限之间的所有友好数字

    首先是定义 一对友好的数字由两个不同的整数组成 其中 第一个整数的除数之和等于第二个整数 并且 第二个整数的除数之和等于第一个整数 完美数是等于其自身约数之和的数 我想做的是制作一个程序 询问用户一个下限和一个上限 然后向他 她提供这两个限
  • C#:如何防止主窗体过早显示

    在我的 main 方法中 我像往常一样启动主窗体 Application EnableVisualStyles Application SetCompatibleTextRenderingDefault false Application
  • C 预处理器库

    我的任务是开发源分析工具C程序 并且我需要在分析本身之前预处理代码 我想知道什么是最好的图书馆 我需要一些重量轻 便于携带的东西 与其推出自己的 为什么不使用cpp这是的一部分gcc suite http gcc gnu org onlin
  • Cython 和类的构造函数

    我对 Cython 使用默认构造函数有疑问 我的 C 类 Node 如下 Node h class Node public Node std cerr lt lt calling no arg constructor lt lt std e
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • 如何将图像路径保存到Live Tile的WP8本地文件夹

    我正在更新我的 Windows Phone 应用程序以使用新的 WP8 文件存储 API 本地文件夹 而不是 WP7 API 隔离存储文件 旧的工作方法 这是我如何成功地将图像保存到 共享 ShellContent文件夹使用隔离存储文件方法
  • 将 unsigned char * (uint8_t *) 转换为 const char *

    我有一个带有 uint8 t 参数的函数 uint8 t ihex decode uint8 t in size t len uint8 t out uint8 t i hn ln for i 0 i lt len i 2 hn in i
  • 实体框架 4 DB 优先依赖注入?

    我更喜欢创建自己的数据库 设置索引 唯一约束等 使用 edmx 实体框架设计器 从数据库生成域模型是轻而易举的事 现在我有兴趣使用依赖注入来设置一些存储库 我查看了 StackOverflow 上的一些文章和帖子 似乎重点关注代码优先方法
  • 将 xml 反序列化为类,list<> 出现问题

    我有以下 XML
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • WCF:将随机数添加到 UsernameToken

    我正在尝试连接到用 Java 编写的 Web 服务 但有些东西我无法弄清楚 使用 WCF 和 customBinding 几乎一切似乎都很好 除了 SOAP 消息的一部分 因为它缺少 Nonce 和 Created 部分节点 显然我错过了一
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • C 中的异或运算符

    在进行按位操作时 我在确定何时使用 XOR 运算符时遇到一些困难 按位与和或非常简单 当您想要屏蔽位时 请使用按位 AND 常见用例是 IP 寻址和子网掩码 当您想要打开位时 请使用包含或 然而 XOR 总是让我明白 我觉得如果在面试中被问

随机推荐

  • CA 厂商排名

    1 NDS 2 Irdeto 3 Nagravision 4 Verimatrix 5 Widevine 6 Latens 7 Viaccess 8 Secure Media
  • 关于跑demo遇到的flask mysql navicat 导入包的解决方式

    Q1 导入demo时的第一步 打开pycharm 左上角 之后 点击settings 进入settings后 点击Project下的python interpreter 此时 右侧的python interpreter显示的是no inte
  • ctfshow web14

    题目描述 无 解题思路 这道题比较简单 分值也只有5分 就是一个简单的sql注入 但是这个sql注入的回显你得看它的源代码里才有 但是它把你右击查看源码那个玩意儿给禁了 你需要在你的url前面加view source 才能看到源码 解题过程
  • 数据库结构对比工具 支持 SqlServer ,Oracle,MySql 相互对比同步转换 源代码生成,Word表格生成Model ,文本格式化,差异对比

    数据库结构对比工具 支持 SqlServer Oracle MySql 相互对比同步 QQ群 434053880 有最新版本下载 1 CSDN 下载链接 不过要积分下载 SqlServer Oracle MySql数据库结构相互对比同步 m
  • 注意力机制学习(二)——空间注意力与pytorch案例

    文章目录 一 空间注意力机制简介 二 空间注意力与pytorch代码 三 使用案例 一 空间注意力机制简介 空间注意力的示意图如下 长条的是通道注意力机制 而平面则是空间注意力机制 可以发现 通道注意力在意的是每个特怔面的权重 空间注意力在
  • Spring框架Security(认证)快速上手

    在处理Spring安全框架时 通常可以选择Shiro或者Security 做认证授权加密等 推荐非SpringBoot 使用Shiro SpringBoot项目使用Security 学习网址 Security Shiro 目录 1 Spri
  • BUUCTF WEB [极客大挑战 2019]Secret File

    BUUCTF WEB 极客大挑战 2019 Secret File 启动后效果如下 F12查看源代码
  • unity中的碰撞和触发事件

    首先 unity中两个游戏对象发生碰撞的条件 1 两个游戏对象必须都有Collider碰撞器这个组件 2 至少有一个游戏对象包含刚体组件 3 两个游戏对象有相对运动 还应该知道跟碰撞事件相关的3个函数 void OnColliderEnte
  • 【Python】近似熵,样本熵,模糊熵计算高效版

    文章目录 前言 整体思路 1 近似熵 Approximate Entropy ApEn 1 1 理论基础 1 2 python第三方库实现 1 3 基于多线程numpy矩阵运算实现 2 样本熵 Sample Entropy SampEn 2
  • 在React中使用Typescript

    使用命令创建项目 生成一个全新的 ts react 的模版 可直接使用指令 npm create react app my app template typescript 该模板包含了全套正常运行React所需要的的包和配置 无需再额外手动
  • 若依RuoYi-Vue代码学习一---通用分页处理

    文章目录 一 先运行看看接口到sql 二 来看看若依怎么处理的分页 三 最后回到接口 及其参数返回 一 先运行看看接口到sql 随便找个表格看看 可以看到传入了 分页关键属性 看看debug的日志打印的sql debug 137 gt Pr
  • 小程序酷炫3D登录页源码(泥陶态)

    小程序酷炫3D登录页源码 泥陶态 1 页面效果 登陆页面一般都要酷炫好看一点 这里分享一个泥陶态3D登录页面 泥陶态是结合 3D 设计流行风格而兴起的新形态 设计趋势由拟物风格发展为扁平风格时 去掉了一切表示深度和层叠的效果 虽然视觉上简化
  • android中Manifest.xml中的intent-filter作用

    注意 隐式启动Activity的intent到底发给哪个activity 需要进行三个匹配 一个是action 一个是category 一个是data 可以是全部或部分匹配 同样适用于Service和BroadcastReceiver 下面
  • lgvl菜单项优化

    项目场景 嵌入式CPU ASR3603S MCU 24MB ROM 16MB RAM 问题描述 功能菜单 设置菜单 菜单列表上下滑动有些慢 LVGL菜单滑动过程中卡顿感 原因分析 代码执行耗时 图片加载耗时 缓冲区太小多次刷新耗时 解决方案
  • 关于微信小程序获取头像和昵称

    不知道为什么微信一直对开发者获取 微信头像 微信昵称 一直抱以限制态度 关于接口调用方法 也是一直在修改 open type getUserInfo 在2021年4月13日停用 wx getUserInfo 在2021年4月28日停用 wx
  • 矢量网络分析仪(矢网)组成和原理简介

    一 概述 矢量网络分析仪是一种常见的射频测量仪器 主要用来测量高频器件 电路及系统的性能参数 如线性参数 非线性参数 变频参数等 1 1 分类 矢量网络分析仪一般以频率来划分 截止频率越高 价格也越贵 根据测试端口的数量可分为 双端口 3
  • oracle重新编译package,这个package为什么编译不了?该怎么解决

    SQL codecreate or replace package pack sunyard test is type refcur is ref cursor 函数名称 func sunyard test 作者 sunyard zheng
  • 阶段性学习总结

    前 言 从开学到现在 时间已过去两个月之久 这段时间 一直断断续续做js学习研究 有时候浪费了不少时间 还疑惑不断 下面就这段时间学习状况 以及相关的生活习惯做关联总结 javascripty以下简称 javascript 总体 在总结之前
  • 后台登陆万能密码总结

    1 Asp Aspx万能密码 or or or 1 1 or 1 1 or a a or a a or a a or a a or 1 1 a or 1 1 or a a or 1 or 1 1 1 1 or 1 1 or 1 1 OR 1
  • MPI并行编程——多进程程序设计

    MPI Massage Passing Interface 它不是一种语言 而是一种库描述 是消息传递函数库的标准规范 MPI标准定义了一组具有可移植性的编程接口 在Fortran和C C 中可以直接对相应的函数进行调用 MPI有很多种实现