本博客参考官方文档进行介绍,全网仅此一家进行中文翻译,走过路过不要错过。
官方网址:https://www.mpi-forum.org/
参考文档:https://www.mpi-forum.org/docs/mpi-4.1/mpi41-report.pdf
引用官方4.1文档方法:
@manual{mpi41,author = "{Message Passing Interface Forum}",title = "{MPI}: A Message-Passing Interface Standard Version 4.1",url = "https://www.mpi-forum.org/docs/mpi-4.1/mpi41-report.pdf",year = 2023,month = nov
}
本文档分成多个博客进行介绍,在本人专栏中含有所有内容:
https://blog.csdn.net/qq_33345365/category_12610893.html
MPI-4.1为2023年11月2日发表,本专栏开始书写日期2024/3/18,当时最新版本4.1
本人会维护一个总版本,一个小章节的版本,总版本会持续更新,小版本会及时的调整错误和不合理的翻译,内容大部分使用chatGPT 3.5翻译,内容不引用文献,请自行去文档查看
开始编辑时间:2024/3/18;最后编辑时间:2024/3/24
MPI4.1文档1:MPI总览与各版本介绍
MPI: A Message-Passing Interface Standard
单词表,本人常用语,不代表官方翻译
缩写/英文 | 全称/翻译 | 缩写/英文 | 全称/翻译 |
---|---|---|---|
MPI | Message-Passing Interface 消息传递接口 | collective | 集合 |
communicator | 通信器 | one-sided | 单边 |
non-blocking | 非阻塞 | persistent | 持久化 |
dynamic processes | 动态进程 |
前言
这份文档描述了消息传递接口(MPI)标准,版本为4.1。 MPI标准包括点对点消息传递、集合通信、组和通信器概念、进程拓扑、环境管理、进程创建和管理、单边通信、扩展集合操作、外部接口、输入输出、一些杂项主题以及多种工具接口。C语言和Fortran语言的语言绑定已定义。
历史上,该标准的演变如下:
- MPI-1.0(1994年5月5日):首次发布。
- MPI-1.1(1995年6月12日):小更新和错误修复。
- MPI-1.2(1997年7月18日):几处澄清和补充。
- MPI-2.0(1997年7月18日):新增功能以及MPI-1.2的所有澄清和补充内容。
- MPI-1.3(2008年5月30日):出于历史原因,将MPI-1.1、MPI-1.2以及几个勘误文档合并为一个文档。
- MPI-2.1(2008年6月23日):合并前述文档。
- MPI-2.2(2009年9月4日):进一步澄清和新增七个新的例程。
- MPI-3.0(2012年9月21日):对MPI-2.2进行扩展。
- MPI-3.1(2015年6月4日):澄清和对MPI-3.0进行小幅扩展。
- MPI-4.0(2021年6月9日):在MPI-3.1基础上增加了重要的新功能。
- MPI-4.1(2023年11月2日):对MPI-4.0进行澄清和小幅扩展。
MPI(Message Passing Interface)是一种用于编写并行计算程序的标准。以下是各个版本的简要介绍:
版本 4.1:2023年11月2日发布。主要对 MPI-4.0 文档进行了修正和澄清。一些例程、属性键 MPI_HOST 和 mpif.h Fortran 包含文件已被弃用。
版本 4.0:2021年6月9日发布。这是一个重要更新版本,包含了显著的新功能。最大的变化包括添加了许多例程的大型计数版本,以解决使用 int 或 INTEGER 作为计数参数的限制,持久性集合操作,分区通信,MPI 初始化的另一种方式,应用程序信息断言以及改进的错误处理定义。此外,还有一些较小的改进和修正。
版本 3.1:2015年6月4日发布。主要对 MPI-3.0 文档进行了修正和澄清。最大的变化是对 MPI-3.0 中引入的 Fortran 绑定进行了修正。此外,还添加了一些新函数,包括以便携方式操作 MPI_Aint 值的例程,非阻塞集合 I/O 例程以及通过名称获取 MPI_T 性能和控制变量的索引值的例程。
版本 3.0:2012年9月21日发布。与 MPI-2.2 的开发同时,MPI 论坛开始讨论对 MPI 进行重大扩展的问题。这个文档包含了 MPI-3 标准。此版本包含了对 MPI 功能的显著扩展,包括非阻塞集合操作,新的单边通信操作以及 Fortran 2008 绑定。与 MPI-2.2 不同,这个标准被视为对 MPI 标准的重大更新。与之前的版本一样,只有在用户有强烈需求时才会采用新功能。然而,一些功能可能对现有的 MPI 实现产生更多的影响。
版本 2.2:2009年9月4日发布。主要对 MPI-2.1 文档进行了修正和澄清。虽然添加了一些扩展,但所有正确的 MPI-2.1 程序也是正确的 MPI-2.2 程序。只有在用户有强烈需求、开放源代码实现和对现有 MPI 实现影响较小时才会采用新功能。
版本 2.1:2008年6月23日发布。此文档合并了之前的 MPI-1.3(2008年5月30日)和 MPI-2.0(1997年7月18日)文档。MPI-2.0 的某些部分,如第4章杂项和第7章扩展集合操作的某些部分,已经合并到 MPI-1.3 的章节中。此外,还包括了 MPI 论坛收集的其他勘误和澄清。
版本 1.3:2008年5月30日发布。此文档合并了之前的 MPI-1.1(1995年6月12日)和 MPI-2(1997年7月18日)中的 MPI-1.2 章节。MPI 论坛收集的关于 MPI-1.1 和 MPI-1.2 的其他勘误也包含在此文档中。
MPI-2.0版本:1997年7月18日。在发布MPI-1.1版本后,MPI论坛开始开会讨论修正和扩展事项。MPI-2.0版本着重于进程创建和管理、单边通信、扩展集合通信、外部接口和并行I/O。杂项章节讨论了其他内容,特别是语言互操作性方面的内容。
MPI-1.2版本:1997年7月18日。MPI-2论坛将MPI-1.2版本作为“MPI-2:消息传递界面的扩展”标准的第3章引入,该标准于1997年7月18日发布。该章节包含了对MPI标准1.1版本的澄清和次要修正。MPI-1.2版本中唯一的新功能是用于确定实现符合MPI标准的版本的功能。MPI-1版本和MPI-1.1版本之间存在一些小差异,而MPI-1.1版本和MPI-1.2版本之间几乎没有差异,但是MPI-1.2版本和MPI-2版本之间存在较大差异。
MPI-1.1版本:1995年6月。从1995年3月开始,消息传递界面论坛重新召开会议,以纠正错误并澄清1994年5月的MPI文档(以下简称为1.0版本)。这些讨论结果形成了1.1版本。与1.0版本相比,1.1版本的更改是次要的。还提供了一个标记了所有更改的文档版本。
MPI-1.0版本:1994年5月。消息传递界面论坛是由40多个组织参与的,自1993年1月以来一直在开会讨论和定义一组消息传递的库接口标准。消息传递界面论坛未经任何官方标准组织的支持或认可。
消息传递界面的目标很简单,即制定一种广泛使用的标准,用于编写消息传递程序。因此,该接口应该确立一种实用、可移植、高效和灵活的消息传递标准。
这是消息传递界面论坛的最终报告,1.0版本。该文档包含了提议的接口的所有技术特性。这份草案是1994年5月通过LATEX处理的。
章节1 介绍MPI
1.1 总览与目标
MPI是一个消息传递库接口规范,这个定义的所有部分都很重要。MPI主要针对消息传递并行编程模型,其中数据通过每个进程上的协作操作从一个进程的地址空间移动到另一个进程的地址空间。在集合操作、远程内存访问操作、动态进程创建和并行I/O等方面提供了对“经典”消息传递模型的扩展。MPI是一个规范,不是一个实现;有多个MPI的实现。这个规范是一个库接口;MPI不是一种语言,所有MPI操作都表达为函数、子程序或方法,根据适当的语言绑定,对于C和Fortran来说,这些是MPI标准的一部分。这个标准是通过一个由并行计算供应商、计算机科学家和应用开发者组成的社区的开放过程定义的。接下来的几节概述了MPI发展的历史。
建立消息传递标准的主要优势是可移植性和易用性。在一个分布式内存通信环境中,高级例程和/或抽象是建立在低级消息传递例程之上的,标准化的好处特别明显。此外,像这里提出的消息传递标准的定义为供应商提供了一个明确定义的基本例程集,供他们有效实现,或在某些情况下提供硬件支持,从而增强可伸缩性。
简而言之,MPI的目标是开发一个广泛使用的用于编写消息传递程序的标准。因此,该接口应该为消息传递建立一个实用、可移植、高效和灵活的标准。
以下是一份完整的目标列表:
- 设计一个应用程序接口(不一定是为编译器或系统实现库设计)。
- 允许高效的通信:避免内存到内存的复制,允许计算和通信的重叠,并在可用时将任务卸载到通信协处理器。
- 支持在异构环境中使用的实现。
- 为接口提供方便的C和Fortran绑定。
- 假设有可靠的通信接口:用户无需处理通信失败,这些失败由底层通信子系统处理。
- 定义的接口应该能够在许多供应商的平台上实现,而不需要在底层通信和系统软件中做出重大改变。
- 接口的语义应该是语言无关的。
- 接口应该被设计为支持线程安全。
1.2 MPI-1.0的背景
MPI寻求利用现有消息传递系统的最有吸引力的特性,而不是选择其中一个并将其作为标准采用。因此,MPI受到了IBM T. J. Watson研究中心的工作[3, 4],Intel的NX/2 [58],Express [15],nCUBE的Vertex [54],p4 [10, 11]和PARMACS [7, 12]的强烈影响。其他重要的贡献来自Zipcode [61, 62],Chimp [21, 22],PVM [6, 19],Chameleon [32]和PICL [27]。
MPI的标准化工作涉及来自40个组织的大约60人,主要来自美国和欧洲。大多数并发计算机的主要供应商参与了MPI,还有来自大学、政府实验室和工业界的研究人员。标准化过程始于1992年4月29日至30日在弗吉尼亚州威廉斯堡举办的“分布式内存环境下消息传递标准研讨会”,由并行计算研究中心赞助 [70]。在这个研讨会上讨论了一个标准消息传递接口所必需的基本特性,并成立了一个工作组来继续标准化过程。
一个初步的草案提议,即MPI-1,由Dongarra,Hempel,Hey和Walker于1992年11月提出,并于1993年2月完成了修订版本 [20]。MPI-1包含了在威廉斯堡研讨会上确定的主要特性,被认为在消息传递标准中是必要的。由于MPI-1主要旨在促进讨论并“启动进程”,因此主要集中于点对点通信。MPI-1提出了一些重要的标准化问题,但没有包括任何集合通信例程,并且不是线程安全的。
1992年11月,在明尼阿波利斯举行了一次MPI工作组会议,决定将标准化过程放在更正式的基础上,并普遍采用高性能Fortran论坛的程序和组织。为标准的主要组件领域成立了小组委员会,并为每个领域建立了一个电子邮件讨论服务。此外,设定了在1993年秋季之前制定出草案MPI标准的目标。为了实现这一目标,MPI工作组在1993年头9个月的每6周举行两天的会议,并在1993年11月的超级计算93会议上提出了草案MPI标准。这些会议和电子邮件讨论共同构成了MPI论坛,其成员资格对所有高性能计算社区的成员开放。
1.3 MPI-1.1 MPI-1.2和MPI-2.0的背景
从1995年3月开始,MPI论坛开始会议,讨论对原始MPI标准文档[24]的修正和扩展。这些讨论的第一个成果是MPI规范的1.1版本,发布于1995年6月[25]。当时的工作重点集中在五个领域。
- MPI-1.1文档的进一步更正和澄清。
- MPI-1.1的补充内容,不会显著改变其功能类型(例如,新的数据类型构造函数,语言互操作性等)。
- 完全新的功能类型(例如,动态进程,单边通信,并行I/O等),即所有人所认为的“MPI-2功能”。
- Fortran 90和C++的绑定。MPI-2为MPI-1和MPI-2函数指定了C++绑定,并扩展了MPI-1和MPI-2的Fortran 77绑定,以处理Fortran 90的问题。
- MPI进程和框架似乎可能有用的领域的讨论,但在标准化之前需要更多讨论和经验(例如,在共享内存机器上的零拷贝语义,实时规范等)。
以下是关于MPI-2文档第3章的纠正和澄清内容:“MPI-2版本1.2”收集了这些内容。该章还包含用于识别版本号的函数。MPI-1.1的补充内容(以上述列表中的2、3和4类型的项目)位于MPI-2文档的其余章节中,并构成MPI-2的规范。列表中第5类型的项目已经移至一个独立的文档,名为“MPI开发杂志”(JOD),不属于MPI-2标准的一部分。这种结构使用户和实施者能够了解给定实现的MPI兼容性水平:
- MPI-1兼容性意味着符合MPI-1.3。这是一个有用的兼容性水平。它意味着该实现符合MPI-1.1在MPI-2文档第3章中给出的MPI-1.1函数行为的澄清。一些实现可能需要更改才能符合MPI-1。
- MPI-2兼容性意味着符合MPI-2.1的所有内容。
- MPI开发日志不是MPI标准的一部分。 需要强调的是,向前兼容性得到了保留。也就是说,一个有效的MPI-1.1程序既是一个有效的MPI-1.3程序,也是一个有效的MPI-2.1程序,而一个有效的MPI-1.3程序则是一个有效的MPI-2.1程序。
1.4 MPI-1.3和MPI-2.1的本经
在MPI-2.0发布之后,MPI论坛继续致力于纠正和澄清标准文件(MPI-1.1和MPI-2.0)。短文档“MPI-1.1的勘误表”于1998年10月12日发布。2001年7月5日,发布了对MPI-2.0的勘误和澄清的第一次投票,第二次投票于2002年5月22日进行。这两次投票都是通过电子方式进行的。这两次投票合并成了一份文档:“MPI-2的勘误表”,发布于2002年5月15日。然后,这个勘误流程中断了,但论坛及其电子邮件反馈者继续处理新的澄清请求。
MPI论坛的常规工作重启始于三次会议,在欧洲PVM/MPI’06(波恩)、欧洲PVM/MPI’07(巴黎)和SC’07(里诺)。2007年12月,一个指导委员会开始组织新的MPI论坛会议,定期每8周召开一次。在2008年1月14日至16日的芝加哥会议上,MPI论坛决定将现有和未来的MPI文档合并为每个版本的MPI标准一个文档。出于技术和历史原因,这个系列从MPI-1.3开始。额外的Ballots 3和4解决了自1995年开始的勘误清单上的旧问题,直到最近几年的新问题。在将所有文档(MPI-1.1、MPI-2、MPI-1.1的勘误表(1998年10月12日)和MPI-2.1 Ballots 1–4)合并成一个草案文档后,为每章定义了一个章节作者和审查团队。他们清理了文档,以实现一致的MPI-2.1文档。最终的MPI-2.1标准文档于2008年6月完成,并在2008年9月的Dublin会议上进行了第二次投票后正式发布,就在欧洲PVM/MPI’08之前。
1.5 MPI-2.2的背景
MPI-2.2是MPI-2.1标准的一个小型更新。这个版本解决了在MPI-2.1标准中没有纠正的额外错误和歧义,以及对MPI-2.1的一小部分扩展,这些扩展符合以下标准:
- 任何正确的MPI-2.1程序都是正确的MPI-2.2程序。
- 任何扩展都必须对用户有显著的好处。
- 任何扩展都不得需要大量的实现工作。为此,所有这些变化都伴随着开源实现。 MPI-2.2的讨论与MPI-3的讨论同时进行;在某些情况下,扩展被提议为MPI-2.2,但后来被移至MPI-3。
1.6 MPI-3.0的背景
MPI-3.0 是MPI标准的一次重大更新。更新内容包括将集合操作扩展到包括非阻塞版本、扩展单边操作以及新增Fortran 2008绑定。此外,已移除不建议使用的C++绑定,以及许多不建议使用的例程和MPI对象(如MPI_UB数据类型)。任何不使用这些已移除MPI例程或对象的有效MPI-2.2程序都是有效的MPI-3.0程序。
1.7 MPI-3.1的背景
MPI-3.1是MPI标准的一个小更新。大部分更新是对标准的修正和澄清,特别是对Fortran绑定部分。新增的函数包括以便于移植的方式操作MPI_Aint值的例程、非阻塞集合I/O例程以及用于获取MPI_T性能和控制变量的名称索引值的例程。还新增了一个通用索引。任何有效的MPI-3.0程序都是有效的MPI-3.1程序。
1.8 MPI-4.0的背景
MPI-4.0 是 MPI 标准的一次重大更新。最大的变化是添加了许多例程的大计数版本,以解决使用 int 或 INTEGER 作为计数参数的限制,添加了持久集合、分区通信、初始化 MPI 的替代方式、应用程序信息断言,并改进了错误处理的定义。此外,还有许多较小的改进和修正。除了第 18 章列出的语义变化外,任何有效的 MPI-3.1 程序都是有效的 MPI-4.0 程序。
1.9 MPI-4.1的背景
MPI-4.1是MPI标准的一个小更新。它主要包含对MPI-4.0文档的修正和澄清。其中几个例程、属性键MPI_HOST以及mpif.h Fortran包含文件已经被弃用。一个新的例程提供了一种查询MPI程序运行硬件的方式。任何有效的MPI-4.0程序都是一个有效的MPI-4.1程序,除了在第18章列出的语义更改。
1.10 谁应用使用这个标准
这个标准旨在供所有希望在Fortran和C(以及通过C++访问C绑定)中编写可移植消息传递程序的人使用。这包括个人应用程序程序员、设计用于并行计算机的软件开发人员,以及环境和工具的创建者。为了吸引这么广泛的受众群体,该标准必须为基本用户提供简单易用的接口,同时不在语义上排除高性能消息传递操作,这些操作可在先进计算机上使用。
1.11 什么平台是实现的目标
消息传递范式的吸引力部分源于其广泛的可移植性。以这种方式表达的程序可以在分布式存储多处理器、工作站网络以及这些的组合上运行。此外,包括多核处理器和混合架构在内的共享内存实现也是可能的。这种范式不会被合并共享和分布式内存视图的架构或网络速度的增加所淘汰。因此,在各种机器上实现这个标准都是可能且有用的,包括由通信网络连接的其他机器的集合构成的“机器”,无论是并行的还是非并行的。
这个接口适用于完全通用的MIMD(Multiple Instruction, Multiple Data)程序的使用,也适用于以SPMD(Single Program, Multiple Data)更受限制的风格编写的程序。MPI提供了许多旨在提高在具有专门的处理器间通信硬件的可扩展并行计算机上性能的特性。因此,我们预期在这些机器上将提供本地的、高性能的MPI实现。与此同时,在标准Unix处理器间通信协议上实现MPI将为工作站集群和异构工作站网络提供可移植性。
1.12 这个标准包括什么
标准包括:
- 点对点通信,
- 分区通信,
- 数据类型(Datatypes),
- 集合操作,
- 进程组,
- 通信上下文,
- MPI进程的虚拟拓扑,
- 环境管理和查询,
- 信息对象,
- 进程初始化、创建和管理,
- 单边通信,
- 外部接口,
- 并行文件I/O,
- 工具支持,
- Fortran和C的语言绑定,
- 以及在辅助文档中的其他主题。
1.13 Side-documents 附带文档
附带文档扩展和/或修改了本文档中涵盖的特性、语义、语言绑定和其他方面。附带文档不得修改MPI标准中定义的任何方面,除非提供了明确允许这些偏差的机制。执行未明确启用MPI标准偏差的程序将符合MPI标准,即使使用实现了修改任何方面的附带文档的MPI实现。
每个附带文档都有一个独立于MPI标准版本和其他附带文档的版本方案。所有附带文档都规定与MPI标准版本的兼容性和互操作性,并且可能定义与其他附带文档的特性和语义的互操作性。附带文档不需要提供对所有MPI概念的完整覆盖,但必须记录哪些MPI概念受到影响。 符合的实现不需要遵守任何附带文档。然而,如果声称符合特定版本的附带文档,实现必须遵守整个附带文档。附带文档将在与MPI标准相同的位置找到1。
1.14 本文件的结构
以下是本文档中剩余章节的列表,以及每个章节的简要描述:
- 第2章,MPI术语和约定,解释了MPI文档中使用的符号术语和约定。
- 第3章,点对点通信,定义了MPI的基本、成对通信子集。这里包括发送和接收操作,以及许多旨在使基本通信功能强大高效的相关函数。
- 第4章,分区点对点通信,定义了在MPI中执行分区通信的方法。分区通信允许从MPI进程中的多个参与者(例如线程或任务)向单个消息中进行多次数据贡献。
- 第5章,数据类型,定义了描述任何数据布局的方法,例如结构体数组。
- 第6章,集合通信,定义了进程组的集合通信操作。这些操作的一些著名例子包括进程组上的屏障和广播(不一定是所有进程)。
- 第7章,进程组、上下文、通信子和缓存,展示了如何形成和操作进程组,如何获取唯一的通信上下文,以及如何将这两者绑定到通信子中。
- 第8章,MPI进程的虚拟拓扑,解释了一组旨在帮助将MPI进程组(线性有序集合)映射到更丰富的拓扑结构(如多维网格)的实用函数。
- 第9章,MPI环境管理,解释了程序员如何管理和查询当前MPI环境。这些函数对于编写正确、健壮的程序至关重要,尤其对于构建高度可移植的消息传递程序非常重要。
- 第10章,信息对象,定义了一个不透明对象,用作多个MPI例程的输入。
- 第11章,进程初始化、创建和管理,定义了几种MPI初始化、进程创建和进程管理方法,同时对执行环境施加最小限制。
- 第12章,单边通信,定义了可以由单个进程完成的通信例程。这些例程包括共享内存操作(put/get)和远程累积操作。
- 第1章3,外部接口,定义了旨在允许开发人员在MPI之上构建层次的例程。
- 第14章,输入/输出,定义了MPI对并行I/O的支持。
- 第15章,工具支持,涵盖了允许调试器、性能分析器和其他工具获取有关MPI进程操作的数据的接口。
- 第16章,已弃用接口,描述了被保留供参考但不建议使用的例程。因为它们可能在未来的标准版本中被删除。
- 第17章,已移除接口,描述了从MPI中移除的例程和构造。
- 第18章,语义变化和警告,描述了与以前版本的MPI相比的语义变化。
- 第19章,语言绑定,讨论了Fortran相关问题,并描述了C和Fortran之间的语言互操作性方面。 附录包括:
- 附录A,语言绑定摘要,提供了所有MPI函数、常量和类型的C和Fortran特定语法。
- 附录B,变更日志,总结了自上一版本以来的一些变化。
- 几个索引页面显示了通用术语和定义、示例、常量和预定义句柄的位置、C和Fortran类型的声明、回调例程原型以及所有MPI函数。
MPI提供了各种接口来促进不同MPI实现之间的互操作性。其中包括MPI I/O的规范数据表示以及MPI_PACK_EXTERNAL和MPI_UNPACK_EXTERNAL的规范。定义实际绑定这些接口以实现互操作性的工作超出了本文档的范围。另外,还有一份单独的文档,记录了在MPI-2开发过程中MPI论坛讨论过的但未包含在MPI标准中的具有价值的想法。这些内容包含在“开发日志”(JOD)中,旨在记录这些想法和讨论。JOD可以在https://www.mpi-forum.org/docs 上找到。
MPI4.1文档2-MPI术语与约定
第2章 MPI术语与约定 MPI Terms and Conventions
这一章节解释了MPI文档中使用的符号术语和惯例,以及其中所做的一些选择和背后的原因。
2.1 文档符号表示 Document Notation
Rational. 在整个文档中,对界面规范中所做设计选择的理由以这个格式(首尾包裹)进行标注。某些读者可能希望跳过这些部分,而对界面设计感兴趣的读者可能会仔细阅读它们。(End of advice to Rational.)
Rationale. (End of rationale.)
Advice to users. 在整个文档中,针对用户并且展示用法的材料以这个格式(首尾包裹)进行标注。某些读者可能希望跳过这些部分,而对MPI编程感兴趣的读者可能会仔细阅读它们。(End of advice to users.)
Advice to users. (End of advice to users.)
Advice to implementors. 在整个文档中,主要针对实现者的评论性材料以这个格式(首尾包裹)进行标注。某些读者可能希望跳过这些部分,而对MPI实现感兴趣的读者可能会仔细阅读它们。(End of advice to implementors.)
Advice to implementors. (End of advice to implementors.)
2.2 命名约定
在许多情况下,C函数的MPI名称的形式为MPI_Class_action_subset。这种约定起源于MPI-1。从MPI-2开始,尝试根据以下规则对MPI函数的名称进行标准化。
- 在C和Fortran的mpi_f08模块中,与特定类型的MPI对象相关联的所有例程应该采用
MPI_Class_action_subset
的形式,如果不存在子集,则采用MPI_Class_action
的形式。在Fortran的mpi模块和(已弃用的)mpif.h文件中,与特定类型的MPI对象相关联的所有例程应该采用MPI_CLASS_ACTION_SUBSET
的形式,如果不存在子集,则采用MPI_CLASS_ACTION
的形式。 - 如果例程与类无关,则名称应采用
MPI_Action_subset
或MPI_ACTION_SUBSET
的形式,在C和Fortran中均适用。 - 某些动作的名称已经标准化。特别地,create表示创建一个新对象,get表示获取关于对象的信息,set表示设置这些信息,delete表示删除信息,is表示询问对象是否具有某种属性。
MPI-1过程中定义的一些MPI函数的C和Fortran名称在几种情况下违反了这些规则。最常见的例外是省略了例程中的Class名称,以及在可以推断出的情况下省略了Action。
2.3 程序规范
MPI 程序使用一种与语言无关的符号表示法进行规范,过程调用的参数被标记为 IN、OUT 或 INOUT。它们的含义如下:
IN:程序调用可以使用输入值,但在调用执行期间不会从调用者的角度更新参数,
OUT:程序调用可以更新参数,但不使用其输入值,
INOUT:程序调用可以同时使用和更新参数。
特殊情况是,如果参数是一个不透明对象的句柄(这些术语在第2.5.1节中有定义),并且该对象在过程调用中被更新,则该参数标记为INOUT或OUT。即使句柄本身没有被修改,我们也使用INOUT或OUT属性来表示句柄所引用的内容被更新了。
Rationale. MPI的定义尽可能避免使用INOUT参数,因为这种使用方式容易出错,特别是对于标量参数而言。 (End of rationale.)
MPI对IN、OUT和INOUT的使用旨在告知用户如何使用参数,但并没有提供一个严格的分类,可以直接转换为所有语言绑定(例如,Fortran 90绑定中的INTENT或C绑定中的const)。例如,“常量”MPI_BOTTOM
通常可以传递给OUT缓冲区参数。类似地,MPI_STATUS_IGNORE
可以作为OUT状态参数传递。
MPI函数中常见的一种情况是一个参数在某些进程中用作IN,在其他进程中用作OUT。这样的参数在语法上是一个INOUT参数,并被标记为这样,尽管在语义上,在单个进程中它不会在一次调用中同时用于输入和输出。
另一个常见情况是当某些进程只需要某个参数值时,当参数在某个进程中不重要时,可以传递任意值作为参数。
除非另有说明,类型为OUT或INOUT的参数不能与传递给MPI过程的任何其他参数别名。下面是一个C中参数别名的示例。如果我们定义一个C过程如下所示,
void copyIntBuffer ( int *pin, int *pout, int len)
{ int i;for (i=0; i<len; ++i) *pout++ = *pin++;
}
以下代码片段中对它的调用存在参数别名。
int a [10];
copyIntBuffer (a, a+3, 7);
C语言允许这样使用,但MPI过程的这种用法是禁止的,除非另有规定。请注意,Fortran禁止参数的别名使用。 所有MPI函数首先以与语言无关的符号表示进行规定。紧随其后是语言相关的绑定:
- ISO C版本的函数。
- 与USE mpi_f08一起使用的Fortran版本。
- 使用USE mpi或(已弃用的)INCLUDE 'mpif.h’的相同函数的Fortran版本。
一些MPI过程为特定语言支持提供了两个接口;参见第2.5.6节和第2.5.8节。
一个例外是第15.3节“MPI工具信息接口”,该节仅提供ISO C接口。
此文档中的“Fortran”指的是Fortran 90或更高版本;请参阅第2.6节。
在本标准中,词语“函数function”、“例程routine”、“过程procedure”、“过程调用procedure call”和“调用call”通常被用作同义词。
2.4 Semantic Terms
在讨论MPI过程时,使用以下语义术语。术语"message data buffer"指的是通信过程中使用的发送/接收缓冲区。术语"file data buffer"指的是MPI I/O过程中使用的数据缓冲区。在本节中,我们使用术语"data buffer",具体指的是消息数据缓冲区还是文件数据缓冲区取决于MPI过程。附录A.2显示了本节中定义的术语如何适用于所有与操作相关的MPI过程。
2.4.1 MPI操作 MPI Operation
MPI操作:MPI操作是由MPI库执行的一系列步骤,用于建立和启用数据传输和/或同步。它包括四个阶段:初始化、启动、完成和释放,并且实现为一组一个或多个MPI过程,详见第2.4.2节。
- 初始化initialization:将参数列表传递给操作,但不传递数据缓冲区的内容(如果有)。操作的规范可能规定,在操作被释放之前,数组参数不得更改。
- 启动Starting:将数据缓冲区(如果有)的控制权交给关联的操作。请注意,初始initiation指的是初始化initialization和启动starting阶段的组合。
- 完成:返回数据缓冲区的内容的控制权,并指示已更新输出缓冲区和参数(如果有)。 请注意,当实现完成阶段的MPI过程返回时,MPI操作已完成。
- 释放:返回剩余参数列表的控制权(例如,数据缓冲区地址和数组参数)。
MPI操作可以以阻塞、非阻塞和持久化的形式之一或多个形式提供。
-
阻塞操作:对于阻塞操作,所有四个阶段都在单个过程调用中组合(如图2.1所示,并在第2.4.2节中定义)。
-
非阻塞操作:对于非阻塞操作,初始化和启动阶段合并为单个非阻塞过程调用,完成和释放阶段合并为单独的、单个过程调用,可以是阻塞的也可以是非阻塞的(如图2.2所示,并在第2.4.2节中定义)。
-
持久化操作:对于持久化操作,每个阶段都有单独的过程(如图2.3所示,并在第2.4.2节中定义)。每个过程可以是阻塞的也可以是非阻塞的。
对于分区发送操作,需要额外调用每个分区的发送缓冲区以完成启动阶段(参见第4.2.1节)。对于分区接收操作,在操作完成之前,用户可以在验证已经到达后访问输出缓冲区的分区(参见第4.2.2节)。
这四个阶段导致了操作状态的初始化、启动、完成和释放。启动的(started)操作也称为活动的(active),而初始化和完成状态也称为非活动的(inactive)。
活动的(active)通信和I/O操作也称为待处理的(pending)操作。请注意,待处理的操作可以是已开始但尚未完成(即使请求句柄已被释放)的非阻塞或持续操作,也可以是尚未完成的阻塞操作,例如等待接收消息的接收操作。
MPI操作还可以是集合的collective或非集合的noncollective。
-
集合操作:一组相关操作,每个MPI进程在一个组或多个MPI进程的组中执行一个操作。对于集合操作,完成阶段可能在组中的所有进程开始操作之前完成,也可能不会。 集合MPI操作也可以作为阻塞、非阻塞或持久操作提供。
-
非集合操作:非集合noncollective操作被定义为not collective操作。
许多MPI操作需要在多个MPI进程中协调活动:这种操作的语义要求在操作转换到完全操作状态之前,必须启动一个或多个其他特定的语义相关操作。例如,接收操作需要在接收完成之前启动一个相关的发送操作;或者一个集合操作在所有相应组的MPI进程中启动这些操作之前可能不会完成。
- Enabled: 当在特定的MPI进程中启动了所有特定语义相关的操作以确保在该MPI进程中完成时,MPI操作就处于启用状态。
Rationale. MPI实现可以包含优化(例如自动缓冲),允许MPI操作在启用之前完成。(End of rationale.)
有些MPI操作是事先启用的(priori enabled),即它们不需要任何其他特定语义相关的操作来完成。例如,一个带缓冲的发送操作可以独立于相关的接收操作而完成。
一旦MPI操作被启用,该操作必须最终完成。一个操作在启动之前可能已经被启用。例如,如果一个接收操作在匹配的发送操作启动之后启动,则该接收操作已经被启用。
Rationale.
操作A的enabled的定义是不对称的:enabled包括所有特定的语义相关操作 A i ′ A^′_i Ai′已经开始以确保完成,但不包括操作A本身已经开始。
示例:
- 当相关的发送操作已经开始时,receive被启用。
- 标准模式send在相关的receive已经开始时被启用。如果MPI实现选择使用内部缓冲区,则send操作可能在启用之前已经完成,即receive操作已经开始。
- 同步模式的send操作在相关的receive操作已经开始时被启用,并且在启用之前不能完成。
- 缓冲(buffered)模式的send操作是事先启用的。
- 就绪(ready)模式的send操作只有在已经启用时才能开始,即相关的receive操作已经开始。
- 对于集合的广播,特定MPI进程上的操作仅在组中所有其他MPI进程已经开始相关广播操作时才被启用。
具体地说,对于构成可能同步的集合操作的一组MPI进程上的相关操作,对于特定MPI进程p上的操作,当组中所有其他MPI进程pi != p已经开始其相关操作时,该操作被启用,而p上的操作不必已经开始。
(End of rationale.)
MPI4.1文档3-MPI过程与MPI数据类型
2.4.2 MPI过程 MPI Procudures
所有MPI过程可以分为本地过程和非本地过程,定义如下:
非本地(nonlocal)过程:如果在执行过程中,返回可能需要调用另一个特定的与语义相关的MPI过程来在另一个MPI进程上执行,则该MPI过程被称为非本地过程。
本地过程:如果MPI过程不是非本地过程,则称其为本地过程。
一个MPI操作实现为一组一个或多个MPI过程。MPI操作相关的过程至少实现了MPI操作的一个阶段的一部分,如2.4.1节所述。MPI操作相关的过程还可以实现一个或多个MPI操作的一个或多个阶段。在某些情况下,可能需要多个MPI操作相关的过程来实现一个单独的阶段。
还有其他一些MPI过程不实现任何MPI操作的阶段。
MPI操作相关的过程的语义使用两个正交(独立)的概念描述:完整性(取决于包含哪些阶段)和本地性。这些过程可以是不完整的、完成的、释放的或者完成和释放的,取决于过程返回时相关操作的状态。
此外,所有这些过程都可以描述为阻塞的或非阻塞的,但后两个术语是指完整性和本地性概念的组合。另外,所有MPI操作相关的过程都可以是集合的或非集合的。
以下是与MPI操作相关的过程的属性:
初始化(Initialization)过程:如果从该过程返回,则表示关联操作已完成初始化阶段,这意味着用户已将参数列表的控制(但不是数据缓冲区的内容)移交给了MPI。用户仍然被允许读取或修改数据缓冲区的内容。如果初始化过程不同时也是关联操作的释放过程(见下文),则用户不得释放数据缓冲区或修改数组参数。
启动(starting)过程:如果从该过程返回,则表示关联操作已完成启动阶段,这意味着用户已将数据缓冲区的控制移交给了MPI。如果启动过程不同时也是关联操作的完成过程(见下文),则用户不得修改输入数据缓冲区或读取输出数据缓冲区。
初始(Initiation)过程:如果从该过程返回,则表示初始化和启动阶段均已完成,这意味着整个参数列表的控制权已移交给了MPI。
完成(Completing)过程:如果从过程返回,则该MPI过程被称为完成过程,这意味着至少有一个关联操作已经完成了其完成阶段,这意味着用户可以依赖输出数据缓冲区的内容,并修改这些操作的输入和输出数据缓冲区的内容。如果完成过程不是释放过程(见下文),则用户不允许释放数据缓冲区或修改数组参数。
不完成(Incomplete)过程:如果它不是完成过程,则该MPI过程被称为不完成过程。
释放(Freeing)过程:如果从过程返回,则该MPI过程是释放过程,这意味着至少有一个关联操作已经完成了其释放阶段,这意味着用户可以重新使用初始化关联操作时指定的所有参数。
非阻塞过程:如果它是不完整的并且是本地的,则该MPI过程是非阻塞的。
阻塞过程:如果它不是非阻塞的,则该MPI过程是阻塞的。
Advice to users.
MPI程序中,与操作相关的MPI过程大多数情况下是不完全的程序是局部的,而完全的程序是非局部的。在定义的地方标注了例外情况。在许多情况下,将I作为不完全和即时的缩写字母前缀标记过程名称中的非阻塞过程。 以下是一些分类示例。
非阻塞过程:
- 不完全且本地:MPI_ISEND、MPI_IRECV、MPI_IBCAST、MPI_IMPROBE、MPI_SEND_INIT、MPI_RECV_INIT等。
阻塞过程:
- 完全且非本地:MPI_SEND、MPI_RECV、MPI_BCAST等。
- 不完成且非本地:MPI_MPROBE、MPI_BCAST_INIT等,MPI_FILE_{READ|WRITE}_{AT_ALL|ALL|ORDERED}_BEGIN。
- 完全且本地:MPI_BSEND、MPI_RSEND、MPI_MRECV。
不是MPI操作相关的MPI过程:
- MPI_COMM_RANK、MPI_WTIME、MPI_PROBE、MPI_IPROBE等。
(End of advice to users.)
集合过程:如果MPI进程组或组中的所有进程需要调用过程,则该MPI过程是集合的。
对于同一进程组的集合操作的初始化过程必须由进程组的所有成员按相同顺序执行。
MPI集合过程是同步的,如果关联的MPI进程组或组中的所有进程调用了相应的匹配MPI过程,则它只会在所有进程都调用了适当的匹配MPI过程后返回。对于非阻塞集合操作的启动过程和持久集合操作的起始过程是本地的,并且不会同步。对于其他集合操作的过程,例如阻塞集合操作的过程和持久集合操作的初始化过程,可能会同步,也可能不会同步。
Advice to users. 当关联进程组中没有可能对所有其他进程进行相应调用时,调用任何同步函数都是错误的。当关联组中所有其他进程没有可能启动相应操作时,等待任何集合操作完成也是错误的。(End of advice to users.)
非集合过程:非集合过程被定义为不是集合的过程。
局部和非局部MPI过程的定义也可以应用于特定的过程调用或在某些约束条件下的过程调用。例如,发生在相关发送操作已经开始之后的完成接收过程的调用可能被描述为本地的,即使没有约束的完成接收过程是非本地的。更普遍地,发生在操作已经启用之后的任何完成过程的调用都是本地的,即使没有约束的完成过程是非本地的。另一个例子,使用大小为一的进程组进行阻塞集合过程调用是本地的,即使没有约束的阻塞集合过程是非本地的。
2.4.3 MPI数据类型 MPI Datatypes
数据类型可以分为以下几种类型:
预定义的predifined:预定义数据类型是指具有预定义(常量)名称的数据类型(如MPI_INT、MPI_FLOAT_INT或MPI_PACKED),或者使用MPI_TYPE_CREATE_F90_INTEGER、MPI_TYPE_CREATE_F90_REAL或MPI_TYPE_CREATE_F90_COMPLEX构造的数据类型。前者是具有名称的,而后者是无名称的。
派生的derived:任何不是预定义的数据类型都属于派生数据类型。
可移植的portable:如果一个数据类型是预定义数据类型,或者它是从可移植数据类型派生而来,并且仅使用了MPI_TYPE_CONTIGUOUS、MPI_TYPE_VECTOR、MPI_TYPE_INDEXED、MPI_TYPE_CREATE_INDEXED_BLOCK、MPI_TYPE_CREATE_SUBARRAY、MPI_TYPE_DUP和MPI_TYPE_CREATE_DARRAY这些类型构造器,则该数据类型是可移植的。这样的数据类型是可移植的,因为数据类型中所有的位移都是以一个预定义数据类型的范围为单位的。因此,如果这样的数据类型适合一个内存中的数据布局,那么如果使用相同的声明,在另一个内存中对应的数据布局也将适合,即使这两个系统具有不同的架构。另一方面,如果一个数据类型是使用MPI_TYPE_CREATE_HINDEXED、MPI_TYPE_CREATE_HINDEXED_BLOCK、MPI_TYPE_CREATE_HVECTOR或MPI_TYPE_CREATE_STRUCT构造的,那么该数据类型包含显式的字节位移(例如,提供填充以满足对齐限制)。如果这些位移适合一个内存中的数据布局,但在另一个运行在具有不同架构处理器上的进程的数据布局中使用,那么这些位移可能不会被正确选择。
等价的equivalent:如果两个数据类型看起来是通过相同的一系列调用(和参数)创建的,并且因此具有相同的类型映射,则它们是等价的。两个等价的数据类型不一定具有相同的缓存属性或相同的名称。
MPI4.1文档4-MPI数据类型 MPI DataTypes
2.5 数据类型 Datatypes
MPI管理用于缓冲消息和存储各种MPI对象的内部表示(如组、通信器、数据类型等)的系统内存。这些内存对用户不可直接访问,存储在其中的对象是不透明的:用户无法看到其大小和形状。不透明对象通过句柄访问,这些句柄存在于用户空间中。MPI操作不透明对象的过程通过传递句柄参数来访问这些对象。除了用于对象访问的MPI调用之外,句柄还可以参与赋值和比较操作。
在Fortran中,使用USE mpi
或(已弃用的)INCLUDE 'mpif.h'
,所有句柄都具有INTEGER类型。在Fortran中使用USE mpi_f08,以及在C中,每个对象类别都定义了不同的句柄类型。使用Fortran USE mpi_f08时,句柄被定义为Fortran BIND©派生类型,只包含一个元素INTEGER :: MPI_VAL。内部句柄值与mpi模块和(已弃用的)mpif.h中使用的Fortran INTEGER值相同。运算符.EQ.、.NE.、==和/ =被重载,以允许对这些句柄进行比较。类型名称与C中的名称相同,只是它们不区分大小写。例如:
TYPE , BIND (C) :: MPI_Comm
INTEGER :: MPI_VAL
END TYPE MPI_Comm
C类型必须支持赋值操作符和相等操作符。
Advice to implementors. 在Fortran中,句柄可以是系统表中不透明对象的索引;在C中,它可以是这样的索引或指向对象的指针。(End of advice to implementors.)
Rationale. 由于Fortran整数值是等效的,应用程序可以在所有三种支持的Fortran方法之间轻松转换MPI句柄。例如,一个整数通信句柄COMM可以直接转换为一个完全等效的mpi_f08通信句柄,命名为comm_f08,通过comm_f08%MPI_VAL=COMM,反之亦然。使用INTEGER定义的句柄和BIND©派生类型句柄的方法是不同的:Fortran 2003(以及更高版本)定义了BIND©派生类型可以在用户定义的公共块内使用,但是这取决于伴随的C编译器规则,用于这些BIND©派生类型句柄的数值存储单元数量是多少。大多数编译器使用一个单位来处理整数句柄和作为BIND(C)
派生类型定义的句柄。(End of rationale.)
Advice to users. 如果用户想要用 mpi_f08 模块替换 mpi 模块或者 (已弃用的) mpif.h,并且应用程序在 Fortran 公共块中存储一个句柄,那么就需要在所有使用这个公共块的应用程序子程序中改变 Fortran 支持方法,因为这个句柄的数值存储单元数量在这两个模块中可能不同。(End of advice to users.)
不透明对象由特定于每种对象类型的调用进行分配和释放。这些调用在描述对象的部分中列出。调用接受匹配类型的句柄参数。在分配调用中,这是一个OUT参数,返回一个有效的对象引用。在调用释放时,这是一个INOUT参数,返回一个“无效句柄”值。MPI为每种对象类型提供了一个“无效句柄”常量。与此常量的比较用于测试句柄的有效性。
对释放例程的调用使句柄无效,并标记对象以进行释放。在调用之后,用户无法访问该对象。但是,MPI不必立即释放对象。在释放时挂起的任何操作(在释放时)和涉及该对象的解耦MPI活动(请参阅第2.9节)将正常完成;对象将在此后被释放。
不透明对象及其句柄仅在创建对象的进程中具有重要性,并且无法转移到另一个进程。
MPI提供了一些预定义的不透明对象和这些对象的预定义静态句柄。用户不得释放这样的对象。
Rationale. 这种设计隐藏了用于MPI数据结构的内部表示,从而允许在C和Fortran中进行类似的调用。它还避免了与这些语言中的类型规则冲突,并且容易允许未来功能的扩展。这里使用的不透明对象的机制松散地遵循了POSIX Fortran绑定标准。
在用户空间中明确分离句柄和系统空间中的对象允许在用户程序中适当的时机进行空间回收和释放调用。如果不透明对象在用户空间中,就必须非常小心,不要在任何待处理的操作需要该对象完成之前超出范围。指定的设计允许标记对象以进行释放,然后用户程序可以超出范围,而对象本身仍然持久存在,直到任何待处理的操作完成为止。
句柄支持赋值/比较的要求是因为这样的操作很常见。这限制了可能实现的范围。在C中的替代方案是允许句柄是一个任意的不透明类型。这将强制引入进行赋值和比较的例程,增加了复杂性,因此被排除了。在Fortran中,句柄被定义为使赋值和比较通过语言的运算符或这些运算符的重载版本可用。(End of rationale.)
Advice to users. 用户可能会通过将一个句柄变量赋值给另一个句柄变量,然后释放与这些句柄相关联的对象,从而意外创建悬空引用。相反,如果一个句柄变量在关联的对象被释放之前被释放,那么该对象就会变得无法访问(例如,如果句柄是子程序中的局部变量,并且在关联对象被释放之前退出了子程序)。用户有责任避免向不透明对象添加或删除引用,除非是由于MPI调用分配或释放这些对象。(End of advice to users.)
Advice to implementors. 不透明对象的预期语义是不透明对象彼此独立;为了创建这样的对象,每次调用都会复制创建对象所需的所有信息。实现可以通过引用替换复制来避免过度复制。例如,派生数据类型可以包含对其组件的引用,而不是组件的副本;对MPI_COMM_GROUP的调用可能会返回与通信器关联的组的引用,而不是这个组的副本。在这种情况下,实现必须维护引用计数,并以使得可见效果就像对象已被复制的方式分配和释放对象。(End of advice to implementors.)
2.5.2 数组参数 Array Arguments
MPI调用可能需要一个参数,该参数是不透明对象的数组,或者是一个句柄的数组。句柄数组是一个常规数组,其中条目是连续位置上相同类型对象的句柄。每当使用这样的数组时,需要额外的len参数来指示有效条目的数量(除非可以通过其他方式推导出这个数字)。有效条目位于数组的开始位置;len指示其中有多少个,并且不必是整个数组的大小。其他数组参数也遵循相同的方法。在某些情况下,将NULL句柄视为有效条目。当需要将数组的状态参数设为NULL时,可以使用MPI_STATUSES_IGNORE。
2.5.3 状态 State
MPI过程在各处使用具有状态类型的参数。此类数据类型的值均由名称标识,并且没有针对它们的任何操作。例如,MPI_TYPE_CREATE_SUBARRAY例程具有一个名为order的状态参数,其值为MPI_ORDER_C和MPI_ORDER_FORTRAN。
MPI程序有时会为基本类型参数的特殊值赋予特殊含义;例如,标签是点对点通信操作的整数值参数,具有特殊的通配符值MPI_ANY_TAG。这样的参数将具有一定范围的常规值,这个范围是相应基本类型的值范围的子范围;特殊值(例如MPI_ANY_TAG)将在常规范围之外。常规值的范围,如标签,可以使用环境查询函数进行查询,参见第9章。其他值的范围,如源,取决于其他MPI例程给定的值(在源的情况下,是通信器的大小)。
MPI还提供了预定义的命名常量句柄,例如MPI_COMM_WORLD。
所有命名的MPI常量,除Fortran中以下列出的情况外,均可在初始化表达式或赋值中使用。由常量句柄访问的不透明对象在MPI初始化(例如,使用MPI_INIT)和MPI结束(例如,使用MPI_FINALIZE)之间定义并且其值不会改变。句柄本身是常量,也可以在初始化表达式或赋值中使用。
在C中,所有命名的MPI常量,其在“A.1.1节”中描述为“整数常量表达式”的常量,必须实现为指定整数类型的C整数常量表达式。在C中的其他MPI常量不要求是C整数常量表达式,但必须可以在初始化表达式和赋值中使用。因此,不能保证它们可用于数组声明或作为switch语句中的case标签。
在Fortran中,所有命名的MPI常量(以下列出的情况除外)必须声明为具有PARAMETER属性的常量。 在Fortran中不能在初始化表达式或赋值中使用的常量如下:
MPI_BOTTOM
MPI_BUFFER_AUTOMATIC
MPI_STATUS_IGNORE
MPI_STATUSES_IGNORE
MPI_ERRCODES_IGNORE
MPI_IN_PLACE
MPI_ARGV_NULL
MPI_ARGVS_NULL
MPI_UNWEIGHTED
MPI_WEIGHTS_EMPTY
Advice to implementors. 在Fortran中,实现这些特殊常数可能需要使用超出Fortran标准的语言构造。通过使用特殊值来定义常数(例如,通过PARAMETER语句),并不可行,因为实现无法区分这些值与有效数据。通常,这些常数被实现为预定义的静态变量(例如,MPI声明的COMMON块中的变量),依赖于目标编译器通过地址传递数据的事实。在子例程中,可以通过一些超出Fortran标准的机制(例如,通过Fortran扩展或在C中实现函数)来提取这个地址。(End of advice to implementors.)
2.5.5 选择 Choice
MPI函数有时会使用具有选择(或联合)数据类型的参数。对同一例程的不同调用可能会通过引用传递不同类型的实际参数。为提供这种参数,各种语言的机制会有所不同。对于使用(已弃用的)mpif.h或mpi模块的Fortran语言,文档中使用<type>表示选择变量;对于使用Fortran 2018语法TYPE(*), DIMENSION(…)声明此类参数的Fortran mpi_f08模块,对于C语言,我们使用void*。
Advice to implementors. 实现者可以自由选择如何在mpi模块中实现选择参数,例如,可以使用非标准的依赖于编译器的方法,其质量类似于隐式Fortran接口中的调用机制,或者可以使用在mpi_f08模块中定义的方法。详见第19.1.1节。(End of advice to implementors.)
2.5.6 绝对地址和相对地址位移 Absolute Addresses and Relative Address Displacements
MPI程序中的一些过程使用地址参数,这些参数代表调用程序中的绝对地址,或者相对位移参数,表示两个绝对地址的差值。这些参数的数据类型在C语言中是MPI_Aint,在Fortran中是INTEGER(KIND=MPI_ADDRESS_KIND)。这些类型必须具有相同的宽度,并以相同的方式编码地址值,以便在不进行转换的情况下,可以直接将一个语言中的地址值传递到另一个语言中。MPI提供了常量MPI_BOTTOM来指示地址范围的开始。为了检索绝对地址或进行任何绝对地址的计算,应使用第5.1.5节提供的例程和函数。第5.1.12节提供了有关正确使用绝对地址的额外规则。对于包含相对位移或其他用途而不是绝对地址的表达式,可以使用内在运算符(例如,+、-、*)。
Rationale. 字节位移值需要足够大,以编码用于表示绝对或相对内存地址的任何值。在MPI-4.0之前,一些MPI例程在C语言中使用int,在Fortran中使用INTEGER作为字节位移参数的类型。为了避免破坏向后兼容性,此版本的标准继续支持在这些例程中使用int在C语言中以及在Fortran中使用INTEGER。此外,此版本的标准支持在这些例程中在C语言中使用MPI_Aint(通过单独的“_c”后缀过程)以及在Fortran中使用INTEGER(KIND=MPI_ADDRESS_KIND)(通过新的MPI Fortran绑定中的多态接口(使用mpi_f08))。请参阅第19.2节进行详细说明。(End of rationale.)
2.5.7 文件偏移 File Offsets
为了进行I/O操作,需要给出文件的大小、位移和偏移量。这些量往往可能大于32位,而Fortran整数的默认大小可能只有32位。为了解决这个问题,在Fortran中可以声明这些量为INTEGER(KIND=MPI_OFFSET_KIND)。在C语言中,可以使用MPI_Offset。这些类型必须具有相同的宽度,并以相同的方式编码地址值,以便可以直接将一个语言中的偏移量值传递到另一个语言中而无需转换。
2.5.8 计数 Counts
MPI定义了类型(例如MPI_Aint),用于表示内存中的位置,以及其他类型(例如MPI_Offset),用于表示文件中的位置。此外,一些MPI过程使用计数参数来表示要操作的MPI数据类型的数量。此外,在MPI工具信息接口的上下文中,时间戳是自过去某个时间以来经过的时钟滴答数。有时,我们需要一个单一类型,可以用于表示内存或文件中的位置,并表示计数值,这个类型就是在C中的MPI_Count,和在Fortran中的INTEGER (KIND = MPI_COUNT_KIND)。这些类型必须具有相同的宽度,并以相同的方式编码值,以便可以直接将一个语言中的计数值传递给另一个语言而无需转换。MPI_Count类型的大小由MPI实现确定,但限制是它必须至少能够编码任何可以存储在C类型int、MPI_Aint或MPI_Offset的变量中的值,以及在Fortran中的INTEGER、INTEGER(KIND=MPI_ADDRESS_KIND)或INTEGER(KIND=MPI_OFFSET_KIND)类型的变量中的值。即使MPI_Count类型足够大以编码地址位置,MPI_Count类型也不应用于表示绝对地址。
Rationale. 计数值需要足够大,以编码用于表示元素计数、步长、偏移量、索引、位移、内存中的类型映射、文件视图中的类型映射等的任何值。在MPI-4.0之前,许多MPI例程在C中使用int类型,在Fortran中使用INTEGER作为计数参数的类型。为了避免破坏向后兼容性,该标准版本继续支持在这些例程中使用C中的int以及Fortran中的INTEGER。此外,该标准版本还支持在这些例程中使用MPI_Count(通过单独的“_c”后缀程序)作为C中的计数类型,以及在Fortran中使用INTEGER(KIND=MPI_COUNT_KIND)(通过新的MPI Fortran绑定中的多态接口(USE mpi_f08))作为计数类型。请参阅第19.2节进行全面解释。(End of rationale.)
“大计数(large count)”一词指的是使用MPI_Count和INTEGER(KIND=MPI_COUNT_KIND)参数类型。有些情况下,在大计数的OUT参数中可以返回MPI_UNDEFINED。根据A.1.1表格(第851页),MPI_UNDEFINED常量被定义为C int(或未命名的枚举)和Fortran INTEGER。因此,实现应选择MPI_Count和INTEGER(KIND=MPI_COUNT_KIND)的底层类型,以便可以将它们与MPI_UNDEFINED进行比较。
Advice to implementors. 将MPI_UNDEFINED与MPI_Count或INTEGER(KIND=MPI_COUNT_KIND)进行比较时,可能需要通过强制转换操作。(End of advice to implementors.)
MPI4.1文档5-语言绑定、进程、错误处理、progress和实现
2.6 语言绑定 Language Binding
这个部分定义了MPI语言绑定的规则,特别是对Fortran和ISO C的规定。(请注意,ANSI C已被ISO C取代。)这里定义了各种对象表示方式,以及用于表达该标准的命名约定。实际的调用序列在其他地方定义。
MPI绑定适用于Fortran 90或更新版本,尽管最初设计是为了在Fortran 77环境中使用。使用mpi_f08模块时,还需要两个新的Fortran特性,即假定类型(即TYPE(*))和假定rank(即DIMENSION(…)),详见第2.5.5节。
由于“PARAMETER”是Fortran语言中的关键字,我们使用“argument”一词来表示子例程的参数。这些参数在C中通常称为参数,但我们期望C程序员能理解“argument”这个词(在C中没有具体的含义),从而避免对Fortran程序员造成不必要的困惑。
由于Fortran是不区分大小写的,链接器在解析Fortran名称时可能会使用小写或大写。使用区分大小写语言的用户应避免使用“MPI_”和“PMPI_”形式的任何前缀,其中任何字母都可以是大写或小写。
2.6.1 已弃用和移除的接口 Deprecated and Removed Interfaces
一些章节提到了被弃用或替换的MPI构造。这些构造仍然是MPI标准的一部分,如第16章所述,但建议用户不要继续使用,因为较新版本的MPI提供了更好的解决方案。例如,MPI-1函数的Fortran绑定使用INTEGER作为地址参数,这与C绑定不一致,在具有32位INTEGER和64位地址的机器上会导致问题。在MPI-2中,这些函数被赋予了新名称,并为地址参数提供了新的绑定。使用旧函数被声明为不推荐使用。为了保持一致性,在这里和其他几个情况下,即使新函数等效于旧函数,也提供了新的C函数。旧名称已被弃用。
一些之前弃用的构造现在已被移除,如第17章所述。它们可能仍由实现提供以保持向后兼容性,但不是必需的。
表2.1列出了所有被弃用和移除的构造列表。请注意,此列表中包括了一些C typedefs和Fortran子程序名称;它们是回调函数的类型。
2.6.2 Fortran绑定事务 Fortran Binding Issues
MPI-1.1最初提供了对Fortran 77的绑定。这些绑定仍然保留,但现在在Fortran 90标准的背景下进行解释。MPI仍然可以与大多数Fortran 77编译器一起使用,如下所述。当使用术语“Fortran”时,指的是Fortran 90或更新版本;如果使用了mpi_f08模块,则指的是Fortran 2008与TS 29113,后者现在已成为Fortran 2018及更高版本的组成部分。
所有Fortran MPI名称都具有MPI_前缀。虽然Fortran不区分大小写,但如果使用了mpi_f08模块,则MPI_前缀后的第一个字符是大写,其余字符均为小写。如果没有使用mpi_f08模块,则所有字符均为大写。程序不能声明以MPI_前缀开头的名称,例如变量、子程序、函数、参数、派生类型、抽象接口或模块。为了避免与分析接口冲突,程序还必须避免具有PMPI_前缀的子程序和函数。这是为了避免可能的名称冲突。
所有MPI Fortran子程序的最后一个参数是错误代码。使用USE mpi_f08时,此最后一个参数声明为可选的,但用户定义的回调函数(例如,COMM_COPY_ATTR_FUNCTION)及其预定义的回调(例如,MPI_COMM_NULL_COPY_FN)除外。一些作为函数的MPI操作没有错误代码参数。成功完成的错误代码值为MPI_SUCCESS。其他错误代码是实现相关的;请参阅第9章和附录A中的错误代码。
表示字符串最大长度的常量在Fortran中比在C中小一个,如第19.3.9节所述。
在Fortran中,句柄表示为INTEGER,或者使用mpi_f08模块的BIND©派生类型;请参阅第2.5.1节。二进制变量的类型为LOGICAL。
数组参数的索引从1开始。
旧版MPI Fortran绑定——使用mpi和(不推荐使用的)mpif.h——在几个方面与Fortran标准不一致。这些不一致性,如寄存器优化问题,对用户代码有影响,详细讨论见第19.1.16节。
在Fortran中,仅在使用较新的MPI Fortran绑定(USE mpi_f08)时才支持大计数和位移。为了提高可读性,所有Fortran大计数过程声明都用注释“!(_c)”标记。
表 2.1 已弃用和移除的构造:请自行去网页查看
2.6.3 C绑定事务 C Binding Issues
我们使用ISO C声明格式。所有MPI名称都有MPI_前缀,定义的常量均为全大写,而定义的类型和函数在前缀后有一个大写字母。程序不得声明任何以MPI_为前缀的名称(标识符),例如变量、函数、常量、类型或宏,其中任何一个字母都可以是大写或小写。为了支持性能分析接口,程序不得声明以PMPI_为前缀的名称的函数,其中任何一个字母都可以是大写或小写。
对于命名常量、函数原型和类型定义,必须提供一个名为mpi.h的包含文件。
几乎所有C函数都会返回一个错误代码。成功的返回值将是MPI_SUCCESS,但在失败后引发的错误代码是与实现相关的。
对于每个类别的不透明对象的句柄,都提供了类型声明。
数组参数从零开始索引。
逻辑标志是整数,值为0表示“false”,非零值表示“true”。
选择参数是void*类型的指针。
2.6.4 函数和宏
实现允许将MPI_AINT_ADD、PMPI_AINT_ADD、MPI_AINT_DIFF和PMPI_AINT_DIFF作为宏在C语言中实现,而不允许实现其他内容。
Advice to implementors. 实现者应该记录哪些例程被实现为宏。(End of advice to implementors.)
Advice to users. 如果这些例程被实现为宏,它们将无法与MPI性能分析接口一起工作。(End of advice to users.)
2.7 进程 Processes
MPI程序由执行自己代码的自治进程组成,采用MIMD(多指令多数据)风格。每个进程执行的代码不必相同。进程通过调用MPI通信原语进行通信。通常情况下,每个进程在自己的地址空间中执行,尽管也可以实现共享内存的MPI。
该文档规定了并行程序的行为,假设仅使用MPI调用。MPI程序与其他可能的通信、I/O和进程管理方式的交互未予规定。除非在标准规范中另有规定,否则MPI不对其与提供类似或等效功能的外部机制的交互结果提出要求。这包括但不限于,与外部进程控制机制、共享和远程内存访问、文件系统访问和控制、进程间通信、进程信号和终端I/O的交互。高质量的实现应努力使这些交互的结果对用户直观,并在必要时尽可能地记录限制。
Advice to implementors. 对于支持MPI中附加机制的功能的实现,预期应该记录这些机制如何与MPI进行交互。(End of advice to implementors.)
MPI和线程的交互在第11.6节中定义。
如果可以在MPI进程之间共享一段内存(即使通过加载/存储访问,使一段内存(共享内存段)同时从所有这些MPI进程访问),则MPI进程驻留在同一共享内存域中。 对于属于多个共享内存域的进程组,创建属于同一共享内存域的进程子组在第7.4.2节中定义。
2.8 错误处理 Error Handling
MPI提供给用户可靠的消息传输。发送的消息始终正确接收,用户无需检查传输错误、超时或其他错误条件。换句话说,MPI不提供处理通信系统传输失败的机制。如果MPI实现建立在不可靠的底层机制上,那么MPI子系统的实现者的任务是保护用户免受这种不可靠性,并仅反映不可恢复的传输失败。尽可能地,这些失败将作为相关通信调用中的错误反映出来。
同样地,MPI本身不提供处理MPI进程失败的机制,即当MPI进程意外并永久停止通信时(例如,软件或硬件崩溃导致MPI进程意外终止)。
当然,MPI程序仍然可能有错误。当使用不正确的参数进行MPI调用时(发送操作中的不存在的目标,接收操作中的缓冲区太小等),程序错误可能会发生。这种类型的错误在任何实现中都会发生。此外,资源错误可能会在程序超出可用系统资源的数量时发生(挂起消息数量、系统缓冲区等)。这种类型的错误发生取决于系统中可用资源的数量和资源分配机制的使用情况;这可能因系统而异。高质量的实现将为重要资源提供宽松的限制,以减轻这种可移植性问题。
在C和Fortran中,几乎所有MPI调用都返回一个代码,指示操作成功完成。在可能的情况下,如果在调用期间发生错误,则MPI调用会返回一个错误代码。默认情况下,在MPI库的执行期间检测到错误将导致并行计算中止,除了文件操作。然而,MPI提供了机制让用户更改此默认行为,并处理可恢复的错误。用户可以指定没有错误是致命的,并自行处理MPI调用返回的错误代码。此外,用户可以提供用户定义的错误处理程序,当MPI调用异常返回时将调用这些程序。MPI的错误处理设施在第9.3节中进行了描述。
MPI调用返回有意义的错误代码时受到多种因素的限制。当出现错误时,MPI可能无法检测到某些错误;其他错误在正常执行模式下可能成本过高以至于无法检测到;一些故障(例如内存故障)可能会破坏MPI库及其输出的状态;最后,一些错误可能是“灾难性的”,可能会阻止MPI将控制权返回给调用者。
此外,一些错误可能在不涉及可以获取关联错误处理程序的MPI对象的操作中被检测到。错误处理程序的关联在第9.3节中进一步描述。在这种情况下,这些错误将在使用世界模型时(参见第11.2节)引发在通信器MPI_COMM_SELF上。当MPI_COMM_SELF未初始化(即在MPI_INIT / MPI_INIT_THREAD之前,在MPI_FINALIZE之后,或者仅使用Sessions模型时)时,错误将引发初始错误处理程序(在启动操作期间设置,请参阅11.8.4节)。Sessions模型在第11.3节中描述。
最后,一些错误可能在关联操作在本地完成后被检测到。这种情况的一个示例是由于异步通信的性质而引起的:MPI调用可能会启动在调用返回后继续异步进行的操作。因此,操作可能会以指示成功完成的代码返回,但随后可能引发错误。如果有后续调用与同一操作相关(例如,验证异步操作是否已完成的调用),那么与此调用相关联的错误参数将用于指示错误的性质。在少数情况下,错误可能会在与操作相关的所有调用都已返回之后发生,因此无法使用错误值来指示错误的性质(例如,在发送具有准备模式的接收器中的错误程序时)。
此文档不会指定发生错误的MPI调用后计算的状态。期望的行为是返回相关的错误代码,并将错误的影响尽可能局限在最大范围内。例如,极度希望错误的接收调用不会导致接收方内存的任何部分被覆盖,超出接收消息指定区域的范围。 在支持在此处定义为错误的MPI调用方面,实现可以超越此文档。例如,MPI在匹配发送和接收操作之间指定了严格的类型匹配规则:发送浮点变量并接收整数是错误的。实现可以超越这些类型匹配规则,并在此类情况下提供自动类型转换。对于这种不符合规范行为,生成警告将是有帮助的。
MPI定义了一种让用户创建新错误代码的方法,如第9.5节所述。
2.9 Progress
MPI通信操作或并行I/O模式通常包括在一个或多个MPI进程中执行的几个相关操作。例如,点对点通信中一个MPI进程执行发送操作,另一个(或同一个)MPI进程执行接收操作,或者一个组中的所有MPI进程执行集合操作。
在每个MPI进程中,通信或并行I/O模式的部分在属于该操作的MPI过程调用中执行,而其他部分则是分离的MPI活动,即它们可以在额外的进展线程中执行,转移到网络接口控制器(NIC),或者在与给定通信或并行I/O模式语义不相关的其他MPI过程调用中执行。
如果MPI过程调用被阻塞,那么它会延迟返回,直到另一个MPI进程中发生了特定的活动或状态变化。
- 一个非本地的MPI过程调用,它延迟返回直到另一个MPI进程上的某个特定语义相关的MPI调用完成,或者
- 一个本地的MPI过程调用,它延迟返回直到另一个MPI进程上的某个不特定的MPI调用导致该进程的特定状态变化,或者
- 一个MPI终止过程(MPI_FINALIZE或MPI_SESSION_FINALIZE),它延迟返回或退出,因为这个MPI终止必须保证在调用MPI进程中与该MPI终止调用相关的所有分离的MPI活动在这个MPI终止完成之前被执行。注意,MPI终止过程可能在最终化之前执行属性删除回调函数(参见第11.2.4节);这些回调函数可能会生成额外的分离MPI活动。
非本地阻塞 MPI 过程调用的一些示例:
- MPI_SSEND 延迟返回,直到目标 MPI 进程启动匹配的接收操作(例如通过调用 MPI_RECV 或 MPI_IRECV)。
- MPI_RECV 延迟返回,直到源 MPI 进程启动匹配的发送操作(例如通过调用 MPI_SEND 或 MPI_ISEND)。
本地阻塞 MPI 过程调用的一些示例:
- 如果消息数据无法完全缓冲,MPI_RSEND 延迟返回,直到目标 MPI 进程接收了无法缓冲的消息数据部分,这可能需要一个或多个不特定的 MPI 过程调用在目标 MPI 进程中执行。
- 如果消息在发送 MPI 进程处被缓冲(例如使用 MPI_BSEND),MPI_RECV 延迟返回,直到消息被接收,这可能需要一个或多个不特定的 MPI 过程调用在发送 MPI 进程中发送缓冲数据。
所有 MPI 进程都需要保证progress,即所有分离的 MPI 活动最终会执行。此保证需要在以下情况下提供:
- 阻塞的 MPI 过程,以及
- 重复调用的 MPI 测试过程(见下文),返回 flag=false。
进度必须独立于MPI活动是否属于特定会话或世界模型(参见第11.2节和11.3节)进行提供。实现此保证的其他方法也是可能的和允许的(例如,专用进度线程或将其卸载到网络接口控制器(NIC))。
MPI测试程序包括MPI_TEST、MPI_TESTANY、MPI_TESTALL、MPI_TESTSOME、MPI_IPROBE、MPI_IMPROBE、MPI_REQUEST_GET_STATUS、MPI_WIN_TEST和MPI_PARRIVED。
如果所有本地程序都独立于其他MPI进程中的MPI过程调用(与操作相关或不相关)而返回,则MPI实现将提供强进度。如果MPI实现没有提供强进度,则提供弱进度。
Advice to users. MPI操作的性能可能会受到进展类型的影响。一个正确的MPI应用程序必须在假设仅提供弱进展的情况下编写。每个在弱进展下正确的MPI应用程序,如果提供了强进展,都将被正确执行。此外,MPI标准的设计是这样的,如果在提供强进展的情况下正确性成立,那么即使实现仅提供弱进展,也应该正确执行。(End of advice to users.)
Rationale. MPI在使用非基于MPI程序的同步方法时不能保证进展。在MPI中没有强进展的保证可能会导致死锁,可以参考第2.7节和第12.7.3节中的示例12.13。(End of rationale.)
请查阅第2.4.2节中关于本地MPI过程的定义,并查看总索引中所有关于进展的引用。
2.10 实现问题 Implementation Issues
MPI实现可能与操作环境和系统的许多领域进行交互。虽然MPI并不要求提供任何服务(如信号处理),但如果这些服务可用,它确实强烈建议提供这些服务的行为。这是实现跨平台可移植性的重要点,前提是提供相同服务集的平台。
2.10.1 基本运行时例程的独立性 Independence of Basic Runtime Routines
MPI程序要求基本语言环境中的库例程(例如Fortran中的write,ISO C中的printf和malloc)在MPI_INIT之后并在MPI_FINALIZE之前独立运行,并且它们的完成与MPI程序中其他进程的操作无关。
请注意,这并不妨碍创建提供并行服务且其操作是集体的库例程。然而,以下程序在ISO C环境中应该能够完成,而不管MPI_COMM_WORLD的大小(假设printf在执行MPI进程中可用)。
int commworld_rank;
MPI_Init (( void *)0 , ( void *)0);
MPI_Comm_rank ( MPI_COMM_WORLD , & commworld_rank );
if (commworld_rank == 0) printf (" Starting program \n");
MPI_Finalize ();
对应的Fortran程序也应该完成。
不需要的是这些例程在被多个MPI进程调用时的具体顺序。例如,MPI对以下程序的输出既不要求也不建议(假设在执行MPI进程中可以进行I/O操作)。
MPI_Comm_rank ( MPI_COMM_WORLD, & commworld_rank);
printf (" Output from MPI process where commworld_rank =%d\n",
commworld_rank);
此外,由于资源耗尽或其他错误而导致的调用失败不被视为违反此处的要求(然而,它们必须完成,只是不必成功完成)。
2.10.2 与信号的交互 Interaction with Signals
MPI不指定进程与信号的交互,并且不要求MPI是信号安全的。实现可以保留一些信号供其自身使用。要求实现文档记录使用的信号,并强烈建议不要使用SIGALRM、SIGFPE或SIGIO。实现还可以禁止在信号处理程序中使用MPI调用。
在多线程环境中,用户可以通过仅在不执行MPI调用的线程上捕获信号来避免信号与MPI库之间的冲突。高质量的单线程实现将是信号安全的:由信号挂起的MPI调用将在处理信号后正常恢复并完成。
2.11 例子
这份文档中的示例仅用于说明目的,不旨在规定标准。其中许多示例是由从MPI标准的源文件中提取示例的工具编译而成的。然而,这些示例并未经过仔细检查或验证。
第三章 点对点通信
MPI进程之间的消息发送和接收是基本的MPI通信机制。基本的点对点通信操作包括发送和接收。它们的使用在示例3.1中进行了说明。
例子3.1 使用点对点通信的hello world例子
#include "mpi.h"
int main(int argc, char *argv[])
{char message[20];int myrank;MPI_Status status;MPI_Init(&argc, &argv);MPI_Comm_rank(MPI_COMM_WORLD, &myrank);if (myrank == 0) /* 0号进程的代码 */{strcpy(message, "Hello , there ");MPI_Send(message, strlen(message) + 1, MPI_CHAR, 1, 99,MPI_COMM_WORLD);}else if (myrank == 1) /* 1号进程的代码 */{MPI_Recv(message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD, &status);printf(" received :%s:\n", message);}MPI_Finalize();return 0;
}
在例子3.1中,进程零(myrank = 0,严格地说是‘在通信器MPI_COMM_WORLD中具有排名0的MPI进程’)使用发送操作MPI_SEND向进程一发送消息。该操作指定了发送缓冲区,从中取出消息数据。在上述示例中,发送缓冲区包括进程零内存中包含变量消息的存储。发送操作的前三个参数指定了发送缓冲区的位置、大小和类型。发送的消息将包含此变量的13个字符。此外,发送操作将一个信封与消息关联。该信封指定了消息的目的地,并包含可由接收操作用来选择特定消息的区分信息。发送操作的最后三个参数,以及发送者的排名,指定了发送消息的信封。
进程一(myrank = 1,严格地说是‘在通信器MPI_COMM_WORLD中具有排名1的MPI进程’)使用接收操作MPI_RECV接收此消息。将根据其信封的值选择要接收的消息,并将消息数据存储到接收缓冲区中。在上述示例中,接收缓冲区包括进程一内存中包含字符串消息的存储。接收操作的前三个参数指定了接收缓冲区的位置、大小和类型。接下来的三个参数用于选择传入的消息。最后一个参数用于返回刚接收到的消息的信息。
Advice to users. 俗语常常使用“rank 0”或“process 0”这样的表述,但这些表述在严格意义上是模糊的,最好在使用时加上相关的上下文来说明,例如,在上面的情况中指明MPI通信器。(End of advice to users.)
下面的部分描述了阻塞发送和接收操作。我们讨论了发送、接收、阻塞通信语义、类型匹配要求、异构环境中的类型转换,以及更一般的通信模式。接下来介绍了非阻塞通信,然后是探测和取消消息、类似通道的构造和发送-接收操作,最后描述了“虚拟”MPI进程MPI_PROC_NULL。
3.2 阻塞发送和接收操作 Blocking Send and Receive Operations
3.2.1 阻塞发送
阻塞发送过程的语法如下所示。
MPI_SEND(buf, count, datatype, dest, tag, comm)
IN/OUT | 参数 | 含义(数据类型) |
---|---|---|
IN | buf | 发送缓冲区的初始地址 (choice) |
IN | count | 发送缓冲取元素的数量 (non-negative integer) |
IN | datatype | 发送缓冲区元素的数据类型 (handle) |
IN | dest | 目的进程号 (integer) |
IN | tag | 消息标记 (integer) |
IN | comm | 通信器 (handle) |
C 绑定
int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
int MPI_Send_c(const void *buf, MPI_Count count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
Fortran 2008 binding
MPI_Send(buf, count, datatype, dest, tag, comm, ierror)TYPE(*), DIMENSION(..), INTENT(IN) :: buf INTEGER, INTENT(IN) :: count, dest, tag TYPE(MPI_Datatype), INTENT(IN) :: datatype TYPE(MPI_Comm), INTENT(IN) :: comm INTEGER, OPTIONAL, INTENT(OUT) :: ierror
MPI_Send(buf, count, datatype, dest, tag, comm, ierror) !(_c)TYPE(*), DIMENSION(..), INTENT(IN) :: bufINTEGER(KIND=MPI_COUNT_KIND), INTENT(IN) :: countTYPE(MPI_Datatype), INTENT(IN) :: datatypeINTEGER, INTENT(IN) :: dest, tagTYPE(MPI_Comm), INTENT(IN) :: commINTEGER, OPTIONAL, INTENT(OUT) :: ierror
Fortran binding
MPI_SEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERROR)<type> BUF(*)INTEGER COUNT, DATATYPE, DEST, TAG, COMM, IERROR
该调用的阻塞语义将在3.4节描述。
3.2.2 消息数据 Message Data
MPI_SEND过程指定的发送缓冲区由地址buf处的连续的count个数据项组成,数据项的类型由datatype指定。需要注意的是,我们指定消息长度是按照元素的数量而不是字节的数量。前者是与机器无关的,更接近应用程序级别。
消息的数据部分由count个值的序列组成,每个值的类型由datatype指定。count可以为零,此时消息的数据部分为空。可以为消息数据值指定的基本数据类型对应于主机语言的基本数据类型。Fortran中的可能值及其对应的Fortran类型列在表3.1中。C中的可能值及其对应的C类型列在表3.2中。
表3.1 MPI预定义数据类型和Fortran数据类型的对应
MPI数据类型 | Fortran数据类型 |
---|---|
MPI_INTEGER | INTEGER |
MPI_REAL | REAL |
MPI_DOUBLE_PRECISION | DOUBLE PRECISION |
MPI_COMPLEX | COMPLEX |
MPI_LOGICAL | LOGICAL |
MPI_CHARACTER | CHARACTER(1) |
MPI_BYTE | |
MPI_PACKED |
表3.2 MPI预定义数据类型和C数据类型的对应
MPI数据类型 | C数据类型 |
---|---|
MPI_CHAR | char (作为可打印字符处理) |
MPI_SHORT | signed short int |
MPI_INT | signed int |
MPI_LONG | signed long int |
MPI_LONG_LONG_INT | signed long long int |
MPI_LONG_LONG (作为同义词) | signed long long int |
MPI_SIGNED_CHAR | signed char (作为整数值处理) |
MPI_UNSIGNED_CHAR | unsigned char (作为整数值处理) |
MPI_UNSIGNED_SHORT | unsigned short int |
MPI_UNSIGNED | unsigned int |
MPI_UNSIGNED_LONG | unsigned long int |
MPI_UNSIGNED_LONG_LONG | unsigned long long int |
MPI_FLOAT | float |
MPI_DOUBLE | double |
MPI_LONG_DOUBLE | long double |
MPI_WCHAR | wchar_t (<stddef.h>中定义,作为可打印字符处理) |
MPI_C_BOOL | _Bool |
MPI_INT8_T | int8_t |
MPI_INT16_T | int16_t |
MPI_INT32_T | int32_t |
MPI_INT64_T | int64_t |
MPI_UINT8_T | uint8_t |
MPI_UINT16_T | uint16_t |
MPI_UINT32_T | uint32_t |
MPI_UINT64_T | uint64_t |
MPI_C_COMPLEX | float _Complex |
MPI_C_FLOAT_COMPLEX(作为同义词) | float _Complex |
MPI_C_DOUBLE_COMPLEX | double _Complex |
MPI_C_LONG_DOUBLE_COMPLEX | long double _Complex |
MPI_BYTE | |
MPI_PACKED |
数据类型MPI_BYTE和MPI_PACKED不对应于Fortran或C数据类型。MPI_BYTE类型的值由一个字节(8个二进制位)组成。一个字节是无解释的,与字符不同。不同的机器可能对字符有不同的表示,或者可能使用多个字节来表示字符。另一方面,一个字节在所有机器上具有相同的二进制值。MPI_PACKED类型的使用在5.2节中解释。
MPI要求支持这些数据类型,这些数据类型与Fortran和ISO C的基本数据类型相匹配。如果主机语言具有其他数据类型,则应提供额外的MPI数据类型:MPI_DOUBLE_COMPLEX用于Fortran中的双精度复数,声明为DOUBLE COMPLEX类型;MPI_REAL2、MPI_REAL4、MPI_REAL8和MPI_REAL16用于Fortran实数,分别声明为REAL*2、REAL*4、REAL*8和REAL*16类型;MPI_INTEGER1、MPI_INTEGER2、MPI_INTEGER4和MPI_INTEGER8用于Fortran整数,分别声明为INTEGER*1、INTEGER*2、INTEGER*4和INTEGER*8类型;MPI_COMPLEX4、MPI_COMPLEX8、MPI_COMPLEX16和MPI_COMPLEX32用于Fortran中的复数,分别声明为COMPLEX*4、COMPLEX*8、COMPLEX*16和COMPLEX*32类型;等等。
Rationale. 设计的一个目标是将MPI实现为一个库,无需额外的预处理或编译。因此,不能假设通信调用对通信缓冲区中变量的数据类型有信息;这些信息必须由显式参数提供。这种数据类型信息的需求将在第3.3.2节中变得清晰。 (End of rationale.)
MPI_AINT、MPI_OFFSET和MPI_COUNT这些数据类型对应于MPI定义的C类型MPI_Aint、MPI_Offset和MPI_Count以及它们的Fortran等效类型INTEGER(KIND=MPI_ADDRESS_KIND)、INTEGER(KIND=MPI_OFFSET_KIND)和INTEGER(KIND=MPI_COUNT_KIND)。这在表3.3中有描述。所有预定义的数据类型句柄都可用于所有语言绑定。有关使用这些类型进行跨语言通信的信息,请参阅页面840和847的19.3.6节和19.3.10节。如果有相应的C++编译器,那么表3.4中的数据类型也支持C和Fortran。
表3.3: MPI预定义数据类型到C和Fortran数据类型的对应
MPI数据类型 | C数据类型 | Fortran数据类型 |
---|---|---|
MPI_AINT | MPI_Aint | INTEGER(KIND=MPI_ADDRESS_KIND) |
MPI_OFFSET | MPI_Offset | INTEGER(KIND=MPI_OFFSET_KIND) |
MPI_COUNT | MPI_Count | INTEGER(KIND=MPI_COUNT_KIND) |
请查看第840页和第847页的19.3.6和19.3.10节,了解关于这些类型的跨语言通信的信息。如果有配套的C++编译器,那么表3.4中的数据类型也支持C和Fortran。
表3.3: MPI预定义数据类型到C数据类型的对应
MPI datatype | C++ datatype |
---|---|
MPI_CXX_BOOL | bool |
MPI_CXX_FLOAT_COMPLEX | std::complex |
MPI_CXX_DOUBLE_COMPLEX | std::complex |
MPI_CXX_LONG_DOUBLE_COMPLEX | std::complex |
3.2.3 消息信封 Message Envelope
除了数据部分之外,消息还携带可以用来区分消息并有选择性地接收它们的信息。这些信息包括一定数量的字段,我们统称为消息信封。这些字段是:
source
destination
tag
communicator
消息源由消息发送者的身份隐含确定。其他字段由发送过程中的参数指定。
消息的目标进程由dest参数指定。
整数型消息标签由tag参数指定。程序可以使用这个整数来区分不同类型的消息。有效标签值的范围是0到UB,其中UB的值取决于实现。可以通过查询MPI_TAG_UB属性的值来获取,如第9章所述。MPI要求UB不小于32767。
comm参数指定用于发送操作的通信器。通信器在第7章中有解释;下面是它们的简要用法概述。
通信器指定了通信操作的通信上下文。每个通信上下文提供一个独立的“通信宇宙”:消息始终在发送它们的上下文中接收,而在不同上下文中发送的消息不会相互干扰。 通信器还指定了共享该通信上下文的MPI进程组。这个MPI进程组是有序的,MPI进程通过它们在这个组中的等级来标识。因此,dest的有效值范围是0到n-1∪{MPI_PROC_NULL},其中n是组中的MPI进程数量。(如果通信器是一个跨通信器,则目标由远程组中的等级来标识。参见第7章。)
一个MPI进程在每个它是成员的组中可能有不同的等级。
当使用世界模型(见第11.2节)时,MPI提供了一个预定义的通信器MPI_COMM_WORLD。它允许与所有MPI初始化后可访问的MPI进程通信,MPI进程通过在MPI_COMM_WORLD组中的等级来标识。
Advice to users. 对于熟悉大多数现有通信库提供的平坦名称空间概念和单一通信上下文的用户,只需在MPI初始化时使用World模型和预定义变量MPI_COMM_WORLD作为通信参数即可。这将允许与初始化时所有可用的MPI进程进行通信。
用户可以像第7章中所述那样定义新的通信器。通信器为库和模块提供了重要的封装机制。它们允许模块拥有自己独立的通信空间和自己的MPI进程编号方案。(End of advice to users.)
Advice to implementors. 消息信封通常由固定长度的消息头编码。然而,实际的编码是实现相关的。一些信息(例如,源或目的地)可能是隐含的,不需要在消息中显式携带。此外,MPI进程可以通过相对排名、绝对ID等进行标识。(End of advice to implementors.)