C++ Boost MPI接口详细讲解

   2023-02-09 学习力0
核心提示:目录一、说明二、开发和运行时环境三、简单数据交换一、说明Boost.MPI 提供了 MPI 标准(消息传递接口)的接口。该标准简化了并发执行任务的程序的开发。您可以使用线程或通过共享内存或网络连接使多个进程相互通信来开发此类程序。 MPI 的优点是你不需要关心

一、说明

Boost.MPI 提供了 MPI 标准(消息传递接口)的接口。该标准简化了并发执行任务的程序的开发。您可以使用线程或通过共享内存或网络连接使多个进程相互通信来开发此类程序。 MPI 的优点是你不需要关心这些细节。您可以完全专注于并行化您的程序。

缺点是您需要 MPI 运行时环境。如果您控制运行时环境,MPI 只是一个选项。例如,如果你想分发一个可以通过双击启动的程序,你将无法使用 MPI。虽然操作系统开箱即用地支持线程、共享内存和网络,但它们通常不提供 MPI 运行时环境。用户需要执行额外的步骤来启动 MPI 程序。

  • 开发和运行时环境
  • 简单的数据交换
  • 异步数据交换
  • 集体数据交换

二、开发和运行时环境

MPI 定义了用于并行计算的函数。并行计算是指在支持任务并行执行的运行时环境中可以并发执行任务的程序。这样的运行时环境通常基于多个处理器。由于单个处理器只能顺序执行代码,因此链接多个处理器会创建一个可以并行执行任务的运行时环境。如果连接了数千个处理器,结果就是一台并行计算机——一种通常只在超级计算机中才能找到的架构。 MPI 来自于寻找更容易地为超级计算机编程的方法的搜索。

如果你想使用 MPI,你需要一个标准的实现。虽然 MPI 定义了许多功能,但它们通常不受开箱即用的操作系统支持。例如,Windows 的桌面版本不附带 MPI 支持。

最重要的 MPI 实现是 MPICH 和 Open MPI。 MPICH 是最早的 MPI 实现之一。它自 1990 年代中期就已存在。 MPICH 是一种成熟且可移植的实现,并得到积极维护和更新。 Open MPI 的第一个版本于 2005 年发布。由于 Open MPI 是一项协作成果,其中包括许多负责早期 MPI 实现的开发人员,因此 Open MPI 被视为未来的标准。然而,这并不意味着可以忽略 MPICH。有几种基于 MPICH 的 MPI 实现。例如,Microsoft 发布了一个名为 Microsoft HPC Pack 的 MPI 实现,它基于 MPICH。

MPICH 为各种操作系统(如 Windows、Linux 和 OS X)提供安装文件。如果您需要 MPI 实现并且不想从源代码构建它,MPICH 安装文件是开始使用 MPI 的最快途径。

MPICH 安装文件包含开发 MPI 程序所需的头文件和库。此外,它们还包含一个 MPI 运行时环境。因为 MPI 程序同时在多个处理器上执行任务,所以它们在多个进程中运行。一个 MPI 程序会启动多次,而不仅仅是一次。同一 MPI 程序的多个实例在多个处理器上运行,并通过 MPI 标准定义的函数进行通信。

您无法通过双击启动 MPI 程序。您使用一个帮助程序,通常称为 mpiexec。您将 MPI 程序传递给 mpiexec,它会在 MPI 运行时环境中启动您的程序。命令行选项确定启动了多少个进程以及它们如何通信——例如,通过套接字或共享内存。因为 MPI 运行时环境会处理这些细节,所以您可以专注于并行编程。

如果您决定使用 MPICH 的安装文件,请注意 MPICH 仅提供 64 位版本。您必须使用 64 位编译器通过 MPICH 开发 MPI 程序并构建 64 位版本的 Boost.MPI。

三、简单数据交换

Boost.MPI 是 MPI 标准的 C++ 接口。该库使用命名空间 boost::mpi。包含头文件 boost/mpi.hpp 就足以访问所有类和函数。

示例 47.1。 MPI 环境和通信器

#include <boost/mpi.hpp>
#include <iostream>
int main(int argc, char *argv[])
{
  boost::mpi::environment env{argc, argv};
  boost::mpi::communicator world;
  std::cout << world.rank() << ", " << world.size() << '\n';
}

Example47.1

示例 47.1 是一个简单的 MPI 程序。它使用两个类,您将在随后的所有示例中找到它们。 boost::mpi::environment 初始化 MPI。构造函数从 MPI 标准调用函数 MPI_Init()。析构函数调用 MPI_Finalize()。 boost::mpi::communicator 用于创建通信器。通信器是 MPI 的核心概念之一,支持进程之间的数据交换。

boost::mpi::environment 是一个非常简单的类,只提供了几个成员函数。您可以调用 initialized() 检查 MPI 是否已成功初始化。成员函数返回一个 bool 类型的值。 processor_name() 以 std::string 的形式返回当前进程的名称。 abort() 会停止 MPI 程序,而不仅仅是当前进程。您将一个 int 值传递给 abort()。该值将作为 MPI 程序的返回值传递到 MPI 运行时环境。对于大多数 MPI 程序,您不需要这些成员函数。您通常在程序开始时实例化 boost::mpi::environment,然后不使用该对象——如示例 47.1 和本章中的以下示例。

boost::mpi::communicator 更有趣。此类是一个通信器,它链接作为 MPI 程序一部分的进程。每个进程都有一个等级,它是一个整数——所有进程都会被枚举。进程可以通过在通信器上调用 rank() 来发现其等级。如果进程想知道有多少个进程,它会调用 size()。

要运行示例 47.1,您必须使用您正在使用的 MPI 实现提供的帮助程序。对于 MPICH,辅 助程序称为 mpiexec。您可以通过以下命令使用此帮助程序运行示例 47.1:

mpiexec-n4sample.exe

mpiexec 需要一个 MPI 程序的名称和一个告诉它要启动多少进程的选项。选项 -n 4 告诉 mpiexec 启动四个进程。因此 MPI 程序启动了四次。但是,这四个过程并不是独立的。它们通过 MPI 运行时环境链接,并且它们都属于同一个通信器,这给每个进程一个等级。如果您使用四个进程运行示例 47.1,rank() 返回一个从 0 到 3 的数字和 size() 4。

请注意,输出可能会混淆。毕竟,有四个进程同时写入标准输出流。例如,不知道排名为 0 或任何其他排名的进程是否是第一个写入标准输出流的进程。也有可能一个进程在写入标准输出流时会中断另一个进程。在另一个进程写入标准输出流之前,被中断的进程可能无法完成写入其等级和通信器的大小,从而破坏输出。

示例 47.2。发送和接收数据的阻塞函数

#include <boost/mpi.hpp>
#include <iostream>
int main(int argc, char *argv[])
{
  boost::mpi::environment env{argc, argv};
  boost::mpi::communicator world;
  if (world.rank() == 0)
  {
    int i;
    world.recv(1, 16, i);
    std::cout << i << '\n';
  }
  else if (world.rank() == 1)
  {
    world.send(0, 16, 99);
  }
}

boost::mpi::communicator 提供了两个简单的成员函数,send() 和 recv(),用于在两个进程之间交换数据。它们是阻塞函数,仅在发送或接收数据时才返回。这对于 recv() 尤其重要。如果在没有其他进程向其发送数据的情况下调用 recv(),调用将阻塞并且进程将在调用中停止。

在示例 47.2 中,等级为 0 的进程使用 recv() 接收数据。等级为 1 的进程使用 send() 发送数据。如果你用两个以上的进程启动程序,其他进程什么都不做就直接退出。

您将三个参数传递给 send():第一个参数是数据应发送到的进程的等级。第二个参数是用于识别数据的标签。第三个参数是数据。

标签始终是一个整数。在示例 47.2 中,标签是 16。标签可以识别对 send() 的调用。您会看到该标签与 recv() 一起使用。

传递给 send() 的第三个参数是 99。这个数字从等级 1 的进程发送到等级 0 的进程。Boost.MPI 支持所有原始类型。可以直接发送像 99 这样的 int 值。

recv() 需要类似的参数。第一个参数是应该从中接收数据的进程的等级。第二个参数是将对 recv() 的调用与对 send() 的调用链接起来的标签。第三个参数是存放接收到的数据的变量。

如果您使用至少两个进程运行示例 47.2,则会显示 99。

示例 47.3。从任何进程接收数据

#include <boost/mpi.hpp>
#include <iostream>
int main(int argc, char *argv[])
{
  boost::mpi::environment env{argc, argv};
  boost::mpi::communicator world;
  if (world.rank() == 0)
  {
    int i;
    world.recv(boost::mpi::any_source, 16, i);
    std::cout << i << '\n';
  }
  else
  {
    world.send(0, 16, world.rank());
  }
}

Example47.3

示例 47.3 基于示例 47.2。它不发送数字 99,而是发送调用 send() 的进程的等级。这可以是等级大于 0 的任何进程。

对等级为 0 的进程的 recv() 调用也发生了变化。 boost::mpi::any_source 是第一个参数。这意味着对 recv() 的调用将接受来自任何发送带有标签 16 的数据的进程的数据。

如果您使用两个进程启动示例 47.3,将显示 1。毕竟,只有一个进程可以调用 send()——rank 为 1 的进程。如果你启动程序时有两个以上的进程,不知道会显示哪个数字。在这种情况下,多个进程将调用 send() 并尝试发送它们的排名。哪个进程最先,因此显示哪个排名是随机的。

示例 47.4。使用 boost::mpi::status 检测发件人

#include <boost/mpi.hpp>
#include <iostream>
int main(int argc, char *argv[])
{
  boost::mpi::environment env{argc, argv};
  boost::mpi::communicator world;
  if (world.rank() == 0)
  {
    int i;
    boost::mpi::status s = world.recv(boost::mpi::any_source, 16, i);
    std::cout << s.source() << ": " << i << '\n';
  }
  else
  {
    world.send(0, 16, 99);
  }
}

recv() 的返回值类型为 boost::mpi::status。此类提供了一个成员函数 source(),它返回从中接收数据的进程的等级。示例 47.4 告诉您数字 99 是从哪个进程收到的。

到目前为止,所有示例都使用 send() 和 recv() 来传输 int 值。在示例 47.5 中,传输了一个字符串。

示例 47.5。使用 send() 和 recv() 传输数组

#include <boost/mpi.hpp>
#include <iostream>
int main(int argc, char *argv[])
{
  boost::mpi::environment env{argc, argv};
  boost::mpi::communicator world;
  if (world.rank() == 0)
  {
    char buffer[14];
    world.recv(boost::mpi::any_source, 16, buffer, 13);
    buffer[13] = '\0';
    std::cout << buffer << '\n';
  }
  else
  {
    const char *c = "Hello, world!";
    world.send(0, 16, c, 13);
  }
}

send() 和 recv() 可以传输数组和单个值。示例 47.5 传输字符数组中的字符串。因为 send() 和 recv() 支持像 char 这样的基本类型,所以可以毫无问题地传输 char 数组。

send() 将指向字符串的指针作为其第三个参数,并将字符串的大小作为其第四个参数。传递给 recv() 的第三个参数是指向存储接收数据的数组的指针。第四个参数告诉 recv() 应该接收多少个字符并将其存储在缓冲区中。示例 47.5 写出 Hello, world!到标准输出流。

示例 47.6。使用 send() 和 recv() 传输字符串

#include <boost/mpi.hpp>
#include <boost/serialization/string.hpp>
#include <string>
#include <iostream>
int main(int argc, char *argv[])
{
  boost::mpi::environment env{argc, argv};
  boost::mpi::communicator world;
  if (world.rank() == 0)
  {
    std::string s;
    world.recv(boost::mpi::any_source, 16, s);
    std::cout << s << '\n';
  }
  else
  {
    std::string s = "Hello, world!";
    world.send(0, 16, s);
  }
}

尽管 Boost.MPI 只支持原始类型,但这并不意味着它不能传输非原始类型的对象。 Boost.MPI 与 Boost.Serialization 一起工作。可以根据 Boost.Serialization 规则序列化的对象可以使用 Boost.MPI 进行传输。

示例 47.6 传输“Hello, world!”这次传输的值不是字符数组,而是 std::string。 Boost.Serialization 提供头文件 boost/serialization/string.hpp,只需要包含它以使 std::string 可序列化。

原文地址:https://yamagota.blog.csdn.net/article/details/127932153
 
标签: C++ Boost MPI 接口
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • Aurelius vs mORMot vs EntityDAC Delphi 的
    Aurelius vs mORMot vs EntityDAC   Delphi 的 ORM框架:http://www.tmssoftware.com/site/aurelius.asp#product-buy-onlinehttps://synopse.info/fossil/wiki/Synopse+OpenSourcehttps://www.devart.com/entitydac/download.htmlkbmMW  http://www.compo
    02-09
  • 【Ruby】Mac gem的一些坑
    前言自上一次升级MacOS系统后出现jekyll无法构建的问题,当时处理半天。谁知道最近又升级了MacOS,荒废博客多时,今天吝啬写了一篇准备发布,构建报错,问题重新。还是记录下,以防下次升级出问题。问题描述安装jekyll静态博客需要在Ruby环境下运行,于是参照
    02-09
  • iOS oc 调用 swift
    如股票oc要调用swift里面的代码 需要包含固定这个头文件项目名称 LiqunSwiftDemo-Swift.h         #ProjectName#-Swift.h固定的写法swift 目的 是取代oc 但是 不会完全取代 只是前端的替换LiqunSwiftDemo-Swift 点进去 可以看到 所有的swift代码 都产生
    02-09
  • objective-c NSTimer 定时器
    -(void)initTimer{//时间间隔NSTimeInterval timeInterval =3.0 ;//定时器repeats 表示是否需要重复,NO为只重复一次NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(mobileAnimation) userInfo:nil
    02-09
  • Objective-C  日记③ 字符串
    Objective-C 日记③ 字符串
    一、创建字符串、类方法   公式创建NSString  +(id) stringWithFormat:(NSString *) format,……;eg:  NSString *height;  height=[NSString stringWithFormat:@"高度是: %d 长度: %d",10,20];得到的字符串:“高度是: 10 长度: 20” 注意:  省
    02-09
  • Objective-C KVC机制
    Objective-C KVC机制http://blog.csdn.net/omegayy/article/details/7381301全部推翻重写一个版本,这是我在公司内做技术分享的文档总结,对结构、条理做了更清晰的调整。 1.    基本概念MODEL主要是英文文档里面经常出现的一些概念,讲解一下,方便英文
    02-09
  • objective-c 加号 减号 - +
    “加号代表static”是错误的说法,可能跟你那样表达的人其实意思是:“前置加号的方法相当于Java 里面的静态方法”。在Oc中,方法分为类方法和实例方法。前置加号(+)的方法为类方法,这类方法是可以直接用类名来调用的,它的作用主要是创建一个实例。有人把
    02-09
  • Objective-C  日记①
    Objective-C 日记①
    1、Xcode的.m扩展名表示文件含有Object-C代码,C以.c文件,C++以.cpp文件2、头文件声明:C使用:#include,O-C使用#import(当然你也可以使用#include) 3、输出方式:  C:printf("",参数);  O-C:NSLog(@"",参数); 4、布尔类型  C:bool 具有true
    02-09
  • ASP.NET MVC 操作AD 获取域服务器当前用户姓
    #region 根据当前登录域账号 获取AD用户姓名和所在OU目录/// summary/// 根据当前登录域账号 获取AD用户姓名和所在OU目录/// /summary/// param name="searchUser"要搜索的当前用户名/param/// param name="paths"out返回该用户所在OU目录/param/// param nam
    02-09
  • swift和OC - 拆分数组 和 拆分字符串
    1. 拆分数组 /// 根据 数组 截取 指定个数返回 多个数组的集合func splitArray( array: [Date], withSubSize subSize: Int) - [[Date]] {//数组将被拆分成指定长度数组的个数let count = array.count% subSize == 0 ? (array.count/ subSize) : (array.count
    02-08
点击排行