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

news2025/1/23 4:42:02

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  1024

int 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 1024

int 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 1024

int 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 1024

int 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.coloradmin.cn/o/966043.html

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

相关文章

Redis——认识Redis

简单介绍 Redis诞生于2009年&#xff0c;全称是Remote Dictionary Server&#xff0c;远程词典服务器&#xff0c;是一个基于内存的键值型NoSQL数据库。 特征 键值&#xff08;Key-value&#xff09;型&#xff0c;value支持多种不同数据结构&#xff0c;功能丰富单线程&…

多线程专栏------多线程的实现方式(三)

目录 1、使用线程池1.1、什么是线程池1.2、使用线程池的优点1.3、线程池的核心工作流程1.3、线程池的五种状态生命周期1.3.1、RUNNING1.3.2、SHUTDOWN1.3.3、STOP1.3.4、TIDYING1.3.5、TERMINATED 1.4、创建线程池的方式1.4.1、通过 ThreadPoolExecutor 创建1.4.1.1、线程池的核…

类的静态成员变量 static member

C自学精简教程 目录(必读) 类的静态成员 static member 变量全局只有一份副本&#xff0c;不会随着类对象的创建而产生副本。 static 静态成员 在类的成员变量前面增加static关键字&#xff0c;表示这个成员变量是类的静态成员变量。 #include <iostream> using name…

kaggle赛后总结

1. 宽表 2.缺失值的处理方法 最简单粗暴的就是删除&#xff0c;这种情况是凡是有缺失值行数很少。均值替代。缺失值的行数比较多一点儿的时候&#xff0c;直接删除会影响样本数量&#xff0c;那就均值替代&#xff0c;或者中位数替代等方法。还有复杂的方法&#xff0c;把有缺…

NMS(非极大值抑制)的 Python 实现

文章目录 1. NMS的步骤2. Python代码 非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;NMS&#xff09;是一种在目标检测中常用的技术。 NMS的目的是消除重叠区域中冗余的边界框&#xff0c;并选择最具代表性的目标作为最终结果。通过调整重叠阈值&#xff0c;可…

400电话号码怎么开通

开通400电话是企业提供客户服务的重要步骤。下面是一些步骤和注意事项&#xff0c;帮助您顺利开通400电话。 第一步&#xff1a;选择400电话服务提供商 选择一家可靠的400电话服务提供商非常重要。您可以通过搜索引擎、咨询行业内人士或者参考其他企业的经验来选择合适的服务提…

FLASH读写数据

目录 嵌入式 Flash大概了解 数据手册2.3.2章节 结构图f407 等待周期 Flash 控制寄存器解锁 编程/擦除并行位数 擦除 编程&#xff08;写入&#xff09; 工程程序 嵌入式 Flash大概了解 可以从flash区域启动程序&#xff1b;大概是程序区可以在flash&#xff0c;所以是可以…

python调用git出错:ImportError: Failed to initialize: Bad git executable.

报错信息 #报错信息 Traceback (most recent call last): File “”, line 1, in File “C:\Python27\lib\site-packages\git_init_.py”, line 85, in raise ImportError(‘Failed to initialize: {0}’.format(exc)) ImportError: Failed to initialize: Bad git executab…

春秋云镜 CVE-2018-16283

春秋云镜 CVE-2018-16283 WordPress Plugin Wechat Broadcast LFI 靶标介绍 WordPress Plugin Wechat Broadcast LFI 启动场景 漏洞利用 exp # Exploit Title: WordPress Plugin Wechat Broadcast 1.2.0 - Local File Inclusion # Author: Manuel Garcia Cardenas # Date:…

图论-01-图的基本表示-邻接矩阵和邻接表-Java

文章目录 邻接矩阵邻接表邻接表的问题和改进总结 邻接矩阵 import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Scanner;public class AdjMatrix {private int V;private int E;private int[][] adj;// 构造函数&#xff0c;从文…

利用网络流通过拆点判断图的路径存在性问题:abc318_g

https://atcoder.jp/contests/abc318/tasks/abc318_g 对于图上一类路径是否存在问题&#xff0c;可以考虑网络流。 Trick1 路径存在转网络流 题目转化为&#xff1a; 找出两条不交路径 B->A, B->C 对于已经找到的路径&#xff0c;我们不能再走。对于当前我们找到的某条…

Matlab图像处理-幂次变换

幂次变换 如下图所示的幂次变换函数曲线图&#xff1a; 当γ <1时&#xff0c;效果和对数变换相似&#xff0c;放大暗处细节&#xff0c;压缩亮处细节&#xff0c;随着数值减少&#xff0c;效果越强。 当γ >1时&#xff0c;放大亮处细节&#xff0c;压缩暗处细节&…

云备份——配置信息及获取配置信息类模块

一&#xff0c;配置信息 使用文件配置加载一些程序的运行关键信息&#xff0c;如ip&#xff0c;端口等&#xff0c;可以让程序的运行更加灵活 我们需要的配置信息如下 IP地址端口号热点判断时间&#xff0c;也就是非热点文件的时间要求文件下载的URL前缀路径&#xff0c;用于表…

Matlab图像处理-

对数变换 对数变换的一项主要应用是压缩动态范围。一些特别的图像在实际显示中&#xff0c;高灰度值部分较占优势&#xff0c;而低灰度值的可见细节部分丢失。通过计算对数&#xff0c;如10的动态范围会降至14左右[即 ln1013.8]&#xff0c;这样就更易于处理。 对数变换就是压…

Pygame中Trivia游戏解析6-4

3.3.3 显示题目选项 在显示题目选项时&#xff0c;有三种情况&#xff1a;分别是用户还未选择答案时&#xff1b;用户的答案是正确时&#xff1b;用户的答案是错误时。 &#xff08;1&#xff09;用户还未选择答案时 此时&#xff0c;用白色显示四个备选答案&#xff0c;如图…

数据工厂-生成接口通用用例

章节目录&#xff1a; 一、背景介绍二、前置准备三、设计思路四、代码具体实现五、执行效果六、其他说明七、结束语 一、背景介绍 有哪些用例是可以通用且固定的&#xff1f; 针对之前提到的接口用例设计思路&#xff0c;拆分为三个切入点&#xff1a; 举个例子&#xff1a; {…

【原创】H3C三层交换机VLAN路由

网络拓扑图 VLAN 配置 VLAN 100 VLAN 200 [H3C]int vlan 100 ip address 1.1.1.1 255.255.255.0[H3C-Vlan-interface100]int vlan 200 ip address 2.2.2.1 255.255.255.0[H3C]int GigabitEthernet 1/0/1port access vlan 100[H3C]int GigabitEthernet 1/0/2port access vlan 2…

R语言中缺失值的处理

目录 一.寻找缺失值 1.complete.cases() 2.manyNAs 二.缺失值的处理 1.直接删除 2.填补缺失值 一.寻找缺失值 1.complete.cases() #会展现缺失值 algae[!complete.cases(algae),] 2.manyNAs > manyNAs(algae) [1] 62 199 #表示第62条和第199条都有很多缺失值>m…

文本标注技术方案(NLP标注工具)

Doccano doccano 是一个面向人类的开源文本注释工具。它为文本分类、序列标记和序列到序列任务提供注释功能。您可以创建用于情感分析、命名实体识别、文本摘要等的标记数据。只需创建一个项目&#xff0c;上传数据&#xff0c;然后开始注释。您可以在数小时内构建数据集。 支持…

基于springboot跟redis实现的排行榜功能(实战)

概述 前段时间&#xff0c;做了一个世界杯竞猜积分排行榜。对世界杯64场球赛胜负平进行猜测&#xff0c;猜对1分&#xff0c;错误0分&#xff0c;一人一场只能猜一次。 1.展示前一百名列表。 2.展示个人排名(如&#xff1a;张三&#xff0c;您当前的排名106579)。 一.redis so…