25.06.2015 Views

clause

clause

clause

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

机 群 应 用 开 发<br />

并 行 编 程 原 理 及<br />

程 序 设 计<br />

Parallel Programming:<br />

Fundamentals and Implementation<br />

戴 荣<br />

dair@dawning.com.cn<br />

曙 光 信 息 产 业 有 限 公 司<br />

2006.4<br />

2006 年 4 月 共 享 存 储 编 程 1/108


参 考 文 献<br />

黄 铠 , 徐 志 伟 著 , 陆 鑫 达 等 译 . 可 扩 展 并 行 计 算 技 术 , 结 构 与 编 程 . 北 京 : 机<br />

械 工 业 出 版 社 , P.33~56,P.227~237, 2000.<br />

陈 国 良 著 . 并 行 计 算 — 结 构 、 算 法 、 编 程 . 北 京 : 高 等 教 育 出 版 社 ,1999.<br />

Barry Wilkinson and Michael Allen. Parallel Programming(Techniques<br />

and Applications using Networked Workstations and Parallel<br />

Computers). Prentice Hall, 1999.<br />

李 晓 梅 , 莫 则 尧 等 著 . 可 扩 展 并 行 算 法 的 设 计 与 分 析 . 北 京 : 国 防 工 业 出<br />

版 社 ,2000.<br />

张 宝 琳 , 谷 同 祥 等 著 . 数 值 并 行 计 算 原 理 与 方 法 . 北 京 : 国 防 工 业 出 版<br />

社 ,1999.<br />

都 志 辉 著 . 高 性 能 计 算 并 行 编 程 技 术 —MPI 并 行 程 序 设 计 . 北 京 : 清 华<br />

大 学 出 版 社 , 2001.<br />

2006 年 4 月 共 享 存 储 编 程 2/108


相 关 网 址<br />

MPI: http://ww.mpi-forum.org,<br />

http://www.mcs.anl.gov/mpi<br />

Pthreads: http://www.oreilly.com<br />

PVM: http://www.epm.ornl.gov/pvm/<br />

OpemMP: http://www.openmp.org<br />

网 上 搜 索 :www.google.com<br />

2006 年 4 月 共 享 存 储 编 程 3/108


共 享 存 储 编 程<br />

Programming with Shared<br />

Memory<br />

2006 年 4 月 共 享 存 储 编 程 4/108


共 享 存 储 并 行 机 模 型<br />

体 系 结 构 特 点 ::<br />

• 多 台 处 理 机 通 过 互 联 网 络 共 享 一 个<br />

统 一 的 内 存 空 间 ,, 通 过 单 一 内 存 地 址 来<br />

实 现 处 理 机 间 的 协 调 ..<br />

• 内 存 空 间 也 可 由 多 个 存 储 器 模 块 构<br />

成 ..<br />

• 每 台 处 理 机 可 以 执 行 相 同 或 不 同 的<br />

指 令 流 ,, 每 台 处 理 机 可 以 直 接 访 问 到 所<br />

有 数 据 ..<br />

• 处 理 机 间 通 信 是 借 助 于 共 享 主 存 来<br />

实 现 的 ..<br />

• 可 扩 展 性 差 ,, 当 处 理 机 需 要 同 时 访 问<br />

共 享 全 局 变 量 时 ,, 产 生 内 存 竞 争 现 象 而<br />

严 重 影 响 效 率 ,, 比 较 适 合 中 小 规 模 应 用<br />

问 题 的 计 算 和 事 务 处 理 ..<br />

2006 年 4 月 共 享 存 储 编 程 5/108


共 享 存 储 编 程 标 准 与 特 点<br />

共 享 存 储 器 编 程 标 准<br />

Pthreads( 线 程 标 准 )<br />

X3H5( 线 程 标 准 )<br />

OpenMP( 最 常 用 的 共 享 存 储 并 行 编 程 方 式 , 是 我 们 讨 论 的 重 点 .)<br />

共 享 存 储 器 编 程 特 点<br />

显 式 多 线 程 库 调 用 .(Pthreads).<br />

编 译 制 导 语 句 ,OpenMP 等 .<br />

语 言<br />

C,Fortran77,Fortran90/95,C++…<br />

2006 年 4 月 共 享 存 储 编 程 6/108


并 行 编 程 标 准<br />

线 程 库 标 准 (Thread Library)<br />

– Win32 API.<br />

– POSIX threads 线 程 模 型 .<br />

– X3H5: 概 念 性 线 程 模 型<br />

编 译 制 导 (Compiler Directives)<br />

– OpenMP - portable shared memory parallelism.<br />

2006 年 4 月 共 享 存 储 编 程 7/108


为 什 么 流 行 多 线 程 编 程 ?<br />

线 程 : 在 进 程 的 内 部 执 行 的 指 令 序 列 .<br />

相 对 于 进 程 , 线 程 开 销 小 :<br />

创 建 一 个 线 程 的 时 间 大 约 是 建 立 一 个 新 进 程 的 1/30。 如 在 Sun4/75 工 作<br />

上 站 上 , 创 建 一 个 非 绑 定 线 程 约 为 52 微 秒 , 而 fork() 一 次 的 时 间 为<br />

1700 微 秒 。<br />

线 程 同 步 时 间 约 是 进 程 同 步 时 间 的 1/3.<br />

线 程 与 RPC 相 结 合 , 发 挥 多 处 理 机 的 处 理 能 力 ;<br />

发 挥 多 处 理 器 的 处 理 能 力 ;<br />

开 发 程 序 的 并 发 性 , 改 善 程 序 的 结 构 .<br />

容 易 实 现 数 据 共 享 : 由 于 线 程 共 用 内 存 地 址 , 因 此 可 实 现 数 据 共 享<br />

例 : 一 高 性 能 Web 服 务 器 可 为 每 一 打 开 链 接 的 浏 览 器 分 配 一 个 线 程 , 所<br />

有 线 程 即 可 共 用 同 一 cache 来 访 问 网 站 的 热 点 话 题<br />

统 一 的 标 准 :<br />

以 前 各 开 发 商 提 供 互 不 兼 容 的 线 程 库 , 结 果 导 致 多 线 程 程 序 不 能 很 好 地<br />

移 值 。 自 1995 年 的 POSIX 线 程 标 准 实 施 之 后 , 极 大 地 促 进 多 线 程 编 程 的<br />

统 一 。 各 系 统 都 支 持 Pthreads, 如 Linux、SUN、IBM AIX 等 。<br />

2006 年 4 月 共 享 存 储 编 程 8/108


Pthreads 线 程 模 型<br />

POSIX1003.4a 小 组 研 究 多 线 程 编 程 标 准 . 当 标 准 完 成 后 , 大 多 数 支<br />

持 多 线 程 的 系 统 都 支 持 POSIX 接 口 . 很 好 的 改 善 了 多 线 程 编 程 的 可 移<br />

植 性 .<br />

IEEE Portable Operating System Interface, POSIX,<br />

1003.1-1995 标 准 :POSIX 线 程 模 型 :pthreads.<br />

2006 年 4 月 共 享 存 储 编 程 9/108


线 程 管 理 (Pthread<br />

为 例 )<br />

创 建 :pthread_create<br />

终 止 :pthread_exit<br />

汇 合 :pthread_join<br />

分 离 :pthread_detach<br />

线 程 属 性 初 始 化 :pthread_attr_init<br />

唯 一 执 行 :pthread_once<br />

2006 年 4 月 共 享 存 储 编 程 10/108


同 步 对 象<br />

在 共 享 存 储 多 处 理 器 并 行 机 上 , 线 程 通 过 全 局 变 量 通 信 ,<br />

对 于 全 局 变 量 的 操 作 必 须 进 行 同 步 。<br />

pthread 提 供 两 个 线 程 同 步 原 语 : 互 斥 和 条 件 变 量 .<br />

2006 年 4 月 共 享 存 储 编 程 11/108


互 斥 锁 函 数<br />

函 数<br />

Mutex_init()<br />

Mutext_lock()<br />

Mutex_trylock()<br />

Mutex_unlock()<br />

Mutex_destroy()<br />

操 作<br />

初 始 化 一 个 互 斥 锁<br />

阻 塞 式 加 锁 操 作<br />

非 阻 塞 式 加 锁 操 作<br />

解 锁<br />

解 除 互 斥 状 态<br />

2006 年 4 月 共 享 存 储 编 程 12/108


条 件 变 量 的 函 数<br />

函 数<br />

操 作<br />

pthread_cond_init() 初 始 化 条 件 变 量<br />

pthread_cond_wait() 阻 塞 直 至 条 件 为 真<br />

pthread_cond_signal() 强 制 条 件 为 真 , 解 除 等 待 条 件 的 线 程 的 阻 塞<br />

pthread_cond_timedwait() 阻 塞 直 到 指 定 条 件 为 真 或 timeout<br />

pthread_cond_broadcast() 解 除 所 有 等 待 条 件 的 线 程 的 阻 塞<br />

pthread_cond _destroy() 销 毁 条 件 变 量<br />

2006 年 4 月 共 享 存 储 编 程 13/108


Hello World(1)<br />

#include <br />

#include "stdio.h"<br />

void *worker();<br />

main()<br />

{<br />

pthread_t thread;<br />

pthread_create(&thread,NULL,worker,NULL);<br />

pthread_join(thread,NULL);<br />

}<br />

void *worker()<br />

{<br />

printf("Hello World!\n");<br />

}<br />

编 译 命 令<br />

gcc gcc hello.c –lpthread<br />

运 行 结 果<br />

Hello World!<br />

2006 年 4 月 共 享 存 储 编 程 14/108


pthread_t 线 程 数 据 类 型<br />

pthread_create(&thread,NULL,worker,NULL);<br />

函 数 pthread_create() 用 于 创 建 一 新 的 线 程 , 新 线 程 一 旦 建 立 便 进 入 运<br />

行 状 态<br />

参 数 :<br />

⌧ 线 程 指 针 或 句 柄<br />

⌧ 线 程 属 性 变 量 , 属 性 参 数 : 默 认 为 NULL. 属 性 对 象 一 旦 建 立 可 以 用 于<br />

创 建 多 个 具 有 共 同 属 性 的 线 程 , 线 程 创 建 后 , 可 删 除 属 性 对 象 .<br />

⌧ 线 程 要 执 行 的 函 数<br />

⌧ 传 入 该 执 行 函 数 的 一 个 参 数 , 无 则 NULL. 可 以 是 任 意 类 型<br />

线 程 的 终 止<br />

线 程 函 数 正 常 终 止 返 回 ;<br />

自 调 用 pthear_exit() 函 数 ;<br />

线 程 被 取 消 ;<br />

线 程 接 收 到 中 止 的 信 号 ;<br />

当 主 进 程 执 行 exit() 后 , 进 程 及 其 全 部 线 程 全 部 终 止 .<br />

2006 年 4 月 共 享 存 储 编 程 15/108


pthread_join(pthread_t wait_for,void** status);<br />

等 待 直 到 线 程 结 束 ;<br />

执 行 该 函 数 的 线 程 发 生 阻 塞 , 直 到 由 wait_for 指 定 的 线 程 终 止 ;<br />

等 与 被 等 的 两 线 程 必 须 是 同 一 进 程 内 部 的 线 程 ( 而 且 不 是 分 离 线<br />

程 );<br />

返 回 值<br />

⌧0<br />

⌧ESRCH<br />

程 ;<br />

成 功 返 回<br />

⌧EINVAL 线 程 参 数 无 效 ;<br />

⌧EDEADLK 等 待 自 身 结 束 .<br />

参 数 wait_for 指 定 的 线 程 不 存 在 或 是 一 分 离 线<br />

不 能 有 两 个 线 程 同 时 等 待 同 一 个 线 程 的 结 束 , 否 则 其 中 一 个 线 程<br />

正 常 返 回 , 另 外 一 个 返 回 ESRCH 错 误 .<br />

2006 年 4 月 共 享 存 储 编 程 16/108


Hello World(2)<br />

#include <br />

#include "stdio.h"<br />

Hello World from from thread 0! 0!<br />

#define numthrds 5<br />

Hello World from from thread 1! 1!<br />

pthread_t *tid;<br />

Hello World from from thread 2! 2!<br />

void *worker();<br />

Hello World from from thread 3! 3!<br />

main(){<br />

Hello World from from thread 4! 4!<br />

int i;<br />

tid = (pthread_t*) calloc(numthrds,sizeof(pthread_t));<br />

for(i=0;i


Hello World(3)<br />

#include <br />

#include "stdio.h"<br />

#define numthrds 5<br />

pthread_t *tid;<br />

pthread_mutex_t mutex;<br />

int sum=0;<br />

void *worker();<br />

main(){<br />

int i;<br />

}<br />

void *worker(){<br />

int myid,num;<br />

myid = pthread_self() - tid[0];<br />

printf("%d was added to the<br />

sum in thread %d\n",myid*10,myid);<br />

pthread_mutex_lock(&mutex);<br />

sum += myid*10;<br />

pthread_mutex_unlock(&mutex);<br />

return;<br />

}<br />

tid = (pthread_t*) calloc(numthrds,sizeof(pthread_t));<br />

pthread_mutex_init(&mutex,NULL);<br />

for(i=0;i


运 行 结 果<br />

0 was added to the sum in thread 0<br />

10 was added to the sum in thread 1<br />

20 was added to the sum in thread 2<br />

30 was added to the sum in thread 3<br />

40 was added to the sum in thread 4<br />

The sum is 100<br />

2006 年 4 月 共 享 存 储 编 程 19/108


基 于 多 线 程 编 程 的 PI 求 解<br />

2<br />

2<br />

∫<br />

1<br />

0<br />

1−<br />

x<br />

dx<br />

=<br />

2 π<br />

4<br />

2006 年 4 月 共 享 存 储 编 程 20/108


#include<br />

#include<br />

<br />

<br />

#include<br />

#include<br />

"stdio.h"<br />

"stdio.h"<br />

pthread_mutex_t<br />

pthread_mutex_t<br />

reduction_mutex;<br />

reduction_mutex;<br />

pthread_t<br />

pthread_t<br />

*tid;<br />

*tid;<br />

double<br />

double<br />

pi,w;<br />

pi,w;<br />

int<br />

int<br />

n;<br />

n;<br />

int<br />

int<br />

num_threads;<br />

num_threads;<br />

double<br />

double<br />

f(a)<br />

f(a)<br />

double<br />

double<br />

a;<br />

a;<br />

{<br />

{<br />

return<br />

return<br />

(4.0/(1.0<br />

(4.0/(1.0<br />

+<br />

+<br />

a*a));<br />

a*a));<br />

}<br />

}<br />

void<br />

void<br />

*PIworker(void*<br />

*PIworker(void*<br />

arg)<br />

arg)<br />

{<br />

{<br />

int<br />

int<br />

i,myid;<br />

i,myid;<br />

double<br />

double<br />

sum,mypi,x;<br />

sum,mypi,x;<br />

/*set<br />

/*set<br />

individual<br />

individual<br />

id<br />

id<br />

to<br />

to<br />

start<br />

start<br />

at<br />

at<br />

0 */<br />

*/<br />

myid<br />

myid<br />

=<br />

=<br />

pthread_self()<br />

pthread_self()<br />

-<br />

-<br />

tid[0];<br />

tid[0];<br />

/*integrate<br />

/*integrate<br />

function*/<br />

function*/<br />

sum=0.0;<br />

sum=0.0;<br />

for(i<br />

for(i<br />

= myid<br />

myid<br />

+ 1;i<br />

1;i<br />


gcc gcchello.c –lpthread<br />

a.out a.out 1000 10005<br />

computed pi pi = 3.1415927369231271<br />

转 去 Openmp<br />

void<br />

void<br />

main(argc,argv)<br />

main(argc,argv)<br />

int<br />

int<br />

argc;<br />

argc;<br />

char*<br />

char*<br />

argv[];<br />

argv[];<br />

{<br />

{<br />

int<br />

int<br />

i;<br />

i;<br />

/*check<br />

/*check<br />

command<br />

command<br />

line<br />

line<br />

*/<br />

*/<br />

if(argc<br />

if(argc<br />

!=<br />

!=<br />

3)<br />

3)<br />

{<br />

{<br />

printf("Usage:<br />

printf("Usage:<br />

%s<br />

%s<br />

Num_intervals<br />

Num_intervals<br />

Num_threads\n",argv[0]);<br />

Num_threads\n",argv[0]);<br />

exit(0);<br />

exit(0);<br />

}<br />

}<br />

/*get<br />

/*get<br />

num<br />

num<br />

intervals<br />

intervals<br />

and<br />

and<br />

num<br />

num<br />

threads<br />

threads<br />

from<br />

from<br />

command<br />

command<br />

line*/<br />

line*/<br />

n =<br />

=<br />

atoi(argv[1]);<br />

atoi(argv[1]);<br />

num_threads<br />

num_threads<br />

=<br />

=<br />

atoi(argv[2]);<br />

atoi(argv[2]);<br />

w =<br />

=<br />

1.0<br />

1.0<br />

/<br />

/<br />

(double)n;<br />

(double)n;<br />

pi<br />

pi<br />

=<br />

=<br />

0.0;<br />

0.0;<br />

tid<br />

tid<br />

=<br />

=<br />

(pthread_t*)<br />

(pthread_t*)<br />

calloc<br />

calloc<br />

(num_threads,sizeof(pthread_t));<br />

(num_threads,sizeof(pthread_t));<br />

/*initilize<br />

/*initilize<br />

lock*/<br />

lock*/<br />

if(pthread_mutex_init(&reduction_mute<br />

if(pthread_mutex_init(&reduction_mute<br />

x,NULL)){<br />

x,NULL)){<br />

fprintf(stderr,"Cannot<br />

fprintf(stderr,"Cannot<br />

init<br />

init<br />

lock\n");<br />

lock\n");<br />

exit(1);<br />

exit(1);<br />

}<br />

}<br />

/*create<br />

/*create<br />

the<br />

the<br />

threads*/<br />

threads*/<br />

for(i<br />

for(i<br />

= 0;<br />

0;<br />

i


多 线 程 并 行 编 程 特 点<br />

pthread_create() 创 建 一 个 新 线 程 比 重 新 启 动 一 个 线 程 花 费 的 时 间<br />

少 : 需 要 时 创 建 + 任 务 结 束 立 刻 杀 掉 vs. 维 护 一 大 堆 的 空 闲 线 程 并 且<br />

相 互 切 换 .<br />

在 加 锁 的 前 提 下 访 问 共 享 资 源<br />

不 支 持 数 据 并 行 , 适 合 于 任 务 级 并 行 , 即 一 个 线 程 单 独 执 行 一 个 任 务 ;<br />

不 支 持 增 量 并 行 化 , 对 于 一 个 串 行 程 序 , 很 难 用 Pthreads 进 行 并 行 化<br />

Pthreads 主 要 是 面 向 操 作 系 统 , 而 不 是 为 高 性 能 计 算 设 计 的 , 因 此<br />

不 是 并 行 计 算 程 序 设 计 的 主 流 平 台 。 但 是 “ 多 线 程 并 发 执 行 ” 这 种 思<br />

想 却 被 广 泛 地 应 用 于 高 性 能 计 算 。 这 就 是 我 们 即 将 要 讲 的 共 享 存 储<br />

并 行 编 程 的 另 外 一 种 被 并 行 机 制 造 商 和 广 用 并 行 计 算 用 户 广 泛 接 受<br />

的 平 台 :OpenMP<br />

2006 年 4 月 共 享 存 储 编 程 23/108


并 行 编 程 标 准<br />

线 程 库 标 准 (Thread Library)<br />

– Win32 API.<br />

– POSIX threads 线 程 模 型 .<br />

– X3H5: 概 念 性 线 程 模 型<br />

编 译 制 导 (Compiler Directives)<br />

– OpenMP - portable shared memory parallelism.<br />

2006 年 4 月 共 享 存 储 编 程 24/108


www.openmp.org<br />

•An Industry Standard API for Shared Memory Programming<br />

•An API for Writing Multithreaded Applications<br />

• 一 系 列 编 译 制 导 语 句 和 库 函 数<br />

• 使 得 Fortran, C and C++ 的 多 线 程 编 程 更 加 容 易<br />

2006 年 4 月 共 享 存 储 编 程 25/108


与 X3H5 的 关 系<br />

X3H5 是 ANSI/X3 授 权 的 小 组 委 员 会 , 主 要 目 的 是 在 PCF(the<br />

Parallel Computing Forum) 工 作 的 基 础 上 , 发 展 并 行 计 算 的 一 个<br />

ANSI 标 准 . PCF 是 一 非 正 式 的 工 业 组 织 , 虽 在 DO 循 环 的 并 行 化 方 法<br />

的 标 准 化 方 面 做 一 些 工 作 , 但 在 起 草 拟 了 一 个 标 准 后 就 草 草 收 场 .<br />

OpenMP 专 门 针 对 这 类 并 行 化 问 题 , 并 完 成 了 这 项 工 作 , 同 时 得 到 工<br />

业 界 的 广 泛 支 持 .<br />

2006 年 4 月 共 享 存 储 编 程 26/108


ANSI X3H5 共 享 编 程 标 准<br />

概 念 性 的 编 程 模 型 (ANSI 标 准 (1993))<br />

没 有 任 何 商 品 化 的 共 享 存 储 器 系 统 依 附 于 X3H5, 但 X3H5 的 基 本 概 念 影<br />

响 以 后 共 享 存 储 器 系 统 的 并 行 编 程 .( 一 些 基 本 概 念 在 OpenMP 均 出 现 !)<br />

X3H5 支 持 C,Fortran77 以 及 Fortran90 语 言 .<br />

X3H5 规 定 的 基 本 的 并 行 结 构 用 于 并 行 性 表 述 :<br />

并 行 块 ( 分 散 任 务 Work Sharing)<br />

并 行 循 环<br />

单 进 程<br />

psections<br />

psections<br />

{<br />

{<br />

…<br />

}<br />

}<br />

end<br />

end<br />

psections<br />

psections<br />

parallel<br />

parallel<br />

{<br />

{<br />

…<br />

}<br />

}<br />

end<br />

end<br />

parallel<br />

parallel<br />

pdo<br />

pdo<br />

{<br />

{<br />

…<br />

}<br />

}<br />

end<br />

end<br />

pdo<br />

pdo<br />

psingle<br />

psingle<br />

{<br />

{<br />

…<br />

}<br />

}<br />

end<br />

end<br />

psingle<br />

psingle<br />

2006 年 4 月 共 享 存 储 编 程 27/108


X3H5 编 程 实 例<br />

program main<br />

! 程 序 以 顺 序 模 式 执 行<br />

A<br />

!A 只 由 基 本 线 程 执 行<br />

线 程 P Q R<br />

parallel<br />

! 转 换 成 并 行 模 式<br />

B<br />

!B 为 每 个 组 员 所 复 制<br />

psections ! 并 行 块 开 始<br />

隐 式 barrier<br />

section<br />

B B B<br />

C<br />

! 一 个 组 员 执 行 C<br />

section<br />

C D<br />

D<br />

! 另 一 个 组 员 执 行 D<br />

隐 式 barrier<br />

end psections ! 等 待 C 和 D 都 结 束<br />

E<br />

psingle<br />

暂 时 转 换 成 顺 序 模 式<br />

E<br />

!E 只 能 被 一 个 组 员 执 行 隐 式 barrier<br />

end psingle ! 转 回 并 行 模 式<br />

F(1:2) F(3:4) F(5:6)<br />

pdo I=1,6 ! 并 行 do 循 环 开 始<br />

无 隐 式 barrier<br />

F(i)<br />

! 各 线 程 分 担 循 环 任 务<br />

G G G<br />

end pdo no wait ! 无 隐 式 路 障 同 步<br />

隐 式 barrier<br />

G<br />

! 更 多 的 复 制 代 码<br />

end parallel<br />

! 结 束 并 行 模 式<br />

H<br />

H<br />

! 根 进 程 执 行 H<br />

…<br />

! 更 多 的 并 行 构 造<br />

各 线 程 以 负 载 平 衡 方 式 分 担 任 务<br />

2006 end年 4 月 共 享 存 储 编 程 28/108<br />

各 线 程 以 负 载 平 衡 方 式 分 担 任 务<br />

可 能 为 :F(1:1),F(2:2),F(3:6)…


X3H5 例 程 执 行 过 程 描 述<br />

程 序 以 顺 序 方 式 启 动 , 此 时 只 有 一 个 初 始 化<br />

线 程 , 称 为 基 本 线 程 或 主 线 程 . 当 程 序 遇 到<br />

parallel 时 , 通 过 派 生 多 个 子 线 程 转 换 为 并<br />

行 执 行 模 式 ( 线 程 数 隐 式 决 定 ). 基 本 线 程 与<br />

它 的 子 线 程 形 成 一 个 组 . 所 有 组 员 并 行 处 理<br />

后 继 并 行 代 码 , 直 至 end parallel. 然 后 程<br />

序 转 为 顺 序 模 式 , 只 有 基 本 线 程 继 续 执 行 .<br />

子 线 程 遇 到 内 部 并 行 或 任 务 分 担 构 造 时 , 可<br />

以 继 续 派 生 其 子 线 程 , 从 而 成 为 一 个 新 组 的<br />

基 本 线 程 .<br />

线 程 间 同 步 , 通 信 与 交 互<br />

隐 式 路 障 :parallel, end parallel,<br />

end pdo 或 end psingle 处 隐 式<br />

barrier. 如 果 不 需 , 则 加 no wait;<br />

各 处 理 机 通 过 全 局 变 量 通 信 , 通 过 私 有<br />

变 量 封 装 数 据<br />

顺 序 执 行<br />

fork<br />

…...<br />

barrier<br />

顺 序 执 行<br />

…<br />

并 行 执 行<br />

2006 年 4 月 共 享 存 储 编 程 29/108


OpenMP: 并 行 模 型<br />

Fork-Join 并 行 模 式 :<br />

主 线 程 根 据 需 要 创 建 一 组 子 线 程 进 行 工 作 分 担 .<br />

可 对 串 行 程 序 进 行 逐 步 并 行 化 .<br />

主 线 程<br />

并 行 执 行 区 域<br />

2006 年 4 月 共 享 存 储 编 程 30/108


如 何 应 用 OpenMP?<br />

OpenMP 常 用 于 循 环 并 行 化 :<br />

– 找 出 最 耗 时 的 循 环 .<br />

– 将 循 环 由 多 线 程 完 成 .<br />

在 串 行 程 序 上 加 上 编 译 制 导 语 句 , 完 成 并 行 化 , 因 此 可 先 完 成 串 行<br />

程 序 , 然 后 再 进 行 OpenMP 并 行 化 .<br />

用 OpenMP 将 该 循 环 通 过 多 线 程 进 行 任 务 分 割<br />

void void main() main()<br />

{{<br />

double double Res[1000]; Res[1000];<br />

for(int for(inti=0;i


线 程 间 如 何 交 互 ?<br />

OpenMP 是 基 于 共 享 内 存 模 型 .<br />

线 程 通 过 共 享 变 量 通 信 .<br />

访 问 共 享 变 量 会 导 致 race condition ( 竞 态 状 态 )<br />

race condition: 是 一 种 状 态 , 在 这 种 状 态 下 两 个 实 体 ( 例 如 两 个 处<br />

理 过 程 ) 对 同 一 资 源 进 行 竞 争 , 而 系 统 没 有 一 种 机 制 来 测 定 首 先 要<br />

执 行 的 是 哪 一 个 。 因 此 , 由 于 系 统 不 能 保 证 数 据 的 正 确 处 理 , 其<br />

结 果 是 不 可 预 测 的 。<br />

为 了 避 免 线 程 进 入 竞 态 状 态 :<br />

通 过 同 步 对 象 来 保 护 数 据 冲 突 .<br />

2006 年 4 月 共 享 存 储 编 程 32/108


OpenMP 术 语<br />

大 多 OpenMP 构 造 是 制 导 语 句 或 pragmas.<br />

C 和 C++ 的 pragmas 形 式 为 :<br />

⌧#pragma omp construct [<strong>clause</strong> [<strong>clause</strong>]…]<br />

Fortran 中 , 制 导 语 句 形 式 为 以 下 几 种 :<br />

⌧C$OMP CONSTRUCT [<strong>clause</strong> [<strong>clause</strong>]…]<br />

⌧!$OMP CONSTRUCT [<strong>clause</strong> [<strong>clause</strong>]…]( 自 由 书 写 格 式 唯 一 )<br />

⌧*$OMP CONSTRUCT [<strong>clause</strong> [<strong>clause</strong>]…]<br />

⌧ 例 : 以 下 三 种 等 价 ( 第 一 行 为 列 数 )<br />

⌧C23456789<br />

⌧!$OMP PARALLEL DO SHARED(A,B,C)<br />

⌧C$OMP PARALLEL DO<br />

⌧C$OMP+SHARED(A,B,C)<br />

⌧C$OMP PARALLELDOSHARED(A,B,C)<br />

由 于 OpenMP 构 造 为 注 释 性 语 句 , 因 此 一 个 OpenMP 程 序 在 用 不 支 持<br />

OpenMP 的 编 译 器 编 译 后 , 仍 为 串 行 程 序 .<br />

2006 年 4 月 共 享 存 储 编 程 33/108


Structured blocks( 结 构 化 块 )<br />

结 构 化 块 性 质 :<br />

仅 在 块 顶 有 一 个 入 口 和 块 底 有 一 个 出 口 ;<br />

块 功 能 可 通 过 构 造 的 语 法 清 晰 地 识 别 ;<br />

块 内 除 Fortran 中 的 STOP 语 句 和 c/c++ 中 的 exit() 语 句 外 , 不 能 有 其<br />

它 分 支 .<br />

大 多 OpenMP 构 造 为 结 构 化 块 .<br />

C$OMP PARALLEL<br />

10 …<br />

…<br />

if(…) goto 10<br />

C$OMP END PARALLEL<br />

print *,id<br />

C$OMP PARALLEL<br />

10 …<br />

30 …<br />

if(…) goto 20<br />

go to 10<br />

C$OMP END PARALLEL<br />

if(…) goto 30<br />

20 print *, id<br />

一 个 结 构 化 块<br />

一 个 非 结 构 化 块<br />

2006 年 4 月 共 享 存 储 编 程 34/108


OpenMP 结 构 化 块 类 型<br />

OpenMP 主 要 有 五 类 结 构 化 块 :<br />

并 行 区 Parallel Regions<br />

任 务 分 割 Worksharing<br />

数 据 环 境 Data Environment<br />

同 步 Synchronization<br />

运 行 时 函 数 / 环 境 变 量<br />

在 Fortran,C/C++ 中 ,OpenMP 基 本 上 是 一 样 的 .<br />

2006 年 4 月 共 享 存 储 编 程 35/108


Parallel Regions( 并 行 区 )<br />

并 行 区 是 OpenMP 的 基 本 构 造 , 并 行 区 内 的 代 码 由 各 线 程 同 时 执 行 .<br />

当 一 个 线 程 执 行 “omp parallel” 后 , 建 立 一 组 线 程 , 该 线 程 成 为 新 建 立 的<br />

线 程 组 的 主 线 程 . 所 有 线 程 构 成 一 线 程 组 , 各 线 程 以 线 程 ID 区 分 , 主 线 程<br />

ID 为 0.<br />

线 程 组 并 行 执 行 并 行 区 内 代 码 .<br />

如 : 建 立 一 个 4 线 程 的 并 行 区 :<br />

每 一 线 程 以 不 同 的 线 程 ID ID<br />

和 相 同 的 参 数 A 执 行 并 行<br />

区 内 代 码 的 一 拷 贝 ..<br />

ID(=0,1,2,3).<br />

double A[1000];<br />

omp_set_num_threads(4);<br />

#pragma omp ompparallel<br />

{{<br />

int intID ID = omp_thread_num();<br />

worker(ID,A);<br />

}}<br />

2006 年 4 月 共 享 存 储 编 程 36/108


并 行 区 的 Lecical / dynamic extent<br />

以 及 Orphaned 制 所 语 句<br />

poo.f<br />

C$OMP PARALLEL<br />

call whoami<br />

C$OMP END END PARALLEL<br />

Static/lexical<br />

extent: 在 书 写 上 直 接<br />

包 含 在 并 行 区 内 的 部 分 .<br />

Dynamic extent: 包 括<br />

并 行 区 内 直 接 和 间 接 ( 函 数<br />

调 用 ) 包 含 的 内 容 , 也 被 称<br />

为 region.<br />

+<br />

bar.f<br />

subroutine whoami<br />

external omp_get_thread_num<br />

integer iam, iam, omp_get_thread_num<br />

iam iam = omp_get_thread_num()<br />

C$OMP C$OMP CRITICAL<br />

print*,’Hello from from ‘, ‘, iam iam<br />

C$OMP C$OMP END END CRITICAL<br />

return<br />

end end<br />

Orphan 制 导 语 句 : 落 在 子 程 序<br />

中 的 制 导 语 句 , 方 便 于 子 程 序 的<br />

并 行 化 , 免 去 传 统 的 inline 处 理<br />

2006 年 4 月 共 享 存 储 编 程 37/108


并 行 区 代 码 流 程<br />

每 一 线 程 执 行 相 同 代 码 , 不 , 同 数 据 ..<br />

Double A[1000]<br />

opm_set_num_threads(4)<br />

double A[1000];<br />

omp_set_num_threads(4);<br />

#pragma omp ompparallel<br />

{{<br />

int intID ID = omp_thread_num();<br />

worker(ID,A);<br />

}}<br />

worker(0,A)<br />

worker(1,A)<br />

worker(2,A)<br />

worker(3,A)<br />

所 有 线 程 在 此 处 同 步 ( 如 , 隐 式 barrier 同 步 )<br />

所 以 , 并 行 区 结 构 也 被 称 为 SPMD 结 构 .<br />

2006 年 4 月 共 享 存 储 编 程 38/108


Hello World(C)<br />

#include <br />

main()<br />

{<br />

int myid,numthreads;<br />

#pragma omp parallel<br />

{<br />

myid = omp_get_thread_num();<br />

numthreads = omp_get_num_threads();<br />

printf("Hello World from thread %d of<br />

%d!\n",myid,numthreads);<br />

}<br />

}<br />

2006 年 4 月 共 享 存 储 编 程 39/108


Hello World(Fortran)<br />

PROGRAM HELLO<br />

integer myid,numthreads<br />

integer omp_get_num_threads,omp_get_thread_num<br />

!$omp parallel private(numthreads,myid)<br />

numthreads = omp_get_num_threads()<br />

myid = omp_get_thread_num()<br />

print *, 'Hello World from thread' ,myid,'of',numthreads<br />

!$omp end parallel<br />

stop<br />

end<br />

2006 年 4 月 共 享 存 储 编 程 40/108


OpenMP 并 行 程 序 编 译<br />

• 支 持 编 译 OpenMP 的 编 译 器 会 提 供 编 译 器 命 令 选 项 , 以 解 释 OpenMP 编 译 制<br />

导 语 句 .<br />

• IBM AIX xlc 编 译 器 , 编 译 器 命 令 选 项 为 -qsmp<br />

• xlc file.c –qsmp<br />

• xlf_r file.f -qsmp (xlf_r 为 IBM AIX4.3 为 支 持 多 线 程 编 程 的 编 译 器 )<br />

• 曙 光 3000:OS: AIX4.3 -qsmp AIX4.3 支 持 OpenMP 编 译 选 项<br />

• Intel C/C++ 编 译 器 icc, Intel Fortran 编 译 器 选 项 为 -openmp<br />

• icc file.c –openmp<br />

• ifc file.f –openmp<br />

• 曙 光 4000L:OS:Redhat Linux 8.0<br />

• PGI C/C++ 编 译 器 icc, PGI Fortran 编 译 器 选 项 为 -mp<br />

• pgcc file.c –mp<br />

• pgf77 file.f –mp<br />

• pgf90 file.f –mp<br />

• 曙 光 4000A:OS:SuSE Linux 8.0 / Turbo Linux<br />

2006 年 4 月 共 享 存 储 编 程 41/108


一 些 细 节 ( 可 先 不 关 心 )<br />

C:<br />

#pragma omp parallel [<strong>clause</strong>[ <strong>clause</strong>] ...] new-line<br />

structured-block<br />

Fortran:<br />

!$OMP PARALLEL [<strong>clause</strong>[[,] <strong>clause</strong>]...]<br />

block<br />

!$OMP END PARALLEL<br />

子 句 <strong>clause</strong> 是 下 列 之 一 :<br />

if(expr): 根 据 expr 表 达 式 执 行 结 果 决 定 是 否 并 行 执 行<br />

private(list): 变 量 私 有 化 , 默 认 为 全 部 变 量<br />

firstprivate(list): 在 并 行 区 间 之 外 引 用 变 量 首 次 赋 值 结 果<br />

default(shared | none)(C)<br />

DEFAULT(PRIVATE | SHARED | NONE)(Fortran)<br />

shared(list): 并 行 区 间 中 的 共 享 变 量 列 表<br />

copyin(list): 拷 贝 主 线 程 的 threadprivate 公 共 区 数 据<br />

reduction(operator: list): 归 约 操 作<br />

2006 年 4 月 共 享 存 储 编 程 42/108


OpenMP 结 构 化 块 类 型<br />

OpenMP 主 要 有 五 类 结 构 化 块 :<br />

并 行 区 Parallel Regions<br />

OpenMP 最 重 要 部 分<br />

任 务 分 割 Worksharing<br />

⌧DO(Fortran)/for(C) 结 构 : 针 对 循 环 的 并 行 化 结 构<br />

⌧Sections: 代 码 段 间 的 并 行<br />

⌧Single: 强 制 并 行 区 中 某 些 代 码 以 串 行 方 式 执 行 ( 如 :I/O)<br />

数 据 环 境 Data Environment<br />

同 步 Synchronization<br />

运 行 时 函 数 / 环 境 变 量<br />

2006 年 4 月 共 享 存 储 编 程 43/108


循 环 分 割 :DO(Fortran)/for(C)<br />

结 构<br />

Fortran<br />

!$OMP DO [<strong>clause</strong>[[,] <strong>clause</strong>]...]<br />

do_loop<br />

[!$OMP END DO [NOWAIT]]( 可 选 )<br />

C/C++<br />

#pragma omp for [<strong>clause</strong>[ <strong>clause</strong>] ... ]<br />

new-line<br />

for-loop<br />

C$OMP PARALLEL<br />

C$OMP DO<br />

DO i=0,n<br />

…<br />

}<br />

#pragma omp parallel<br />

#pragma omp for<br />

for (i=0;i


比 较 parrallel 构 造 与 for 构 造<br />

串 行 代 码<br />

用 并 行 区 实 现 并 行 化<br />

用 任 务 分 割 构 造 实 现 并 行 化<br />

for(i=0;I


并 行 区 与 任 务 分 割 间 的 关 系<br />

并 行 区 和 任 务 分 割 是 OpenMP 两 类 基<br />

本 的 并 行 性 构 造 ;<br />

根 进 程<br />

并 行 区 中 的 代 码 对 组 内 的 各 线 程 是 可 见<br />

的 , 也 即 并 行 区 内 的 代 码 由 各 线 程 同 时<br />

执 行 ;<br />

任 务 分 割 与 并 行 区 不 同 , 它 是 将 一 个 整<br />

体 任 务 按 负 载 平 衡 的 方 式 分 配 给 各 线 程<br />

来 互 相 配 合 完 成 .<br />

并 行 区 是 并 行 的 先 决 条 件 , 任 务 分 割 必<br />

须 要 与 并 行 区 一 起 使 用 才 能 生 效 ;<br />

并 行 区 构 造 为 !omp parallel;<br />

任 务 分 割 构 造 有 :do/for,section, 和<br />

single 三 种 .<br />

并 行 区<br />

2006 年 4 月 共 享 存 储 编 程 46/108


更 详 细 的 for 语 法<br />

#pragma omp for [<strong>clause</strong>[ <strong>clause</strong>] ... ] new-line<br />

for-loop<br />

Clause 可 是 下 列 说 明 :<br />

private(list)<br />

firstprivate(list)<br />

lastprivate(list)<br />

reduction(operator: list)<br />

ordered<br />

schedule(kind[, chunk_size])<br />

nowait<br />

后 面 将 详 细<br />

说 明<br />

2006 年 4 月 共 享 存 储 编 程 47/108


更 详 细 的 DO 语 法<br />

!$OMP DO [<strong>clause</strong>[[,] <strong>clause</strong>]...]<br />

do_loop<br />

[!$OMP END DO [NOWAIT]]<br />

PRIVATE(list)<br />

FIRSTPRIVATE(list)<br />

LASTPRIVATE(list)<br />

REDUCTION({operator|intrinsic_procedure_name}:list)<br />

SCHEDULE(type[,chunk])<br />

ORDERED<br />

2006 年 4 月 共 享 存 储 编 程 48/108


DO/for 使 用 注 意 事 项<br />

循 环 体 必 须 紧 接 在 DO 或 for 之 后 .<br />

For 循 环 必 须 为 一 结 构 化 块 , 且 其 执 行 不 被 break 语 句 中<br />

断 .<br />

在 Fortran 中 , 如 果 写 上 END DO 制 导 语 句 , 其 必 须 要 紧 跟 在<br />

DO 循 环 的 结 束 之 后 .<br />

循 环 变 量 必 须 为 整 形 .<br />

Schedule, ordered,nowait 子 句 只 能 出 现 一 次 .<br />

2006 年 4 月 共 享 存 储 编 程 49/108


schedule<br />

Schedule 子 名 决 定 循 环 如 何 在 各 线 程 中 进 行 分 配 :<br />

schedule(dynamic[,chunk])<br />

各 线 程 每 次 得 到 chunk_size 大 小 的 任 务 , 执 行 完 后 继 续 取 得 任 务 , 以 此 反<br />

复 , 直 至 任 务 完 成 ( 最 后 一 任 务 可 能 会 小 于 chunk_size).( 任 务 池 )<br />

当 chunk_size 未 被 指 定 时 , 默 认 为 1.<br />

schedule(static[,chunk])<br />

如 果 chunk_size 被 指 定 , 则 各 线 程 按 线 程 号 顺 序 每 人 每 得 chunk 次 的<br />

循 环 任 务 , 如 果 任 务 不 能 一 次 平 分 掉 , 则 分 配 循 环 进 行 .<br />

如 果 chunk_size 未 被 指 定 , 则 各 线 程 任 务 数 即 为 循 环 数 除 以 所 用 线 程 数<br />

的 结 果 .<br />

schedule(guided[,chunk])<br />

开 始 以 一 大 的 单 位 进 行 分 配 忆 , 逐 渐 减 小 到 chunk 指 定 的 值 .<br />

schedule(runtime)<br />

分 配 方 式 与 chunk 值 大 小 取 决 于 环 境 变 量 OMP_SCHEDULE 的 设 置 .<br />

chunk 以 循 环 次 数 为 单 位 .<br />

示 意 图 见 下 页 .<br />

2006 年 4 月 共 享 存 储 编 程 50/108


Schedule 示 意 图<br />

Work pool<br />

Work<br />

Work<br />

.. .. .. ..<br />

..<br />

执<br />

行<br />

时 .<br />

间<br />

.<br />

.<br />

.<br />

.<br />

.<br />

Dynamic 方 式<br />

Static 方 式<br />

Guided 方 式<br />

2006 年 4 月 共 享 存 储 编 程 51/108


适 用 条 件<br />

静 态 : 适 用 于 大 部 分 情 形 .<br />

特 点 :<br />

⌧ 各 线 程 任 务 明 确 , 在 任 务 分 配 时 无 需 同 步 操 作 .<br />

⌧ 运 行 快 的 线 程 需 等 慢 的 线 程 为 :<br />

动 态 : 适 用 于 任 务 数 量 可 变 或 不 确 定 的 情 形 ( 如 条 件 收 敛 循 环 ).<br />

特 点 :<br />

⌧ 各 线 程 将 要 执 行 的 任 务 不 可 预 见 , 任 务 分 配 需 同 步 操 作 .<br />

Guided: 线 程 异 步 到 达 for 结 构<br />

特 点 :<br />

⌧ 首 先 到 达 的 线 程 总 是 分 得 q=ceiling(n/p) 次 循 环 , 然 后<br />

n=max(n-q,p*k), 循 环 分 配 , 直 到 n=p*k 为 止 .<br />

环 境 变 量 : 无 需 重 新 编 译 程 序 , 可 根 据 原 始 输 入 数 据 的 情 况 改 变 任 务 分<br />

配 策 略 .<br />

2006 年 4 月 共 享 存 储 编 程 52/108


NOWAIT 子 句 (Fortran)<br />

C$OMP PARALLEL<br />

C$OMP DO<br />

do i=1,n<br />

a(i)= cos(a(i))<br />

enddo<br />

C$OMP END DO<br />

隐 式<br />

C$OMP DO<br />

BARRIER<br />

do i=1,n<br />

b(i)=a(i)+b(i)<br />

enddo<br />

C$OMP END DO<br />

C$OMP END PARALLEL<br />

默 认 循 环 变 量 i 为 私 有 线 程 私 有 类 型 变 量<br />

C$OMP PARALLEL<br />

C$OMP DO<br />

do i=1,n<br />

a(i)= cos(a(i))<br />

enddo<br />

C$OMP END DO NOWAIT<br />

C$OMP DO<br />

No<br />

do i=1,n BARRIER<br />

b(i)=a(i)+b(i)<br />

enddo<br />

C$OMP END DO<br />

C$OMP END PARALLEL<br />

END DO 必 须 紧 随 enddo, 可 省 略 .<br />

2006 年 4 月 共 享 存 储 编 程 53/108


no wait 子 句 (C)<br />

#pragma omp parallel<br />

{<br />

#pragma omp for<br />

for(i=1;i


Sections 构 造 ( 非 循 环 并 行 )<br />

Sections 用 于 程 序 中 大 范 围 的 非 迭 代 执 行 代 码 段 间 的 并 行 化 .( 如 前 10<br />

行 和 后 10 行 间 代 码 间 无 依 赖 关 系 , 可 以 并 行 .)<br />

#pragma omp sections [no wait]<br />

{<br />

x_calculation();<br />

#pragma omp section<br />

y_calculation();<br />

#pragma omp section<br />

z_calculation();<br />

}<br />

缺 省 时 每 一 个 “omp sections” 构 造 结 束 后 有 一 个 barrier 同 步 操 作 . 通 过<br />

使 用 “nowait” 子 句 禁 止 隐 式 barrier 同 步 ( 在 构 造 语 句 后 直 接 加 即 可 ).<br />

与 for 结 构 相 类 似 ,OpenMP 也 提 供 parallel sections.<br />

x_calculation(),y_calc<br />

ulation() 以 及<br />

z_calculation() 代 表 三<br />

部 分 之 间 无 依 赖 关 系<br />

的 非 循 环 代 码 段 . 实<br />

质 上 它 们 各 代 表 很 多<br />

行 代 码 .<br />

2006 年 4 月 共 享 存 储 编 程 55/108


Single<br />

Single: 强 制 并 行 区 中 某 些 代 码 以 串 行 方 式 执 行<br />

#pragma omp single [<strong>clause</strong>[ <strong>clause</strong>] ...] new-line<br />

⌧structured-block<br />

Clause is one of the following:<br />

⌧private(list)<br />

⌧firstprivate(list)<br />

⌧nowait<br />

2006 年 4 月 共 享 存 储 编 程 56/108


Work sharing 语 句 汇 总<br />

<br />

<br />

Fortran<br />

DO<br />

SECTIONS<br />

SINGLE<br />

WORKSHARE<br />

C<br />

for<br />

sections<br />

single<br />

2006 年 4 月 共 享 存 储 编 程 57/108


Binding<br />

并 行 结 构 的 联 合 使 用<br />

•for, sections, single, master, and barrier 如 果 不 位 于 并 行 区 内 或<br />

不 与 并 行 区 联 合 使 用 , 便 不 起 任 何 作 用 .<br />

#pragma omp parallel for<br />

for (I=0;I


Fortran<br />

!$OMP !$OMP PARALLEL DO DO [<strong>clause</strong>[[,] <strong>clause</strong>]...]<br />

do_loop do_loop<br />

[!$OMP [!$OMP END END PARALLEL DO] DO]<br />

!$OMP !$OMP PARALLEL PARALLEL SECTIONS SECTIONS [<strong>clause</strong>[[,] [<strong>clause</strong>[[,] <strong>clause</strong>]...] <strong>clause</strong>]...]<br />

[!$OMP [!$OMP SECTION SECTION ]]<br />

block block<br />

[!$OMP [!$OMP SECTION SECTION<br />

block] block]<br />

. . ..<br />

!$OMP !$OMP END END PARALLEL PARALLEL SECTIONS SECTIONS<br />

!$OMP !$OMP PARALLEL WORKSHARE [<strong>clause</strong>[[,]<br />

<strong>clause</strong>]...]<br />

block block<br />

!$OMP !$OMP END END PARALLEL WORKSHARE<br />

2006 年 4 月 共 享 存 储 编 程 59/108


C<br />

#pragma omp omp parallel for for [<strong>clause</strong>[ <strong>clause</strong>] ...] new-line<br />

for-loop<br />

#pragma omp omp parallel sections [<strong>clause</strong>[ <strong>clause</strong>] ...] new-line<br />

{<br />

[#pragma omp omp section new-line]<br />

structured-block<br />

[#pragma omp omp section new-line<br />

structured-block<br />

.<br />

.] .]<br />

}<br />

2006 年 4 月 共 享 存 储 编 程 60/108


OpenMP 结 构 化 块 类 型<br />

OpenMP 主 要 有 五 类 结 构 化 块 :<br />

并 行 区 Parallel Regions<br />

任 务 分 割 Worksharing<br />

数 据 环 境 Data Environment<br />

同 步 Synchronization<br />

运 行 时 函 数 / 环 境 变 量<br />

2006 年 4 月 共 享 存 储 编 程 61/108


默 认 数 据 类 型<br />

共 享 变 量 编 程 模 型 :<br />

– 大 部 分 变 量 默 认 为 共 享 类 型<br />

各 线 程 共 享 全 局 变 量<br />

– Fortran: COMMON 块 , SAVE 变 量 , MODULE 变 量<br />

– C: File scope variables, static<br />

但 并 不 是 所 有 变 量 全 部 共 享 ...<br />

– 在 并 行 区 内 调 用 的 子 程 序 中 的 栈 变 量 是 私 有<br />

– 在 语 句 块 中 的 Auto 变 量 为 私 有 变 量 .<br />

2006 年 4 月 共 享 存 储 编 程 62/108


举 例 说 明 变 量 类 型<br />

program sort<br />

common /input/ A(10)<br />

integer index(10)<br />

call input<br />

C$OMP PARALLEL<br />

call work(index)<br />

C$OMP END PARALLEL<br />

print*, index(1)<br />

subroutine work<br />

common /input/ A(10)<br />

real temp(10)<br />

integer count<br />

save count<br />

…………<br />

变 量 A,index 和 cound 为 所 有 线<br />

程 共 享 .<br />

temp 为 线 程 私 有 变 量 , 各 线 程 间<br />

到 不 可 见 .<br />

2006 年 4 月 共 享 存 储 编 程 63/108


变 量 类 型 改 变<br />

变 量 类 型 编 译 制 导 : threadprivate: 将 某 些 全 局 变 量 或 数 据 区 变 为<br />

线 程 私 有 .<br />

通 过 下 列 类 型 属 性 子 句 来 改 变 变 量 类 型 :<br />

– shared( 并 行 区 结 构 )<br />

– private<br />

– firstprivate<br />

– lastprivate: 循 环 体 内 的 私 有 变 量 可 以 转 变 为 全 局 变 量 , 在 循<br />

环 结 束 后 访 问 ;<br />

默 认 数 据 类 型 状 态 可 以 改 变 :<br />

– default (private | shared | none)<br />

2006 年 4 月 共 享 存 储 编 程 64/108


私 有 类 型 变 量<br />

变 量 私 有 化 是 OpenMP 采 用 的 重 要 的 编 译 技 术 .<br />

变 量 私 有 化 ;<br />

私 有 变 量 初 始 化 :<br />

⌧Copyin<br />

⌧Firstprivate<br />

私 有 变 量 返 回 :<br />

⌧Reduction<br />

⌧Lastprivate<br />

OpenMP 私 有 类 型 变 量 是 指 并 行 区 内 的 只 能 被 线 程 组 内 的 某 一 线 程 访 问 的<br />

变 量 .<br />

OpenMP 的 私 有 变 量 包 括 :<br />

并 行 区 内 定 义 的 变 量 ;<br />

由 threadprivate 制 导 语 句 指 明 的 变 量 ;<br />

由 private, firstprivate,lastprivate, or reduction 子 句 指 定 的 变<br />

量 ;<br />

循 环 控 制 变 量 .<br />

2006 年 4 月 共 享 存 储 编 程 65/108


Threadprivate:<br />

线 程 的 全 局 私 有 变 量<br />

parameter (N=1000)<br />

real A(N,N)<br />

C$OMP THREADPRIVATE(/buf/)<br />

common/buf/lft(N),rht(N)<br />

C$OMP PARALLEL<br />

call init<br />

call scale<br />

call order<br />

buf 是 线 程 内 的 公 共 块<br />

C$OMP END PARALLEL<br />

subroutine scale<br />

parameter (N=1000)<br />

C$OMP THREADPRIVATE(/buf/)<br />

common/buf/lft(N),rht(N)<br />

do i=1, N<br />

lft(i)= const* A(i,iam)<br />

end do<br />

return<br />

end<br />

subroutine order<br />

parameter (N=1000)<br />

C$OMP THREADPRIVATE(/buf/)<br />

common/buf/lft(N),rht(N)<br />

do i=1, N<br />

A(i,iam) = lft(index)<br />

end do<br />

return<br />

end<br />

2006 年 4 月 共 享 存 储 编 程 66/108


Private<br />

private(list) 表 明 list 中 所 列 变 量 为 组 内 线 程 私 有 变 量 .<br />

– 对 变 量 不 进 行 初 始 化 ( 有 构 造 函 数 除 外 )<br />

– 私 有 拷 贝 独 立 存 储 , 不 与 原 变 量 一 致<br />

main()<br />

{{<br />

int inti i = 10000;<br />

#pragma omp ompparallel private(i)<br />

{{<br />

i i = -10000;<br />

}}<br />

printf("%d\n",i);<br />

}}<br />

运 行 ::<br />

10000 10000<br />

main()<br />

{{<br />

int inti i = 10000;<br />

#pragma omp ompparallel<br />

{{<br />

i i = -10000;<br />

}}<br />

printf("%d\n",i);<br />

}}<br />

运 行 ::<br />

-10000<br />

2006 年 4 月 共 享 存 储 编 程 67/108


Private 变 量 将 在 组 内 每 一 线 程 中 进 行 创 建 , 如 果 只 有 如 果<br />

变 量 有 构 造 函 数 , 则 调 用 构 造 函 数 进 行 初 始 化 , 否 则 该 私 有<br />

变 量 的 值 不 确 定 .<br />

在 进 入 并 行 结 构 中 , 私 有 变 量 引 用 的 原 变 量 值 是 不 确 定 的 ;<br />

也 不 能 在 并 行 构 造 对 引 用 的 变 量 进 行 修 改 , 在 结 构 中 对 这<br />

些 对 私 有 变 量 的 修 改 不 影 响 退 出 结 构 后 的 同 名 变 量 值 .<br />

私 有 变 量 应 在 并 行 性 构 造 中 进 行 初 始 化 ;<br />

私 有 变 量 不 能 为 引 用 类 型 ( 破 坏 了 数 据 的 独 立 性 );<br />

2006 年 4 月 共 享 存 储 编 程 68/108


?<br />

For 结 构 中 :<br />

main(){<br />

int inti,sum=0,myid;<br />

#pragma omp ompparallel for for \\<br />

private(i,sum)<br />

for(i=0;i


Firstprivate<br />

Firstprivate 是 private 的 特 殊 情 况 .<br />

– 在 主 线 程 中 初 始 化 每 一 个 线 程 的 私 有 拷 贝 变 量 .<br />

main(){<br />

int i,sum=0,myid;<br />

#pragma omp parallel for firstprivate(i,sum)<br />

for(i=0;i


Lastprivate<br />

Lastprivate 将 位 于 迭 代 末 的 私 有 变 量 转 化 为 全 局 变 量 .<br />

for (i=1;i


Default<br />

默 认 情 形 =default(shared)( 因 此 不 显 示 使 用 )<br />

改 变 默 认 default(private):<br />

将 并 行 区 内 变 量 的 默 认 类 型 改 为 私 有 , 而 不 是 共 享<br />

省 去 在 每 一 并 行 区 结 构 后 加 private(list)<br />

default(none): 指 事 先 指 定 并 行 区 中 变 量 的 默 认 类 型<br />

Fortran 支 持 default(private).<br />

C/C++ 无 default(private), 只 有 default(shared) 和 default(none).<br />

2006 年 4 月 共 享 存 储 编 程 72/108


DEFAULT 例 (Fortran)<br />

itotal = 1000<br />

C$OMP PARALLEL PRIVATE(np, each)<br />

np = omp_get_num_threads()<br />

each = itotal/np<br />

………<br />

C$OMP END PARALLEL<br />

itotal = 1000<br />

C$OMP PARALLEL DEFAULT(PRIVATE) SHARED(itotal)<br />

np = omp_get_num_threads()<br />

each = itotal/np<br />

………<br />

C$OMP END PARALLEL<br />

2006 年 4 月 共 享 存 储 编 程 73/108


eduction<br />

归 约 操 作 ( 将 各 线 程 中 处 理 结 果 经 某 种 运 算 后 送 回 到 根 进 程 )<br />

reduction (op : list).<br />

List 中 所 列 变 量 必 须 为 并 行 区 内 的 共 享 变 量 .<br />

支 持 的 运 算 操 作 :<br />

Operator Initialization<br />

+ 0 #pragma omp parallel for private(i) shared(x, y, n)<br />

* 1 reduction(+: a, b)<br />

&& 1 for (i=0; i


实 例 :PIPI 求 解<br />

通 过 数 值 积 分 的 方 法 串 行 求 解 PI 很 简 单 的 ( 具 体 代 码 见 下 一 页 ).<br />

基 于 OpenMP 将 该 串 行 程 序 并 行 化 . 依 据 使 用 制 导 语 句 的 不 同 , 可 有 若<br />

干 种 方 法 ( 我 们 只 讨 论 基 于 刚 讲 过 的 并 行 区 与 for 结 构 ):<br />

仅 用 并 行 区 结 构 将 该 程 序 改 为 一 SPMD 并 行 程 序 .<br />

用 任 务 分 割 结 构 进 行 并 行 化 .<br />

归 约 .<br />

2006 年 4 月 共 享 存 储 编 程 75/108


串 行 程 序<br />

#include "stdio.h"<br />

main(int argc,char* argv[])<br />

{{<br />

…<br />

n = atoi(argv[1]);<br />

w = 1.0 1.0 //(double) n; n;<br />

sum sum = 0.0; 0.0;<br />

}}<br />

for(i=0;i


仅 基 于 Parallel 块 结 构 的 并 行 程 序<br />

#include #include"stdio.h"<br />

main(int main(intargc, argc, char char *argv[]){ *argv[]){<br />

…<br />

n n = atoi(argv[1]);<br />

w = 1.0 1.0 //(double) (double) n; n;<br />

sum sum = 0.0; 0.0;<br />

#pragma #pragmaomp ompparallel parallel private(x) shared(w) reduction(+:sum)<br />

{{<br />

myid myid = omp_get_thread_num();<br />

numthreads = omp_get_num_threads();<br />

for(i=myid;i


基 于 for 结 构 的 并 行 化 代 码<br />

#include #include"stdio.h"<br />

main(int main(intargc, argc, char char *argv[]) *argv[])<br />

{{<br />

…<br />

n = atoi(argv[1]);<br />

w = 1.0 1.0 //(double) (double) n; n;<br />

sum sum = 0.0; 0.0;<br />

#pragma omp ompfor for reduction(+:sum)<br />

for(i=0;i


OpenMP 结 构 化 块 类 型<br />

OpenMP 主 要 有 五 类 结 构 化 块 :<br />

并 行 区 Parallel Regions<br />

任 务 分 割 Worksharing<br />

数 据 环 境 Data Environment<br />

同 步 Synchronization<br />

运 行 时 函 数 / 环 境 变 量<br />

2006 年 4 月 共 享 存 储 编 程 79/108


OpenMP 同 步<br />

共 享 变 量 读 写<br />

单 一 文 件 I/O<br />

OpenMP 提 共 下 列 同 步 结 构 :<br />

– atomic<br />

– critical section<br />

– barrier<br />

– flush<br />

– ordered<br />

– single<br />

– master<br />

这 两 个 结 构 实 质 上 不 属 于 同 步 结<br />

构 , 是 并 行 区 结 构 和 任 务 分 割 结 构<br />

中 的 内 容 .<br />

2006 年 4 月 共 享 存 储 编 程 80/108


critical section<br />

同 时 至 多 有 一 个 线 程 进 入 临 界 区 .<br />

#pragma omp critical [(name)] new-line<br />

structured-block<br />

C$OMP PARALLEL DO PRIVATE(B)<br />

C$OMP& SHARED(RES)<br />

DO 100 I=1,NITERS<br />

B = DOIT(I)<br />

C$OMP CRITICAL<br />

CALL CONSUME (B, RES)<br />

C$OMP END CRITICAL<br />

100 CONTINUE<br />

2006 年 4 月 共 享 存 储 编 程 81/108


atomic<br />

Atomic 是 临 界 区 的 特 例 , 主 要 用 于 某 些 简 单 的 语 句 .<br />

#pragma omp atomic new-line<br />

expression-stmt<br />

其 中 expression-stmt 只 能 为 下 列 情 形 :<br />

⌧x binop = expr (bino 只 能 是 + * - / & ^ | > )<br />

⌧x++<br />

⌧++x<br />

⌧x--<br />

⌧--x<br />

仅 用 于 内 存 变 量 的 更 新 ( 在 下 面 的 例 子 中 即 对 X 的 更 新 )<br />

• C$OMP PARALLEL PRIVATE(B)<br />

• B = DOIT(I)<br />

• C$OMP ATOMIC<br />

• X = X + B<br />

• C$OMP END PARALLEL<br />

2006 年 4 月 共 享 存 储 编 程 82/108


下 面 两 段 代 码 区 别 是 什 么 ?<br />

#pragma omp parallel for \<br />

shared(x, y, index, n)<br />

for (i=0; i


原 子 操 作 和 临 界 区 比 较<br />

原 子 性 是 指 操 作 的 不 可 再 分 性 ,OpenMP 利 用 原 子 结 构 主<br />

要 是 用 于 防 止 多 线 程 对 内 存 的 同 一 地 址 的 并 发 写 .<br />

临 界 区 可 以 完 成 所 有 的 原 子 操 作 .<br />

原 子 结 构 可 更 好 被 编 译 优 化 :<br />

有 硬 件 指 令 可 用 于 实 现 原 子 操 作 , 而 且 系 统 开 销 也 很 小 .<br />

原 子 操 作 与 并 发 并 不 矛 盾 ,<br />

临 界 区 一 定 是 串 行 执 行 的 , 原 子 操 作 不 一 定 是 串 行 执 行<br />

2006 年 4 月 共 享 存 储 编 程 84/108


arrier<br />

Barrier: 每 一 线 程 等 待 直 至 所 有 组 内 其 它 线 程 执 行 到 Barrier 为 止 .<br />

#pragma omp parallel shared (A, B, C) private(id)<br />

{<br />

id=omp_get_thread_num();<br />

A[id] = big_calc1(id);<br />

#pragma omp barrier<br />

#pragma omp for<br />

for(i=0;i


ordered<br />

Ordered 用 于 指 定 循 环 中 各 线 程 执 行 任 务 的 顺 序 严 格 与 顺 序 方 式 时 的<br />

执 行 顺 序 一 致 .<br />

以 制 导 语 句 , 而 不 是 子 句 出 现 的 ordered, 只 能 出 现 在 并 行 区 动 态 范 围 内<br />

如 : 在 并 行 区 中 按 顺 序 方 式 打 印 输 出 :<br />

void worker(int k){<br />

#pragma omp ordered<br />

printf(“%d ”,k);<br />

}<br />

main(){<br />

int i;<br />

#pragma omp parallel for schedule(dynamic)<br />

for(i=0;i


master<br />

Master 结 构 用 于 标 志 一 个 结 构 块 只 能 由 主 线 程 执 行 . 其 它 线 程 跨 越 该<br />

块 . 该 结 构 无 隐 式 barrier 同 步 .<br />

#pragma omp parallel private (tmp)<br />

{<br />

}<br />

do_many_things();<br />

#pragma omp master<br />

{ exchange_boundaries(); }<br />

#pragma barrier<br />

do_many_other_things();<br />

2006 年 4 月 共 享 存 储 编 程 87/108


single<br />

Single 结 构 用 于 标 志 一 个 块 内 的 代 码 仅 能 由 一 个 线 程 执 行 ( 不 一 定 是 主 线<br />

程 ).<br />

在 single 块 后 有 一 隐 式 的 barrier 同 步 操 作 .<br />

#pragma omp ompparallel{<br />

最 早 遇 到<br />

#pragma omp ompsingle<br />

single 块<br />

者 执 行<br />

printf("Beginning work1.\n");<br />

work1();<br />

同 步<br />

#pragma omp ompsingle<br />

printf("Finishing work1.\n");<br />

同 步<br />

#pragma omp ompsingle nowait<br />

printf("Finished work1 and and beginning<br />

work2.\n");<br />

不 同 步<br />

work2();<br />

}}<br />

2006 年 4 月 共 享 存 储 编 程 88/108


flush<br />

#pragma omp flush [(list)] new-line<br />

!$OMP FLUSH [(list)]<br />

Flush 语 句 可 显 式 或 隐 式 执 行 . 用 于 在 程 序 的 某 一 点 处 , 确 定<br />

共 享 内 存 中 的 变 量 对 各 线 程 的 应 印 象 是 一 致 的 .<br />

如 : 编 译 器 必 须 将 寄 存 器 中 的 值 写 回 内 存 , 硬 件 刷 新 缓 冲<br />

区 等 .<br />

2006 年 4 月 共 享 存 储 编 程 89/108


Flush(Fortran)<br />

隐 式 flush<br />

BARRIER<br />

CRITICAL and END CRITICAL<br />

END DO<br />

END SECTIONS<br />

END SINGLE<br />

END WORKSHARE<br />

ORDERED and END ORDERED<br />

PARALLEL and END PARALLEL<br />

PARALLEL DO and END PARALLEL DO<br />

PARALLEL SECTIONS and END PARALLEL SECTIONS<br />

PARALLEL WORKSHARE and END PARALLEL WORKSHARE<br />

下 列 语 句 无 隐 式 flush<br />

DO<br />

MASTER and END MASTER<br />

SECTIONS<br />

SINGLE<br />

WORKSHARE<br />

2006 年 4 月 共 享 存 储 编 程 90/108


Flush(C)<br />

隐 式 flush<br />

barrier<br />

Critical 区 的 出 入 口<br />

Ordered 区 的 出 入 口<br />

Parallel 的 出 口<br />

For 的 出 口<br />

Sections 的 出 口<br />

Single 的 出 口<br />

无 隐 式 flush<br />

no wait<br />

2006 年 4 月 共 享 存 储 编 程 91/108


隐 式 同 步<br />

下 列 OpenMP 结 构 之 后 有 隐 式 Barrier:<br />

•end parallel<br />

•end do do (( 用 nowait 禁 止 ))<br />

•end sections (( 用 nowait 禁 止 ))<br />

•end critical<br />

•end single (( 用 nowait 禁 止 ))<br />

2006 年 4 月 共 享 存 储 编 程 92/108


Nesting<br />

并 行 结 构 的 嵌 套<br />

一 个 parallel 语 句 可 以 出 现 在 嵌 套 在 另 一 个 parallel 内 . 如 果 不 明 确 指 定<br />

嵌 套 并 行 性 , 则 该 并 行 区 的 线 程 组 只 由 当 前 的 线 程 组 成 , 否 则 , 该 并 行 区<br />

可 建 立 自 己 的 线 程 组 .<br />

同 一 parallel 内 的 for, sections, 和 single 语 句 不 允 许 相 互 嵌 套 .<br />

同 名 critical 语 句 不 允 许 相 互 嵌 套 .<br />

for, sections, 和 single 不 能 出 现 在 critical, ordered, 和 master 的 动 态 范<br />

围 中 .<br />

barrier 语 句 不 能 出 现 在 for, ordered, sections, single, master, 和 critical<br />

的 动 态 范 围 中 .<br />

master 不 能 出 现 在 for, sections, 和 single 的 动 态 范 围 内 .<br />

ordered 不 能 出 现 在 critical 的 动 态 范 围 内 .<br />

2006 年 4 月 共 享 存 储 编 程 93/108


OpenMP 结 构 化 块 类 型<br />

OpenMP 主 要 有 五 类 结 构 化 块 :<br />

并 行 区 Parallel Regions<br />

任 务 分 割 Worksharing<br />

数 据 环 境 Data Environment<br />

同 步 Synchronization<br />

运 行 时 函 数 / 环 境 变 量<br />

2006 年 4 月 共 享 存 储 编 程 94/108


OpenMP 函 数<br />

锁 函 数<br />

omp_init_lock(), omp_set_lock(), omp_unset_lock(),<br />

omp_test_lock(), omp_destroy_lock()<br />

运 行 时 环 境 函 数 :<br />

– 改 变 或 检 查 线 程 数<br />

omp_set_num_threads(), omp_get_num_threads(),<br />

omp_get_thread_num(), omp_get_max_threads()<br />

– 开 / 关 嵌 套 和 动 态 代 码<br />

omp_set_nested(), omp_set_dynamic(), omp_get_nested(),<br />

omp_get_dynamic()<br />

– 当 前 代 码 位 于 并 行 区 内 吗 ?<br />

omp_in_parallel()<br />

– 机 器 有 多 少 个 处 理 器 ?<br />

omp_get_num_procs()<br />

2006 年 4 月 共 享 存 储 编 程 95/108


例 : 锁 的 使 用<br />

下 面 的 例 子 是 用 来 显 示 锁 的 使 用 :<br />

锁 对 象 具 有 omp_lock_t, 在 锁 使 用 前 需 对 锁 进 行 初 始 化 :<br />

omp_init_lock().<br />

当 一 个 线 程 试 图 获 得 锁 以 进 入 第 一 个 临 界 区 时 , 处 理 于 空 闲 状 态 , 以<br />

等 待 进 入 临 界 区 .<br />

一 旦 得 到 锁 , 则 对 临 界 区 加 锁 : omp_set_lock(), 退 出 完 临 界 区 时<br />

解 锁 omp_unset_lock().<br />

在 进 入 第 二 个 临 界 区 时 , 则 通 过 非 阻 塞 函 数 omp_test_lock() 来<br />

测 试 可 能 性 , 同 时 做 一 些 其 它 的 事 情 .<br />

在 锁 不 再 使 用 时 , 用 omp_destroy_lock() 销 毁 .<br />

2006 年 4 月 共 享 存 储 编 程 96/108


#include <br />

int main()<br />

{<br />

omp_lock_t lck;<br />

int id;<br />

omp_init_lock(&lck);<br />

#pragma omp parallel shared(lck) private(id)<br />

{<br />

id = omp_get_thread_num();<br />

omp_set_lock(&lck);<br />

printf("My thread id is %d.\n", id);<br />

omp_unset_lock(&lck);<br />

while (! omp_test_lock(&lck)) {<br />

skip(id); /* we do not yet have the lock, so we must do something else */<br />

}<br />

work(id); /* we now have the lock and can do the work */<br />

omp_unset_lock(&lck);<br />

}<br />

omp_destroy_lock(&lck);<br />

}<br />

2006 年 4 月 共 享 存 储 编 程 97/108


锁 : 保 护 共 享 资 源<br />

omp_lock_t lck; lck;<br />

omp_init_lock(&lck);<br />

#pragma omp ompparallel private (tmp)<br />

{<br />

id id = omp_get_thread_num();<br />

tmp tmp = do_lots_of_work(id);<br />

omp_set_lock(&lck);<br />

printf(“%d %d”, %d”, id, id, tmp);<br />

omp_unset_lock(&lck);<br />

}<br />

2006 年 4 月 共 享 存 储 编 程 98/108


设 定 动 态 模 式 , 由 程 序 来 指 定 线 程 数 ( 一 般 默 认 线 程 数 与 处 理 器 个 数 相 等 ).<br />

#include <br />

void void main()<br />

{<br />

omp_set_dynamic(0);<br />

omp_set_num_threads(4);<br />

#pragma omp ompparallel<br />

{<br />

int intid=omp_get_thread_num();<br />

do_lots_of_stuff(id);<br />

}<br />

}<br />

2006 年 4 月 共 享 存 储 编 程 99/108


指 定 线 程 数<br />

omp_set_dynamic(0);<br />

omp_set_num_threads(16);<br />

#pragma omp parallel shared(x, npoints) private(iam, ipoints)<br />

{<br />

if (omp_get_num_threads() != 16) abort();<br />

iam = omp_get_thread_num();<br />

ipoints = npoints/16;<br />

do_by_16(x, iam, ipoints);<br />

}<br />

2006 年 4 月 共 享 存 储 编 程 100/108


得 到 线 程 数<br />

int numthreads;<br />

numthreads = omp_get_num_threads();<br />

printf(“%d\n”,numthreads);<br />

#pragma omp parallel private(i)<br />

{<br />

numthreads = omp_get_num_threads();<br />

printf(“%d\n”,numthreads);<br />

}<br />

运 行 :<br />

1<br />

4<br />

4<br />

4<br />

4<br />

2006 年 4 月 共 享 存 储 编 程 101/108


环 境 变 量<br />

线 程 任 务 调 度 控 制<br />

– OMP_SCHEDULE “SCHEDULE[, CHUNK_SIZE]”<br />

设 定 缺 省 线 程 数<br />

– OMP_NUM_THREADS INT_LITERAL<br />

是 否 允 许 各 并 行 区 内 线 程 数 目 不 相 同 ( 动 态 生 成 线 程 组 )?<br />

– OMP_DYNAMIC TRUE || FALSE<br />

动 态 模 式 ( 默 认 ):<br />

⌧ 并 行 区 中 的 线 程 数 动 态 确 定 , 各 并 行 区 可 具 有 不 同 的 线 程 数 .<br />

⌧ 此 时 , 线 程 数 设 置 函 数 omp_set_num_threads() 只 设 定 一 上 限 , 实<br />

际 中 发 生 的 线 程 数 可 能 要 比 我 们 设 置 的 数 目 少 .<br />

静 态 模 式 :<br />

⌧ 由 程 序 员 确 定 并 行 区 中 线 程 的 数 量 .<br />

嵌 套 并 行 区 是 否 建 立 新 的 线 程 组 或 它 们 是 否 要 串 行 化 ?( 仅 由 线 程 组 中 的 主 线<br />

程 执 行 , 如 I/O 操 作 等 )<br />

– OMP_NESTED TRUE || FALSE( 默 认 为 FALSE)<br />

2006 年 4 月 共 享 存 储 编 程 102/108


汇 总<br />

并 行 区<br />

parallel<br />

任 务 分 割<br />

for<br />

sections<br />

Single<br />

parallel for<br />

parallel sections<br />

同 步<br />

master<br />

critical<br />

barrier<br />

atomic<br />

flush<br />

ordered<br />

<br />

<br />

<br />

<br />

<br />

<br />

并 行 区<br />

parallel<br />

任 务 分 割<br />

DO<br />

SECTIONS<br />

SINGLE<br />

WORKSHARE<br />

PARALLEL DO<br />

PARALLEL SECTIONS<br />

PARALLEL WORKSHARE<br />

同 步<br />

MASTER<br />

CRITICAL<br />

BARRIER<br />

ATOMIC<br />

FLUSH<br />

ORDERED<br />

2006 年 4 月 共 享 存 储 编 程 103/108


运 行 环 境 函 数 汇 总<br />

omp_set_num_threads<br />

omp_get_num_threads<br />

omp_get_max_threads<br />

omp_get_thread_num<br />

omp_get_num_procs<br />

omp_in_parallel<br />

omp_set_dynamic<br />

omp_get_dynamic<br />

omp_set_nested<br />

omp_get_nested<br />

OMP_SET_NUM_THREADS<br />

OMP_GET_NUM_THREADS<br />

OMP_GET_MAX_THREADS<br />

OMP_GET_THREAD_NUM<br />

OMP_GET_NUM_PROCS<br />

OMP_IN_PARALLEL<br />

OMP_SET_DYNAMIC<br />

OMP_GET_DYNAMIC<br />

OMP_SET_NESTED<br />

OMP_GET_NESTED<br />

OMP_GET_WTIME<br />

OMP_GET_WTICK<br />

2006 年 4 月 共 享 存 储 编 程 104/108


锁 函 数 汇 总<br />

omp_init_lock<br />

omp_init_nest_lock<br />

omp_destroy_lock<br />

omp_destroy_nest_lock<br />

omp_set_lock<br />

omp_set_nest_lock<br />

omp_unset_lock<br />

omp_unset_nest_lock<br />

omp_test_lock<br />

omp_test_nest_lock<br />

OMP_INIT_LOCK<br />

OMP_INIT_NEST_LOCK<br />

OMP_DESTROY_LOCK<br />

OMP_DESTROY_NEST_LOCK<br />

OMP_SET_LOCK<br />

OMP_SET_NEST_LOCK<br />

OMP_UNSET_LOCK<br />

OMP_UNSET_NEST_LOCK<br />

OMP_TEST_LOCK<br />

OMP_TEST_NEST_LOCK<br />

2006 年 4 月 共 享 存 储 编 程 105/108


环 境 变 量 (C 和 Fortran 一 样 )<br />

OMP_SCHEDULE<br />

OMP_NUM_THREADS<br />

OMP_DYNAMIC<br />

OMP_NESTED<br />

2006 年 4 月 共 享 存 储 编 程 106/108


OpenMP 特 点 总 结<br />

支 持 C,C++ 和 Fortran<br />

共 享 存 储 并 行 机 并 行 编 程 的 主 流 平 台 , 获 得 并 行 机 厂 商 和<br />

用 户 的 广 泛 支 持<br />

特 别 适 合 于 串 行 程 序 的 逐 步 并 行 化<br />

如 : 先 进 行 一 些 特 别 耗 时 的 大 循 环 的 并 行 化 , 然 后 再<br />

逐 步 进 行 再 大 粒 度 的 并 行 .<br />

Orphan 制 导 语 句 的 支 持 , 使 得 OpenMP 较 之 于 以 前 的<br />

共 享 存 储 编 程 模 型 更 适 合 粗 粒 度 的 并 行<br />

2006 年 4 月 共 享 存 储 编 程 107/108


谢<br />

谢 !<br />

2006 年 4 月 共 享 存 储 编 程 108/108

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!