1. OpenMP简介
- 通过多线程实现并行
- 开放式的多处理框架 Open Multi-Process
- 统一内存访问 : 多个CPU共享同一片内存,pthread/OpenMP在CPU上执行,属于统一内存访问
- 非统一内存访问: 每个CPU有单独的Memory, 如MPI,通过总线通信的方式获取内存信息
OpenMP与PThread
- pthread需要明确指定每个线程的行为,openmp只需简单的生命代码段需要并行即可
- pthread依赖pthreads库,能被任意C编译器使用,openmp依赖openmp库,还依赖编译器支持某些特定操作
- pthread可通过编写代码明确线程执行的过程,而openmp难以对底层的线程交互进行编程
Fork与Join
- Fork: 由主线程创建出一系列并行化执行的子线程(派生)
- Join: 多个线程完成工作之后,进行同步与终止,回到主线程执行(合并)
2. OpenMP使用
- OpenMP提供基于指令的共享内存API
- 编译器需要支持pragma指令提示的语句编译
- 在系统中加入预处理指令一般是用来允许不符合C语言基本规划的行为
OpenMP parallel 指定并发线程数目
- 编译器指令: #pragma omp parallel num_threads(num)
#include void test()
{int num = omp_get_thred_num(); // 获取线程是第几个int thrNum = omp_get_num_threads(); //获取线程总数// 打印当前线程是omp诸多线程中的哪一个
}int main()
{
#pragma omp parallel num_threads(4)test();
return 0;
}
OpenMP for
- 编译器指令: #pragma omp parallel for
- 指定指令后的for循环语句,将使用并行的方式执行,for语句需要满足:
(1)循环的迭代器必须是可计算的,在循环前需要确定迭代的次数
(2)循环代码块中不能包含break、return、exit
(3)循环语句中不能使用goto跳出循环之外
(4)迭代器只能被for语句中的增量表达式所修改
#pragma omp parallel for num_threads(4)for(int i = 0; i < 1000; ++i){if(i == 400){// 打印在哪个线程中执行}}
- 数据依赖性: 循环中的计算依赖之前迭代计算的结果
(1)编译器不检查被parallel for指令并行化的循环所包含的迭代间的依赖关系
(2)一个或更多迭代的结果依赖其他迭代的循环,一般不能被正确的并行化
// 斐波那契数列案例
OpenMP Private Variables
- OpenMP能共享进程中作用域范围内的变量,也可指定parallel块中能够访问该变量的线程集合
- 能够被线程组中所有的线程访问的变量拥有共享作用域,而一个只能被单个线程访问的变量拥有私有作用域
- Private在pragma指令中是一个可选附加的选项,它能直接高速编译器去将共享变量作为每个线程中的私有变量 #pragma omp … private()
- Private的说明
(1)每一个线程都是拥有独立的该变量的副本,不会共享原来的初始值
(2)变量被定义为私有变量后,其他线程均不能访问某一个线程的私有变量
(3)所有线程都不会使用先前变量的定义
(4)所有线程均不能改变私有变量赋值
(5)在循环的出入口均不会进行定义
*FirstPrivate的说明 #pragma omp parallel for firstprivate(var)
(1)告诉编译器私有变量在第一个循环会继承共享变量的值
(2)这个私有变量只会在每个线程的第一个循环继承原始的值,不会再每个循环中继承
(3)使用方法与Private一致
-
变量的拷贝
(1)变量是基础数据类型,如Int,double等,会将数据进行直接的拷贝
(2)如果变量是一个数组,会分配数组大小的私有内存后,拷贝数组内容
(3)如果变量是指针,会将地址指向的地址拷贝过去,指向相同的堆内容
(4)如果变量是一个类的实例,会调用对应的构造函数构造一个私有的变量
-
LastPrivate的说明 #pragma omp parallel for lastprivate(var)
(1)告诉编译器私有变量会在最后一个循环出去的时候,用私有变量的值替换掉共享变量的值
OpenMP Section
for: 在代码段中一部分代码再次分成多个子线程执行
single : 代码段中指定一部分代码仅由单个线程执行
master: 代码段中指定一部分代码仅由主线程执行
Section选项
- sections在封闭代码的指定部分中,由线程组进行分配任务
- 每个独立的section都需要在sections里面
- 每个section都是被一个section执行
- 不同的section可能执行不同的任务
- 如果一个线程够快,它可能执行多个section