1.简介

本文档指定可用于在 C 和 C++ 程序中指定共享内存并行的编译器指令、库函数和环境变量的集合。 本文档中所述的功能统称为 OpenMP C/C++ 应用程序程序接口 (API)。 此规范的目标是提供一个用于并行编程的模型,该模型使程序可以跨不同供应商的共享内存体系结构进行移植。 许多供应商的编译器都支持 OpenMP C/C++ API。 有关 OpenMP 的详细信息(包括 OpenMP Fortran 应用程序接口)可在以下网站上找到:

https://www.openmp.org

通过本文档中定义的指令、库函数和环境变量可以创建和管理并行程序,同时实现可移植性。 指令使用单程序多数据 (SPMD) 构造、工作共享构造和同步构造扩展 C 和 C++ 顺序编程模型。 它们还支持数据的共享和私有化。 支持 OpenMP C 和 C++ API 的编译器会包含一个编译器命令行选项,用于激活并允许解释所有 OpenMP 编译器指令。

1.1 范围

此规范仅涵盖用户导向的并行化,在这种并行化中会显式定义编译器和运行时系统为并行执行程序所执行的操作。 不需要 OpenMP C 和 C++ 实现来检查依赖项、冲突、死锁、争用条件或导致程序执行错误的其他问题。 你负责确保使用 OpenMP C 和 C++ API 构造的应用程序可正确执行。 本文档不涵盖编译器生成的自动并行化和用于帮助实现此类并行化的指令。

1.2 术语的定义

本文档中使用以下术语:

  • barrier

    团队中所有线程都必须到达的同步点。 每个线程都会等到团队中的所有线程到达此点。 存在由指令标识的显式屏障和实现创建的隐式屏障。

  • 构造

    构造是语句。 它由指令组成,后跟结构化块。 某些指令不是构造的一部分。 (请参阅附录 C 中的 openmp-directive)。

  • directive

    C 或 C++ #pragma,后跟 omp 标识符、其他文本和新行。 指令指定程序行为。

  • 动态盘区

    词法盘区中的所有语句,以及函数内由于在词法盘区中执行语句而执行的任何语句。 动态盘区也称为区域

  • 词法盘区

    在结构化块中以词法形式保留的语句

  • 主线程

    在进入并行区域时创建团队的线程

  • 并行区域

    绑定到 OpenMP 并行构造,并且可由多个线程执行的语句。

  • private

    私有变量会命名对发出引用的线程唯一的存储块。 可通过多种方式指定变量是私有变量:并行区域中的定义、threadprivate 指令、privatefirstprivatelastprivatereduction 子句,或是在紧跟在 forparallel for 指令之后的 for 循环中将变量用作 for 循环控制变量。

  • region

    一个动态盘区。

  • 串行区域

    仅由主线程在任何并行区域的动态盘区外部执行的语句

  • 序列化

    用于执行并行构造,存在以下情况:

    • 仅由单个线程(这是该并行构造的主线程)构成的线程团队,

    • 结构化块中语句的串行执行顺序(与块不是并行构造一部分时的顺序相同),以及

    • omp_in_parallel() 返回的值没有影响(任何嵌套并行构造的影响除外)。

  • shared

    共享变量会命名单个存储块。 访问此变量的团队中的所有线程也会访问此单个存储块。

  • 结构化块

    结构化块是具有单个进入和单个退出的语句(单个或复合)。 如果会跳入或跳出某个语句,则该语句是结构化块。 (此规则包括调用 longjmp (3C) 或使用 throw,不过允许调用 exit。) 如果其执行始终以左 { 开头,并且始终以 } 结束,则复合语句是结构化块。 如果通过括在 {} 中来获取的对应复合语句是结构化块,则表达式语句、选择语句、迭代语句或 try 块是结构化块。 跳转语句、标记语句或声明语句不是结构化块。

  • team

    在构造执行中进行协作的一个或多个线程。

  • 线程 (thread)

    执行实体,具有串行控制流、私有变量集以及对共享变量的访问权限。

  • variable

    命名对象的标识符(可以选择通过命名空间名称进行限定)。

1.3 执行模型

OpenMP 使用并行执行的分支-联接模型。 尽管此分支-联接模型可用于解决各种问题,但它专为基于数组的大型应用程序而定制。 OpenMP 旨在支持作为并行程序(许多执行线程和完整 OpenMP 支持库)正确执行的程序。 它还适用于作为顺序程序(忽略指令,具有简单的 OpenMP 存根库)正确执行的程序。 但是,可以并允许开发在按顺序执行时无法正常运行的程序。 此外,由于数字运算关联的变化,不同程度的并行可能会导致不同的数字结果。 例如,串行加法缩减可能具有与并行缩减不同的加法关联模式。 这些不同的关联可能会更改浮点加法的结果。

使用 OpenMP C/C++ API 编写的程序作为单个执行线程(称为主线程)开始执行。 主线程会在串行区域中执行,直到遇到第一个并行构造。 在 OpenMP C/C++ API 中,parallel 指令构成并行构造。 遇到并行构造时,主线程会创建线程团队,主线程会成为团队的主成员。 团队中的每个线程都在并行区域的动态盘区中执行语句(工作共享构造除外)。 团队中的所有线程都必须按相同顺序遇到工作共享构造,并且一个或多个线程会在关联结构化块中执行语句。 在没有 nowait 子句的工作共享构造末尾隐含的屏障会由团队中的所有线程执行。

如果线程修改共享对象,则它不仅影响其自己的执行环境,还会影响程序中其他线程的环境。 从另一个线程的角度来看,仅当对象声明为易失性时,才能保证修改在下一个序列点(基础语言中定义)完成。 否则,会保证修改在第一个修改线程后完成。 其他线程随后(或同时)看到一个指定对象(隐式或显式)的 flush 指令。 当其他 OpenMP 指令隐含的 flush 指令不保证副作用的正确排序时,由程序员负责提供其他显式 flush 指令。

并行构造完成时,团队中的线程会在隐式屏障处同步,只有主线程才会继续执行。 可以在单个程序中指定任意数量的并行构造。 因此在执行期间,程序可能会多次创建分支和联接。

OpenMP C/C++ API 允许程序员在从并行构造内调用的函数中使用指令。 未出现在并行构造的词法盘区中但可能处于动态盘区中的指令称为孤立指令。 借助孤立指令,程序员可以并行执行其程序的主要部分,只需对顺序程序进行极少的更改即可。 借助此功能,可以在程序调用树的顶层对并行构造进行编码,并使用指令在任何调用函数中控制执行。

对写入相同文件的 C 和 C++ 输出函数的非同步调用可能会导致输出中由不同线程写入的数据以不确定的顺序出现。 同样,对从相同文件读取的输入函数的非同步调用可能会以不确定的顺序读取数据。 I/O 的非同步使用(以便每个线程访问不同的文件)会生成与 I/O 函数的串行执行相同的结果。

1.4 遵从性

如果可识别并保留此规范的所有元素的语义(如第 1、2、3、4 章和附录 C 中所述),则 OpenMP C/C++ API 的实现符合 OpenMP 标准。附录 A、B、D、E 和 F 仅供参考,不属于规范的一部分。 仅包含 API 子集的实现不符合 OpenMP 标准。

OpenMP C 和 C++ API 是实现所支持的基础语言的扩展。 如果基础语言不支持本文档中出现的语言构造或扩展,则 OpenMP 实现不需要支持它。

所有标准 C 和 C++ 库函数以及内置函数(即编译器具体了解的函数)必须是线程安全的。 并行区域中不同线程对线程安全函数的非同步使用不会产生未定义的行为。 但是,行为可能与串行区域中的行为不同。 (随机数生成函数是一个示例。)

OpenMP C/C++ API 指定某些行为是实现定义的。在这些情况下,符合 OpenMP 标准的实现需要定义并记录其行为。 有关实现定义的行为的列表,请参阅附录 E

1.5 规范引用

  • ISO/IEC 9899:1999,信息技术 - 编程语言 - C。此 OpenMP API 规范将 ISO/IEC 9899:1999 引用为 C99。

  • ISO/IEC 9899:1990,信息技术 - 编程语言 - C。此 OpenMP API 规范将 ISO/IEC 9899:1990 引用为 C90。

  • ISO/IEC 14882:1998,信息技术 - 编程语言 - C++。 此 OpenMP API 规范将 ISO/IEC 14882:1998 引用为 C++。

如果此 OpenMP API 规范引用 C,则会引用实现所支持的基础语言。

1.6 组织