MPI之通信模式(标准,缓存,同步,就绪)

MPI缓冲区

  • 由MPI自行维护的一块内存区域,也可由用户(MPI_Bsend)自行维护;
  • 发送方 维护一块发送缓冲区; 接收方 维护一块接收缓冲区。

数据收发过程:

在这里插入图片描述

  • 当发送端将数据拷贝到自身的数据缓冲区后(注意这里是拷贝,即数据到达发送缓冲区,再对原数据进行修改将不会影响发送的数据),对应的Send函数将会返回,意味着发送动作已经完成;
  • 当接收端将数据拷贝到自身的接收缓冲区中,Recv函数调用结束。

类似于Socket通信时,调用send将数据放置发送缓冲区即表示send完成。

阻塞通信模型

发送

1. 标准通信模式 MPI_Send 该接口是否进行缓存由MPI决定,分情况讨论

  1. 发送进程的发送动作不依赖接收进程(有缓存):发送进程将数据拷贝到数据缓冲区,不管接收进程有没有执行接收动作,函数都直接返回,发送动作对于用户来讲已经完成;
  2. 发送进程的发送动作依赖于接收进程(不带缓存,直接发送):发送进程发送消息时需要接收进程也要开始接收进程,两者处于一边发送一边接收的状态,此时MPI_Send阻塞当前发送方直到数据被接收方确认收到,基于底层的数据传输机制;
    在这里插入图片描述

2. 缓存通信模式 MPI_Bsend 当用户对上述标准通信模式不满意,不能满足需求,可以采用该模式

该函数在发送消息时使用明确的缓冲区,并具有较低的内存使用率和较高的性能。相对于 MPI_Send 函数,MPI_Bsend 不阻塞发送方,也不会复制消息缓冲区中的数据,而是将数据拷贝到MPI缓冲区中,MPI_Bsend 函数将立即返回,在MPI缓冲区中的消息稍后使用异步方式传输。

函数原型

int MPI_Bsend(const void *buf, int count, 
MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
//用户自行管理缓冲区
int MPI_Buffer_attach(void *buffer,int size)
int MPI_Buffer_detach(void *buffer,int &size)

参数详解

  • buf: 待发送数据的首地址
  • count: 待发送的数据量
  • datatype: 待发送数据类型
  • dest: 目标进程的 MPI rank
  • tag: 消息标记
  • comm: MPI通信域

注意事项
发送时需要将数据从消息缓冲区拷贝到用户提供的缓冲区buffer,该方法消除了发送端同步的开销,如前面分析,消息发送能否进行及能否正确返回不依赖于接收进程。好处是用户可以认为程序需要发送的消息提供缓冲区,但用户也需要负责管理该缓冲区。如果该缓冲区buffer大小不足以存储消息缓冲区中待发送的数据,将导致程序错误退出

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>#define BUFFER_SIZE  1024int main(int argc, char *argv[])
{int rank, size;MPI_Status status;double t0, t1;char buf[BUFFER_SIZE];char *my_buffer;MPI_Request request;MPI_Init(&argc, &argv);MPI_Comm_rank(MPI_COMM_WORLD, &rank);MPI_Comm_size(MPI_COMM_WORLD, &size);if(rank == 0){//为MPI缓冲区分配内存空间my_buffer=(char *)malloc(BUFFER_SIZE);MPI_Buffer_attach(my_buffer, BUFFER_SIZE);snprintf(buf, BUFFER_SIZE, "Hello, world, from rank %d!", rank);t0=MPI_Wtime();//异步发送消息MPI_Bsend(buf, BUFFER_SIZE, MPI_CHAR, 1, 0, MPI_COMM_WORLD, &request);MPI_Wait(&request, &status);t1=MPI_Wtime();printf("Time taken=%f\n", t1-t0);free(my_buffer);MPI_Buffer_detach(&my_buffer, &BUFFER_SIZE);}else{//为MPI缓冲区分配内存空间my_buffer=(char *)malloc(BUFFER_SIZE);MPI_Buffer_attach(my_buffer, BUFFER_SIZE);t0=MPI_Wtime();//等待接收消息MPI_Recv(buf, BUFFER_SIZE, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &status);t1=MPI_Wtime();printf("Rank %d received message=%s, time=%f\n", rank, buf, t1-t0);free(my_buffer);MPI_Buffer_detach(&my_buffer, &BUFFER_SIZE);}MPI_Finalize();return 0;
}

3. 同步通信模式 MPI_Ssend

该模式的开始不依赖接收进程相应的接收操作是否已经启动,但是同步发送却必须等到相应的接收进程开始后才可以正确返回。即

  • 发送方必须等待接收方的确认才能继续执行。
  • 接收方必须收到消息后才能继续执行。
  • 可以确保接收方正确接收消息。

在这里插入图片描述
函数原型

int MPI_Ssend(void* buf, int count, MPI_Datatype datatype, 
int dest, int tag, MPI_Comm comm)

参数详解

  • buf:指向待发送数据的指针。
  • count:发送元素数量。
  • datatype:发送元素的类型。
  • dest:目标进程的进程号。
  • tag:消息标签。
  • comm:进程通信域

代码示例

#include <stdio.h>
#include <mpi.h>int main(int argc, char** argv) {int size, rank;int data;MPI_Status status;MPI_Init(&argc, &argv);MPI_Comm_size(MPI_COMM_WORLD, &size);MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {data = 12345;MPI_Ssend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);printf("Process 0 sent data %d to process 1\n", data);} else if (rank == 1) {MPI_Recv(&data, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);printf("Process 1 received data %d from process 0\n", data);}MPI_Finalize();return 0;
}    

4. 就绪通信模式 MPI_Rsend

该模式只有当接收进程的接收操作已经启动时才可以在发送进程启动发送操作(即是一种发送并立即返回操作),否则,当发送操作启动而相应的操作还没有启动时,发送操作将出错,对于非阻塞发送操作的正确返回,并不意味着发送已经完成,但对于阻塞发送的正确返回,则代表发送缓冲区可以重复使用。

函数原型

int MPI_Rsend(const void *buf, int count, MPI_Datatype datatype, 
int dest, int tag, MPI_Comm comm)

参数详解

  • buf:指向待发送数据的指针。
  • count:发送元素数量。
  • datatype:发送元素的类型。
  • dest:目标进程的进程号。
  • tag:消息标签。
  • comm:进程通信域

代码实例

#include <stdio.h>
#include <mpi.h>int main(int argc, char** argv) {int size, rank;int data;MPI_Status status;MPI_Init(&argc, &argv);MPI_Comm_size(MPI_COMM_WORLD, &size);MPI_Comm_rank(MPI_COMM_WORLD, &rank);if (rank == 0) {data = 12345;MPI_Request request;MPI_Rsend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);printf("Process 0 sent data %d to process 1\n", data);} else if (rank == 1) {MPI_Status status;int data_recv;MPI_Recv(&data_recv, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);printf("Process 1 received data %d from process 0\n", data_recv);}MPI_Finalize();return 0;
}

接收

  • MPI_Recv

非阻塞通信模式

发送

标准通信模式 MPI_Isend

缓存通信模式 MPI_Ibsend

是 MPI_Bsend 的非阻塞版本,在 MPI_Bsend 的基础上,MPI_Ibsend 函数允许发送方在消息发送期间继续执行其他操作,而不必等待消息发送完成。MPI_Ibsend 函数将消息存储在缓冲区中,并返回一个 MPI_Request 对象,代表着消息的发送请求。
函数原型

int MPI_Ibsend(const void* buf, int count, 
MPI_Datatype datatype, int dest, int tag, 
MPI_Comm comm, MPI_Request* request)

参数详解

  • buf:指向待发送数据的指针。
  • count:发送元素数量。
  • datatype:发送元素的类型。
  • dest:目标进程的进程号。
  • tag:消息标签。
  • comm:进程通信域。
  • request:MPI_Request 类型的指针,存储发送请求对象。

代码实例

#include <stdio.h>
#include <mpi.h>#define BUFFER_SIZE 1024int main(int argc, char** argv) {int size, rank;int data;MPI_Status status;char buffer[BUFFER_SIZE];MPI_Request request;MPI_Init(&argc, &argv);MPI_Comm_size(MPI_COMM_WORLD, &size);MPI_Comm_rank(MPI_COMM_WORLD, &rank);if (rank == 0) {data = 12345;MPI_Buffer_attach(buffer, BUFFER_SIZE);MPI_Ibsend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);printf("Process 0 sent data %d to process 1\n", data);MPI_Wait(&request, &status);MPI_Buffer_detach(buffer, &BUFFER_SIZE);} else if (rank == 1) {int data_recv;MPI_Recv(&data_recv, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);printf("Process 1 received data %d from process 0\n", data_recv);}MPI_Finalize();return 0;
}

同步通信模式 MPI_Issend

MPI_Ssend 的非阻塞版本,在 MPI_Ssend 的基础上,MPI_Issend 函数允许发送方在消息发送期间继续执行其他操作,而不必等待消息发送完成。MPI_Issend 函数将消息存储在缓冲区中,并返回一个 MPI_Request 对象,代表着消息的发送请求。
函数原型

int MPI_Issend(const void* buf, int count, 
MPI_Datatype datatype, int dest, int tag, 
MPI_Comm comm, MPI_Request* request)

参数详解

  • buf:指向待发送数据的指针。
  • count:发送元素数量。
  • datatype:发送元素的类型。
  • dest:目标进程的进程号。
  • tag:消息标签。
  • comm:进程通信域。
  • request:MPI_Request 类型的指针,存储发送请求对象。

代码实例

#include <stdio.h>
#include <mpi.h>#define BUFFER_SIZE 1024int main(int argc, char** argv) {int size, rank;int data;MPI_Status status;char buffer[BUFFER_SIZE];MPI_Request request;MPI_Init(&argc, &argv);MPI_Comm_size(MPI_COMM_WORLD, &size);MPI_Comm_rank(MPI_COMM_WORLD, &rank);if (rank == 0) {data = 12345;MPI_Buffer_attach(buffer, BUFFER_SIZE);MPI_Issend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);printf("Process 0 sent data %d to process 1\n", data);MPI_Wait(&request, &status);MPI_Buffer_detach(buffer, &BUFFER_SIZE);} else if (rank == 1) {int data_recv;MPI_Recv(&data_recv, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);printf("Process 1 received data %d from process 0\n", data_recv);}MPI_Finalize();return 0;
}

就绪通信模式 MPI_Irsend

是 MPI_Rsend 的非阻塞版本,在 MPI_Rsend 的基础上,MPI_Irsend 函数允许发送方在消息发送期间继续执行其他操作,而不必等待消息发送完成。MPI_Irsend 函数将消息存储在缓冲区中,并返回一个 MPI_Request 对象,代表着消息的发送请求。
函数原型

int MPI_Irsend(const void* buf, int count, 
MPI_Datatype datatype, int dest, int tag, 
MPI_Comm comm, MPI_Request* request)

参数详解

  • buf:指向待发送数据的指针。
  • count:发送元素数量。
  • datatype:发送元素的类型。
  • dest:目标进程的进程号。
  • tag:消息标签。
  • comm:进程通信域。
  • request:MPI_Request 类型的指针,存储发送请求对象。

代码实例

#include <stdio.h>
#include <mpi.h>#define BUFFER_SIZE 1024int main(int argc, char** argv) {int size, rank;int data;MPI_Status status;char buffer[BUFFER_SIZE];MPI_Request request;MPI_Init(&argc, &argv);MPI_Comm_size(MPI_COMM_WORLD, &size);MPI_Comm_rank(MPI_COMM_WORLD, &rank);if (rank == 0) {data = 12345;MPI_Buffer_attach(buffer, BUFFER_SIZE);MPI_Irsend(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);printf("Process 0 sent data %d to process 1\n", data);MPI_Wait(&request, &status);MPI_Buffer_detach(buffer, &BUFFER_SIZE);} else if (rank == 1) {int data_recv;MPI_Recv(&data_recv, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);printf("Process 1 received data %d from process 0\n", data_recv);}MPI_Finalize();return 0;
}

注,MPI_Irsend 的缓冲区大小由 MPI_BSEND_BUFFER_SIZE 宏定义所决定。如果发送的消息大于缓冲区大小,将会导致 MPI_Irsend 函数阻塞

接收

  • MPI_Irecv

区别:

  1. 是否要对发送的数据进行缓存;(缓存)
  2. 是否只有当接收调用执行后才可以执行发送操作;(同步)
  3. 什么时候发送调用可以正确返回;
  4. 发送调用正确返回是否意味着发送已完成。

几种特殊使用场景

短时间内有大量的短消息的发送

可能会导致缓冲区爆掉,导致消息丢失,用户可能需要使用MPI_Bsend控制缓冲区的使用;
如果短消息可以打包成一个大消息,降低发送次数

发送的数据量特别大

MPI提供的缓冲区的大小不够,可能导致数据丢失,从而导致程序卡,MPI_Bsend模式来直接控制缓冲区的使用;

可以进行分段发送,对数据进程拆分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/64817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity中Shader的帧缓存区Clear(color+Z+stencil)

文章目录 前言一、什么是帧缓冲区二、片段运算三、随机扫描显示器&#xff08;可以按照自定义路径绘制帧&#xff09;四、光栅扫描显示器&#xff08;从左到右&#xff0c;从上到下&#xff0c;依次绘制&#xff09;五、缓冲的方式&#xff1a;单缓冲 和 双缓冲1、单缓冲2、双缓…

oled--SSD1315驱动

OLED 接口方式&#xff08;由硬件电路确定&#xff09;&#xff1a;6800、8080、spi、i2c. 常见的驱动芯片&#xff1a;ssd1306、ssd1315。 oled屏幕的发光原理不同于lcd&#xff0c;上电后无法直接显示&#xff0c;需要初始化后才能正常显示。 SSD1315手册资料 SSD1315是一款…

pom.xml配置文件失效,显示已忽略的pom.xml --- 解决方案

现象&#xff1a; 在 Maven 创建模块Moudle时,由于开始没有正确创建好&#xff0c;所以把它删掉了&#xff0c;然后接着又创建了与一个与之前被删除的Moudle同名的Moudle时&#xff0c;出现了 Ignore pom.xml&#xff0c;并且新创建的 Module 的 pom.xml配置文件失效&#xf…

OpenGL-入门-BMP像素图glReadPixels(1)实现读取屏幕中间的颜色和获取屏幕上鼠标点击位置的颜色

glReadPixels函数用于从帧缓冲区中读取像素数据。它可以用来获取屏幕上特定位置的像素颜色值或者获取一块区域内的像素数据。下面是该函数的基本语法&#xff1a; void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *da…

质谱技术对蛋白质进行鉴定

参考B站教学视频: 质谱如何鉴定蛋白质_哔哩哔哩_bilibili 针对该视频&#xff0c;别人的 笔记 质谱是一台体重秤&#xff0c;称的不是人&#xff0c;而是分子、原子的体重 不同分子有不同分子量是质谱仪工作的底层逻辑 图片来自&#xff1a;【蛋白组】蛋白质组定量技术的原理和…

C++的单例模式

忘记之前有没有写过单例模式了。 再记录一下&#xff1a; 我使用的代码&#xff1a; #ifndef SINGLETON_MACRO_HPP #define SINGLETON_MACRO_HPP#define SINGLETON_DECL(class_name) \ public: \static class_name& instance() { \static class_name s_instance; \return …

【UE 材质】实现方形渐变、中心渐变材质

步骤 一、实现方形渐变 1. 新建一个材质&#xff0c;材质域选择“后期处理” 2. 通过“Mask”节点单独获取R、G通道&#xff0c;可以看到R通道是从左到右0~1之间的变化&#xff0c;对应U平铺 可以看到G通道是从上到下0~1之间的变化&#xff0c;对应V平铺 3. 完善如下节点 二、…

vim 使用/进行关键字搜索,跳转到下一个搜索结果的快捷键

在命令行中使用 / 或 ? 向下或向上搜索指定的字符&#xff0c;字符可手动输入 或 粘贴指定寄存器里的字符输入。通过 n 或 N 继续原方向或反向跳转到下一个匹配到的位置。 此外还可以同时进行多个匹配(关键字)的搜索&#xff0c;不同匹配项之间使用\|隔开即可。只要找到满足这…

里氏替换原则在继承关系中子类对父类方法的重写(覆盖)或重载时应遵循的规则

什么是里氏替换原则&#xff1a;只要父类能出现的地方子类就可以出现&#xff0c;而且 替换为子类也不会产生任何错误或异常&#xff0c;使用者可能根本就不需要知道是父类还是子类。但是&#xff0c;反过来就不行了&#xff0c;有子类出现的地方&#xff0c;父类未必就能适应。…

非关系型数据库Redis的安装

一、关系型数据库与非关系型数据库的区别&#xff1a;---------面试高频率问题 1、首先了解一下 什么是关系型数据库&#xff1f; 关系型数据库最典型的数据结构是表&#xff0c;由二维表及其之间的联系所组成的一个数据组织。 优点&#xff1a; 易于维护&#xff1a;都是使用…

2023年高教社杯数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 最短时…

数据分析作业2

中国在 2020 年开展第七次全国人口普查&#xff0c;截止 2021 年 5 月 11 日普查结果公布&#xff0c;全国人口共1411778724人。单从数据表格看相关数据不够直观&#xff0c;需要进行数据可视化展示&#xff0c;方便查看数据结果。 任务一&#xff1a;链接 MySQL 数据库&#x…

【C++设计模式】依赖倒转原则

2023年8月30日&#xff0c;周三上午 目录 概述含义举个简单的例子传统做法使用依赖倒转原则代码说明再举一个具体的例子以生活为例 概述 依赖倒转原则(Dependency Inversion Principle,DIP)是面向对象设计中的一个基本原则。 含义 高层模块不应该依赖低层模块,两者都应该依…

npm报错sass

1.删除node模块 2.删除node-sass&#xff1a; npm uninstall node-sass 3.重新下载对应版本node-sass&#xff1a; npm i node-sass7.0.3&#xff08;指定版本 控制台报错什么版本就写什么版本&#xff09; 4.再运行项目 或者

jdk-8u371-linux-x64.tar.gz jdk-8u371-windows-x64.exe 【jdk-8u371】 全平台下载

jdk-8u371 全平台下载 jdk-8u371-windows-x64.exejdk-8u371-linux-x64.rpmjdk-8u371-linux-x64.tar.gzjdk-8u371-macosx-x64.dmgjdk-8u371-linux-aarch64.tar.gz 下载地址 迅雷云盘 链接&#xff1a;https://pan.xunlei.com/s/VNdLL3FtCnh45nIBHulh_MDjA1?pwdw4s6 百度…

C语言:截断+整型提升练习

详情关于整型提升与截断见文章&#xff1a;《C语言&#xff1a;整型提升》 一、代码一 int main() { char a -1; signed char b -1; unsigned char c -1; printf("%d %d %d", a, b, c); return 0; } 求输出结果 解析如下代码&#xff1a; int mai…

基于Matlab实现生活中的图像信号分类(附上源码+数据集)

在我们的日常生活中&#xff0c;我们经常会遇到各种各样的图像信号&#xff0c;例如照片、视频、图标等等。对这些图像信号进行分类和识别对于我们来说是非常有用的。在本文中&#xff0c;我将介绍如何使用Matlab来实现生活中的图像信号分类。 文章目录 介绍源码数据集下载 介…

738.单调递增的数字 ,968.监控二叉树 +贪心总结

738.单调递增的数字 示例&#xff1a;如将322变成成单调递增的最大数字即299,如3442变成3399 首先要将n转换成字符数组&#xff0c; 其次遍历数组进行两次判断&#xff1a; 第一次是遍历数字时求当前最大的数字max。然后只在max<arr[i]的时候才更新max和对应的下标idx 第二…

Ajax与jQuery

目录 Ajax是一种异步无刷新的技术 Ajax的优点&#xff1a; 可以无需刷新页面与服务器端进行通信允许根据用户事件来更新部分页面内容 Ajax的缺点&#xff1a; 没有浏览历史&#xff0c;不能回退存在跨域问题&#xff08;同源&#xff09;SEO&#xff08;搜索引擎优化&#x…

Lambda表达式第一版

1、匿名内部类 package com.lambda;import java.util.Arrays; import java.util.Comparator;public class LambdaDemo1 {public static void main(String[] args) {Integer[] arr {2, 3, 1, 5, 6, 7, 8, 4, 9};Arrays.sort(arr, new Comparator<Integer>() {Overridepub…