Linux openmp教程,OpenMP中文教程

本文是针对 OpenMP 的基础教程,介绍了 OpenMP 的编程模型,包括共享内存并行、线程管理和数据范围。讲解了 OpenMP 的核心概念,如并行区域、工作共享、线程创建、数据共享与私有化、动态线程调整和并行循环调度。此外,还提到了环境变量和运行时库函数的作用,以及如何通过环境变量控制并行行为。适合初学者了解 OpenMP 并行编程的基本原理和实践。
摘要由CSDN通过智能技术生成

1、摘要

OpenMP 是一个应用程序接口(API),由一组主要的计算机硬件和软件供应商联合定义。OpenMP 为共享内存并行应用程序的开发人员提供了一个可移植的、可伸缩的模型。该API在多种体系结构上支持 C/C++ 和 Fortran。本教程涵盖了 OpenMP 3.1 的大部分主要特性,包括用于指定并行区域、工作共享、同步和数据环境的各种构造和指令。还将讨论运行时库函数和环境变量。本教程包括 C 和 Fortran 示例代码以及一个实验练习。

水平/先决条件:本教程非常适合那些刚接触 OpenMP 并行编程的人。需要对 C 语言或 Fortran 语言中的并行编程有基本的了解。对于那些通常不熟悉并行编程的人来说,EC3500: 并行计算导论中的材料将会很有帮助。

2、简介

2.1、什么是OpenMP

OpenMP是:

一种应用程序接口(API),可用于显式地指示多线程、共享内存并行性。

由三个主要的API组件组成:

编译器指令

运行时库函数

环境变量

Open Multi-Processing的缩写

OpenMP的目标

标准化

在各种共享内存架构/平台之间提供一个标准。

由一组主要的计算机硬件和软件供应商联合定义和认可。

至精至简

为共享内存机器建立一组简单且有限的指令。

重要的并行性可以通过使用3或4个指令来实现。

显然,随着每个新版本的发布,这个目标变得越来越没有意义。

易用性

提供以增量方式并行化串行程序的能力,这与通常需要全有或全无方法的消息传递库不同。

提供实现粗粒度和细粒度并行的能力。

可移植性

为 C/C++ 和 FORTRAN 指定API。

大多数主要平台已经实现,包括Unix/Linux平台和Windows。

注意:本教程参考了OpenMP 3.1版。新版本的语法和特性目前还没有涉及。

3、OpenMP编程模型

3.1、共享内存模型

OpenMP是为多处理器或多核共享内存机器设计的。底层架构可以是共享内存 UMA 或 NUMA。

Uniform Memory Access 一致内存访问

Uniform Memory Access 非一致内存访问

9931c05f4058

9931c05f4058

因为OpenMP是为共享内存并行编程而设计的,所以它在很大程度上局限于单节点并行性。通常,节点上处理元素(核心)的数量决定了可以实现多少并行性。

3.2、在 HPC 中使用 OpenMP 的动机

OpenMP本身的并行性仅限于单个节点。

对于高性能计算(HPC - High Performance Computing)应用程序,OpenMP 与 MPI 相结合以实现分布式内存并行。这通常被称为混合并行编程。

OpenMP 用于每个节点上的计算密集型工作。

MPI 用于实现节点之间的通信和数据共享。

这使得并行性可以在集群的整个范围内实现。

Hybrid OpenMP-MPI Parallelism

9931c05f4058

3.3、基于线程的并行性

OpenMP 程序仅通过使用线程来实现并行性。

执行线程是操作系统可以调度的最小处理单元。一种可以自动运行的子程序,这个概念可能有助于解释什么是线程。

线程存在于单个进程的资源中。没有这个进程,它们就不复存在。

通常,线程的数量与机器处理器/核心的数量相匹配。但是,线程的实际使用取决于应用程序。

3.4、显式并行性

OpenMP 是一个显式的(而不是自动的)编程模型,为程序员提供了对并行化的完全控制。

并行化可以像获取串行程序和插入编译器指令一样简单…

或者像插入子程序来设置多个并行级别、锁甚至嵌套锁一样复杂

3.5、Fork - Join 模型

OpenMP 使用并行执行的 fork-join 模型:

9931c05f4058

所有 OpenMP 程序都开始于一个主线程。主线程按顺序执行,直到遇到第一个并行区域结构。

FORK:主线程然后创建一组并行线程。

之后程序中由并行区域结构封装的语句在各个团队线程中并行执行。

JOIN:当团队线程完成并行区域结构中的语句时,它们将进行同步并终止,只留下主线程。

并行区域的数量和组成它们的线程是任意的。

3.6、数据范围

因为 OpenMP 是共享内存编程模型,所以在默认情况下,并行区域中的大多数数据都是共享的。

一个并行区域中的所有线程都可以同时访问共享数据。

OpenMP 为程序员提供了一种方法,可以在不需要默认共享范围的情况下显式地指定数据的“作用域”。

数据范围属性子句将更详细地讨论这个主题。

3.7、嵌套的并行性

该 API 提供了在其他并行区域内放置并行区域的方法。

实现可能支持这个特性,也可能不支持。

3.8、动态线程

该 API 为运行时环境提供了动态更改线程数量的功能,这些线程用于执行并行区域。如有可能,旨在促进更有效地利用资源。

实现可能支持这个特性,也可能不支持。

3.9、I/O

OpenMP 没有指定任何关于并行 I/O 的内容。如果多个线程试图从同一个文件进行写/读操作,这一点尤其重要。

如果每个线程都对不同的文件执行 I/O,那么问题就不那么重要了。

完全由程序员来确保在多线程程序的上下文中正确地执行 I/O。

3.10、内存模型:经常刷新?

OpenMP 提供了线程内存的“宽松一致性”和“临时”视图(用他们的话说)。换句话说,线程可以“缓存”它们的数据,并且不需要始终与实际内存保持精确的一致性。

当所有线程以相同的方式查看共享变量非常重要时,程序员负责确保所有线程根据需要刷新该变量。

4、OpenMP API 概述

4.1、三大构成

OpenMP 3.1 API 由三个不同的组件组成:

编译器指令

运行时库函数

环境变量

后来的一些 API 包含了这三个相同的组件,但是增加了指令、运行时库函数和环境变量的数量。

应用程序开发人员决定如何使用这些组件。在最简单的情况下,只需要其中的几个。

实现对所有 API 组件的支持各不相同。例如,一个实现可能声明它支持嵌套并行,但是 API 清楚地表明它可能被限制在一个线程上——主线程。不完全符合开发人员的期望?

4.2、编译器指令

编译器指令在源代码中以注释的形式出现,编译器会忽略它们,除非您另外告诉它们 — 通常通过指定适当的编译标志,如后面的编译部分所述。

OpenMP 编译器指令用于各种目的:

生成一个并行区域

在线程之间划分代码块

在线程之间分配循环迭代

序列化代码段

线程之间的工作同步

编译器指令有以下语法:

sentinel directive-name [clause, ...]

例如:

#pragma omp parallel default(shared) private(beta, pi)

后面将详细讨论编译器指令。

4.3、运行时库函数 Run-time Library Routines:

OpenMP API 包括越来越多的运行时库函数。

这些程序用于各种目的:

设置和查询线程的数量

查询线程的唯一标识符(线程ID)、父线程的标识符、线程团队大小

设置和查询动态线程特性

查询是否在一个并行区域,以及在什么级别

设置和查询嵌套并行性

设置、初始化和终止锁以及嵌套锁

查询 wall clock time 和分辨率

对于 C/C++,所有运行时库函数都是实际的子程序。对于Fortran来说,有些是函数,有些是子程序。例如:

#include

int omp_get_num_threads(void)

注意,对于C/C++,通常需要包含 头文件。

运行时库函数将在运行时库函数一节中作为概述进行简要讨论,更多细节将在附录A中讨论。

4.4、环境变量

OpenMP 提供了几个环境变量,用于在运行时控制并行代码的执行。

这些环境变量可以用来控制这些事情:

设置线程数

指定如何划分循环交互

将线程绑定到处理器

启用/禁用嵌套的并行性;设置嵌套并行度的最大级别

启用/禁用动态线程

设置线程堆栈大小

设置线程等待策略

设置 OpenMP 环境变量的方法与设置任何其他环境变量的方法相同,并且取决于您使用的是哪种 shell。例如:

csh/tcsh: setenv OMP_NUM_THREADS 8

sh/bash: export OMP_NUM_THREADS=8

稍后将在环境变量一节中讨论 OpenMP 环境变量。

4.5、OpenMP代码结构示例

#include

main () {

int var1, var2, var3;

串行代码 `Serial code`

.

.

.

并行区域的开始。派生一组线程。 `Beginning of parallel region. Fork a team of threads.`

指定变量作用域 `Specify variable scoping `

#pragma omp parallel private(var1, var2) shared(var3)

{

由所有线程执行的并行区域 `Parallel region executed by all threads`

.

其他 OpenMP 指令 `Other OpenMP directives`

.

运行时库调用 `Run-time Library calls`

.

所有线程加入主线程并解散 `All threads join master thread and disband`

}

恢复串行代码 `Resume serial code`

.

.

.

}

5、编译 OpenMP 程序

OpenMP 版本依赖的 GCC 版本

OpenMP 版本

GCC版本

OpenMP 5.0

>= GCC 9.1

OpenMP 4.5

>= GCC 6.1

OpenMP 4.0

>= GCC 4.9.0

OpenMP 3.1

>= GCC 4.7.0

OpenMP 3.0

>= GCC 4.4.0

OpenMP 2.5

>= GCC 4.2.0

g++ Test.cpp -o omptest -fopenmp

6、OpenMP 指令

6.1、C/C++ 指令格式

格式

#pragma omp

directive-name

[clause, ...]

newline

所有 OpenMP C/C++ 指令都需要。

一个有效的 OpenMP 指令。必须出现在 pragma 之后和任何子句之前。

可选的。除非另有限制,子句可以按任何顺序重复。

必需的。在此指令所包含的结构化块之前。

示例

#pragma omp parallel default(shared) private(beta, pi)

一般规则

区分大小写。

指令遵循 C/C++ 编译器指令标准的约定。

每个指令只能指定一个指令名。

每个指令最多应用于一个后续语句,该语句必须是一个结构化块。

长指令行可以通过在指令行的末尾使用反斜杠(“\”)来转义换行符,从而在后续的行中“继续”。

6.2、指令范围

静态(词法)范围

在指令后面的结构化块的开始和结束之间以文本形式封装的代码。

指令的静态范围不跨越多个程序或代码文件。

孤立的指令

一个 OpenMP 指令,独立于另一个封闭指令,称为孤立型指令。它存在于另一个指令的静态(词法)范围之外。

将跨越程序和可能的代码文件。

动态范围

指令的动态范围包括静态(词法)范围和孤立指

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值