Presentation is loading. Please wait.

Presentation is loading. Please wait.

周纯葆 中国科学院计算机网络信息中心 超级计算中心

Similar presentations


Presentation on theme: "周纯葆 中国科学院计算机网络信息中心 超级计算中心"— Presentation transcript:

1 周纯葆 中国科学院计算机网络信息中心 超级计算中心 zhoucb@sccas.cn
OpenMP编程基础 周纯葆 中国科学院计算机网络信息中心 超级计算中心

2 提纲 OpenMP简介 OpenMP编译制导 OpenMP库函数 OpenMP环境变量

3 并行机体系结构及通信机制 SMP:共享内存并行机 (Shared Memory Processors)
多个处理器通过交叉开关(Crossbar)或总线与共享内存互连 任意处理器可直接访问任意内存地址,且访问延迟、带宽、几率都是等价的 系统是对称的 单地址空间 、共享存储、UMA DSM:分布共享存储并行机 (Distributed Shared Memory) 结点(一般是SMP系统)通过高速消息传递网络互连而成。存储系统在物理上分布、逻辑上共享。各结点有自己独立的寻址空间 单地址空间 、分布共享 NUMA (Nonuniform Memory Access) 与SMP的主要区别:DSM在物理上有分布在各个节点的局部内存从而形成一个共享的存储器

4 访存模型

5 OpenMP OpenMP是共享存储体系结构上的一个并行编程模型。适合于SMP共享内存多处理系统和多核处理器体系结构。
起源于ANSI X3H5标准 简单、移植性好和可扩展性等特点 提供了支持Fortran、C/C++的API和规范 由一组编译制导、运行时库函数(Run-Time routines) 和环境变量组成。 工业标准 DEC、Intel、IBM、HP、Sun、SGI等公司支持 包括Linux、UNIX和Windows等多种操作系统平台

6 OpenMP编程模式 OpenMP是基于线程的并行编程模型。 OpenMP采用Fork-Join并行执行方式:
OpenMP程序开始于一个单独的主线程(Master Thread),然后主线程一直串行执行,直到遇见第一个并行域(Parallel Region),然后开始并行执行并行区域。其过程如下: Fork:主线程创建一个并行线程队列,然后,并行域中的代码在不同的线程上并行执行; Join:当并行域执行完之后,它们或被同步或被中断,最后只有主线程在执行。

7 OpenMP执行模型 F O R K J I N F O R K J I N Master thread 并行域 并行域 串行部分 串行部分

8 OpenMP存储模型

9 OpenMP存储模型 x = 2; #pragma omp parallel num_threads(2) shared(x) {
if (omp_get_thread_num() == 0) { sleep(1); x = 5; } else { printf("1: Thread# %d: x = %d\n", omp_get_thread_num(),x ); } #pragma omp barrier printf("2: Thread# %d: x = %d\n", omp_get_thread_num(),x ); printf("3: Thread# %d: x = %d\n", omp_get_thread_num(),x );

10 支持条件编译 int main() { #ifdef _OPENMP
printf("Compiled by an OpenMP-compliant implementation.\n"); #endif return 0; }

11 Hello world #include <omp.h> int main(int argc, char *argv[]) {
int nthreads, tid; /* Fork a team of threads */ #pragma omp parallel private(nthreads,tid) {     tid = omp_get_thread_num(); /* Obtain and print thread id */            printf("Hello, world from OpenMP thread %d\n", tid); if (tid == 0) /*Only master thread does this */ {      nthreads = omp_get_num_threads();      printf(" Number of threads %d\n",nthreads);   } } return 0; }

12 OpenMP编译器

13 OpenMP编译执行 编译 执行 运行结果: icc -openmp –o HelloWorld HelloWorld.c
Hello World from OpenMP thread 2 Hello World from OpenMP thread 0 Number of threads 4 Hello World from OpenMP thread 3 Hello World from OpenMP thread 1

14 OpenMP程序结构 基于Fortran语言的OpenMP程序结构 PROGRAM PROG_NAME
INTEGER VAR1, VAR2 ,VAR3 ………. !$OMP PARALLEL PRIVATE(VAR1, VAR2) SHARED(VAR3) !$OMP END PARALLEL …… END

15 OpenMP程序结构 基于C/C++语言的OpenMP程序结构 #include<omp.h> void main(){
int var1, var2, var3; …….. #pragma omp parallel private(var1, var2) shared(var3) { …………. } ……………

16 OpenMP制导指令 parallel用在一个代码段之前,表示这段代码将被多个线程并行执行
for 用于for循环之前,将循环分配到多个线程中并行执行,必须保证每次循环之间无相关性。 parallel for是parallel 和 for语句的结合,也是用在一个for循环之前,表示for循环的代码将被多个线程并行执行。 sections 用在可能会被并行执行的代码段之前 parallel sections parallel和sections两个语句的结合 critical 用在一段代码临界区之前 single 用在一段只被单个线程执行的代码段之前,表示后面的代码段将被单线程执行 barrier,用于并行区内代码的线程同步,所有线程执行到barrier时要停止直到所有线程都执行到barrier时才继续往下执行。 Atomic 用于指定一块内存区域被制动更新 Master 用于指定一段代码块由主线程执行 Ordered 用于指定并行区域的循环按顺序执行 threadprivate 用于指定一个变量是线程私有

17 OpenMP子句 private, 指定每个线程都有它自己的变量私有副本。
firstprivate,指定每个线程都有它自己的变量私有副本,并且变量要被继承主线程中的初值。 lastprivate,主要是用来指定将线程中的私有变量的值在并行处理结束后复制回主线程中的对应变量。 reduction,用来指定一个或多个变量是私有的,并且在并行处理结束后这些变量要执行指定的运算。 nowait,忽略指定中暗含的等待 num_threads,指定线程的个数 schedule,指定如何调度for循环迭代 shared,指定一个或多个变量为多个线程间的共享变量 ordered,用来指定for循环的执行要按顺序执行 copyprivate,用于single指导中的指定变量广播到并行区中其它线程 copyin,用来指定一个threadprivate的变量的值用主线程的值初始化。 default,用来指定并行处理区域内的变量的使用方式,缺省是shared

18 OpenMP库函数 omp_get_num_procs, 返回运行本线程的多处理机的处理器个数。
omp_get_num_threads, 返回当前并行区域中的活动线程个数。 omp_get_thread_num, 返回线程号       omp_set_num_threads, 设置并行执行代码的线程个数 omp_init_lock, 初始化一个简单锁 omp_set_lock, 上锁操作 omp_unset_lock, 解锁操作,要和omp_set_lock函数配对使用。 omp_destroy_lock, omp_init_lock函数的配对操作函数,关闭一个锁

19 提纲 OpenMP简介 OpenMP编译制导 OpenMP库函数 OpenMP环境变量

20 编译制导 OpenMP的并行化是通过使用嵌入到C/C++或Fortran源代码中的编译制导语句来实现。 编译制导是对程序设计语言的扩展。
通过对串行程序添加制导语句实现并行化。 支持并行区域、工作共享、同步等。 支持数据的共享和私有化。

21 制导语句格式 编译制导语句由下列几部分组成: 格式:制导标识符 制导名称 [子句,]
制导标识符 ( !$OMP 、 #pragma omp ) 制导名称(parallel,DO/for,section等) 子句(private, shared, reduction, copyin等) 格式:制导标识符 制导名称 [子句,]

22 编译制导标识 制导是特殊的、仅用于特定编译器的源代码。 制导由一个位于行首的标识加以区分。 OpenMP 制导标识: Fortran:
!$OMP (or C$OMP or *$OMP) C/C++: #pragma omp

23 并行域制导 一个并行域就是一个能被多个线程并行执行的程序段 Fortran: !$OMP PARALLEL [clauses] BLOCK
!$OMP END PARALLEL C/C++: #pragma omp parallel [clauses] { } 说明: 在并行域结尾有一个隐式同步(barrier)。 子句(clause)用来说明并行域的附加信息。 在Fortran语言中,子句间用逗号或空格分隔; C/C++子句间用空格分开。

24 并行域结构 Master thread Threads barrier Master thread Threads barrier

25 shared和private子句 并行域内的变量,可以通过子句说明为公有或私有;
在编写多线程程序时,确定哪些数据的公有或私有非常重要:影响程序的性能和正确性 Fortran: SHARED(list) PRIVATE(list) DEFAULT(SHARED|PRIVATE|NONE) C/C++: shared(list) private(list) default(shared|private|none)

26 shared和private子句 例:每个线程初始共享数组的一列 说明:如何决定哪些变量是共享哪些是私有?
!$OMP PARALLEL DEFAULT(NONE), PRIVATE(I, MYID), !$OMP & SHARED(a, n) myid=omp_get_thread_num()+1 do i=1, n a(i, myid)=1.0 end do !$OMP END PARALLEL 说明:如何决定哪些变量是共享哪些是私有? 通常循环变量、临时变量、写变量一般是私有的; 数组变量、仅用于读的变量通常是共享的。默认时为公有。 i 2 3 1

27 计算Pi值 /* Seriel Code */ static long num_steps = 100000; double step;
void main () { int i; double x, pi, sum = 0.0; step = 1.0/(double) num_steps; for (i=1;i<= num_steps; i++) { x = (i-0.5)*step; sum = sum + 4.0/(1.0+x*x); } pi = step * sum; printf (“Pi = %f\n”, pi);

28 并行域并行 #include <omp.h>
static long num_steps = ; double step; #define NUM_THREADS 4 void main () { int i ; double pi, sum[NUM_THREADS]; step = 1.0/(double) num_steps; omp_set_num_threads(NUM_THREADS) ; #pragma omp parallel private(i) { int id; double x; id = omp_get_thread_num(); for (i=id, sum[id]=0.0;i< num_steps; i=i+NUM_THREADS) { x = (i+0.5)*step; sum[id] += 4.0/(1.0+x*x); } for (i=0, pi=0.0;i<NUM_THREADS;i++) pi += sum[i] * step; printf (“Pi = %f\n”, pi);

29 并行DO/for循环制导 并行DO/for循环制导用来将循环划分成多个块,并分配给各线程并行执行。 说明:
Fortran: !$OMP DO[clauses] DO 循环 !$OMP END DO C/C++: #pragma omp for [clauses] for 循环 说明: 并行DO/for循环有时需要PRIVATE和FIRSTPRIVARE子句; 循环变量是私有的。

30 并行DO/for循环制导 可以将并行域和DO/for制导结合成单一的简单形式 !$OMP PARALLEL [clauses]
Fortran: !$OMP PARALLEL [clauses] !$OMP DO[clauses] 循环体 !$OMP END DO !$OMP END PARALLEL 合并后形式: !$OMP PARALLEL DO[clauses] !$OMP END PARALLEL DO 同样地,C/C++:合并后形式 #pragma omp parallel for [clauses] { 循环体 }

31 循环制导 #include <omp.h> #define NUM_THREADS 4
static long num_steps = ; double step; void main () { int i,id; double x, pi, sum[NUM_THREADS]; step = 1.0/(double) num_steps; omp_set_num_threads(NUM_THREADS); #pragma omp parallel private(x, id) { id = omp_get_thread_num(); sum[id] = 0; #pragma omp for for (i=0;i< num_steps; i++) { x = (i+0.5)*step; sum[id] += 4.0/(1.0+x*x); } for(i=0, pi=0.0;i<NUM_THREADS; i++) pi += sum[i] * step; printf (“Pi = %f\n”, pi);

32 SCHEDULE子句 该子句给出迭代循环划分后的块大小和线程执行的块范围 Fortran:
SCHEDULE(kind[, chunksize]) C/C++: schedule (kind[, chunksize]) 其中:kind为STATIC, DYNAMIC或RUNTIME, chunksize是一个整数表达式 例如: !$ OMP DO SCHEDULE (DYNAMIC,4) 循环体 !$ OMP DO

33 SCHEDULE子句 schedule (STATIC [, chunksize]) :
如果chunksize被指明,迭代空间被划分为chunksize大小,然后被轮转的分配给各个线程 例如:假如线程数为4 schedule(static) T T T T3 schedule(static, 4) T0 T1 T2 T3 T0 T1 T2 T3 T0 T1

34 SCHEDULE子句 schedule (DYNAMIC [, chunksize]) :
schedule (GUIDED [, chunksize]) 类似于DYNAMIC调度,但区间开始大,然后迭代区间越来越少,循环区间的划分是基于类似下列公式完成的(不同的编译系统可能不同): 其中N是线程个数,Sk表示第k块的大小,Rk是剩余下未被调度的循环迭代次数。 chunksize说明最小的区间大小。省略chunksize时,其默认值为1。

35 SCHEDULE子句

36 reduction子句 归约用来从相关的操作(+,*,max或min等)中产生一个单一值; OpenMP提供了reduction子句。
Fortran:REDUCTION(op:list) C/C++: reduction(op:list) 例子:将一组数值归约求和 sum=0; $OMP PARALLEL REDUCTION(+: sum), PRIVATE(i,myid) myid=omp_get_thread_num()+1 do i= 1, n sum=sum+a(i, myid) end do $OMP END PARALLEL 说明: 在reduction子句中,编译器为每个线程创建变量sum的私有副本。当循环完成后,将这些值加在一起并把结果放到原始的变量sum中; 数组、指针和引用类型不能出现在list; Reduction中的op操作必须满足算术结合律和交换律。

37 reduction子句 #include <omp.h> #define NUM_THREADS 4
static long num_steps = ; double step; void main () { int i,id; double x, pi, sum; step = 1.0/(double) num_steps; omp_set_num_threads(NUM_THREADS) ; start_time=omp_get_wtime(); #pragma omp parallel private(x, id) { #pragma omp for private(x) reduction(+:sum) for (i=0;i< num_steps; i++){ x = (i+0.5)*step; sum += 4.0/(1.0+x*x); } pi = sum*step; printf (“Pi = %f\n”, pi);

38 数据竞争 下面的循环无法正确执行: 在parallel结构中声明变量,这样的变量是私有的。 正确的方式: (直接声明为私有变量)
#pragma omp parallel for for(k=0;k<100;k++) { x=array[k]; array[k]=do_work(x); } 正确的方式: (直接声明为私有变量) #pragma omp parallel for private(x) 在parallel结构中声明变量,这样的变量是私有的。 #pragma omp parallel for for(k=0;k<100;k++) { int x; x=array[k]; array[k]=do_work(x); }

39 BARRIER制导 BARRIER是OpenMP用于线程同步的一种方法 Fortran: !$ OMP BARRIER C/C++:
#pragma omp barrier 说明: 在所有的线程到达之前,没有线程可以提前通过一个barrier; 在DO/FOR、SECTIONS和SINGLE制导后,有一个隐式barrier 存在; 要么所有线程遇到barrier;要么没有线程遇到barrier,否则会出现死锁

40 BARRIER制导 例子 ! $OMP PARALLEL PRIVATE(i, myid, neighb)
myid=omp_get_thread_num() neighb=myid-1 if (myid .eq. 0) neighb=omp_get_num_threads()-1 ……… a(myid)=a(myid)*3.5 ! $ OMP BARRIER b(myid)=a(neighb)+c ………… ! $ OMP END PARALLEL

41 提纲 OpenMP简介 OpenMP编译制导 OpenMP库函数 OpenMP环境变量

42 库函数 OpenMP标准定义了一个应用程序编程接口来调用库中的多个函数。
有时需要得到线程数和线程号,这在控制不同线程执行不同的功能代码时特别有用。 得到线程队列中的线程数 Fortran: interger function OMP_GET_NUM_THREADS () C/C++: #include<omp.h> int omp_get_num_threads()

43 库函数 得到执行线程的线程号: Fortran: Interger function OMP_GET_THREAD_NUM ()
C/C++: #include<omp.h> int omp_get_thread_num()

44 库函数 设定执行线程的数量 在制导语句中通过 NUM_THREADS设定。 通过环境变量OMP_NUM_THREADS 设定。
Fortran: routine OMP_SET_NUM_THREADS ( ) C/C++: #include<omp.h> omp_set_num_threads() 在制导语句中通过 NUM_THREADS设定。 通过环境变量OMP_NUM_THREADS 设定。

45 库函数 得到处理器数量 时间函数 Fortran INTEGER FUNCTION OMP_GET_NUM_PROCS() C/C++
#include <omp.h> int omp_get_num_procs(void) 时间函数 Fortran: DOUBLE PRECISION FUNCTION OMP_GET_WTIME() C/C++: double omp_get_wtime(void);

46 提纲 OpenMP简介 OpenMP编译制导 OpenMP库函数 OpenMP环境变量

47 环境变量 OpenMP提供环境变量用来控制并行代码的执行 设定线程数环境变量: 例如:
1. OMP_NUM_THREADS:设定最大线程数。 export OMP_NUM_THREADS=4 2. KMP_AFFINITY:设定线程绑定的环境变量。 export KMP_AFFINITY=scatter

48 NUM_THREADS子句 OpenMP (Fortran 、C/C++) 提供了NUM_THREADS子句设定线程数。 例子 说明:
!$OMP PARALLEL DO NUM_THREADS(4) DO J = 1,N A(I,J) = B(I,J) !$OMP END DO 说明: 在NUM_THREADS中提供的值将取代环境变量OMP_NUM_THREADS 的值 (或由 omp_set_num_threads()设定的值 )

49 NUM_THREADS子句 num_threads子句用来指定并行域内使用线程的个数,随后的其它并行域不受此影响。 例:
#include"omp.h" #include"stdio.h“ void main() { omp_set_num_threads(4); #pragma omp parallel num_threads(2) printf(“my thead number is %d\n",omp_get_thread_num()); } num_threads子句的优先权高于库例程omp_set_num_threads和环境变量NMP_NUM_THREADS。


Download ppt "周纯葆 中国科学院计算机网络信息中心 超级计算中心"

Similar presentations


Ads by Google