openMP是基于共享内存的编程模型,其编程是对线程来说的,就是基于编译器对omp的编译指导语句来编写的并行程序,其最大的好处是屏蔽掉多线程的底层细节,可以较快的把串行执行的程序通过少量的修改变成多线程程序。当然它的劣势很明显,只能够线程并行,而且调试起来麻烦,并不适合多机器分布的多任务之间的并行。
OMP 包含编译制导(Compiler Directive)、运行库例程(Runtime Library)和环境变量(Environment Variables)三部分。
OpenMP采用Fork-Join并行执行方式。
下面是一些omp语法的介绍,其中比较难的是对程序的并行划分,和数据依赖的处理。数据依赖分为:读后写,写后读和写后写的情况。这点和数据库的要保持数据的一致性蛮像的。
parallel语句结构
并行域中的代码被所有的线程执行,相当于有N个线程,都执行这个并行域中的代码。
#pragma omp parallel [clause[[,]clause]…]newline
- clause=
- if (scalar_expression)
- private (list)
- shared (list)
- default (shared | none)
- firstprivate (list)
- reduction (operator: list)
- copyin (list)
- for 语句结构
- for 用于for循环之前,将循环分配到多个线程中并行执行,必须保证每 次循环之间无相关性。
- parallel forparallel
- 和for语句的结合,也是用在一个for循环之前,表示for 循环的代码将被多个线程并行执行。
- sections 用在可能会被并行执行的代码段之前 parallel sections parallel和sections两个语句的结合
- critical 用在一段代码临界区之前
- single 用在一段只被单个线程执行的代码段之前,表示后面的代码段将 被单线程执行
- barrier,用于并行区内代码的线程同步,所有线程执行到barrier时要停止 直到所有线程都执行到barrier时才继续往下执行。
- Atomic 用于指定一块内存区域被制动更新
- Master 用于指定一段代码块由主线程执行
- Ordered 用于指定并行区域的循环按顺序执行
- threadprivate用于指定一个变量是线程私有
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函数的配对操作函数,关闭一个锁
OMP的子句还有如下一些
- private, 指定每个线程都有它自己的变量私有副本。
- firstprivate,指定每个线程都有它自己的变量私有副本,并且变量要被 继承主线程中的初值。
- lastprivate,主要是用来指定将线程中的私有变量的值在并行处理结束后 复制回主线程中的对应变量。
- reduction,用来指定一个或多个变量是私有的,并且在并行处理结束后 这些变量要执行指定的运算。
- nowait,忽略指定中暗含的等待
- num_threads,指定线程的个数
- schedule,指定如何调度for循环迭代
- shared,指定一个或多个变量为多个线程间的共享变量
- ordered,用来指定for循环的执行要按顺序执行
- copyprivate,用于single指导中的指定变量广播到并行区中其它线程
- copyin,用来指定一个threadprivate的变量的值要用主线程的值进行初始 化。
- default,用来指定并行处理区域内的变量的使用方式,缺省是shared
再写OpenMP的时候遇到的问题
1. OMP 的for 语句后面只可以选择一个clause。
2. 外部环境的export优先级低于程序里函数设置参数的优先级。
3. omp_set_num_threads 用于获取当前线程组(team)的线程数量,如果不在并行区调用,返回1.
下面是LU矩阵分解并行化的程序
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <omp.h>
#define N 5
#define sytax "%6.3f "
#define NUM_THREADS 4
float a[N][N];
float l[N][N];
float u[N][N];
void clumar(float a[N][N], float l[N][N], float u[N][N])
{
// memset(l, 0, sizeof(l));
// memset(u, 0, sizeof(u));
int i, j, k;
omp_set_num_threads(NUM_THREADS);
#pragma omp parallel private(i, j, k)
{
int id = omp_get_thread_num();
printf("thread is %d\n", id);
for(k = 1 +id ; k <= N; k += NUM_THREADS)
{
for(i = k+1; i <= N; ++i)
{
a[i][k] = a[i][k] / a[k][k];
}
for(i = k+1; i <= N; ++i)
for(j = k+1; j <= N; ++j)
a[i][j] = a[i][j] - a[i][k] * a[k][j];
}
}
int chunk = 2;
#pragma omp parallel for \
shared(a, l, u, chunk) private(i, j) \
schedule(dynamic, chunk)
for(i = 1; i <= N; ++i)
{
for(j = 1; j <= N; ++j)
{
if( j < i)
l[i][j] = a[i][j];
else
u[i][j] = a[i][j];
}
l[i][i] = 1;
}
}
void print(float data[N][N])
{
int i, j;
for(i = 1; i <= N; ++i)
{
for(j = 1; j <= N; ++j)
{
printf(sytax, data[i][j]);
}
printf("\n");
}
}
int main()
{
// init_data(a, 10); //为什么VS下面可以调用此函数而PGI的环境不支持,OpenMP 感觉各种奇葩
for(int i = 1; i <= N; ++i)
{
for(int j = 1; j <= N; ++j)
{
a[i][j] = i + j;
}
}
clumar(a, l, u);
printf("\n\nthe l Matrix value:\n\n");
print(l);
printf("\n\nthe u Matrix value:\n\n");
print(u);
}