OpenMP入门
目的
一种简单的方式, 让程序员不需要懂得创建和销毁线程就能写出多线程化程序
只需要告诉OpenMP哪里需要被线程化, 剩下的由OpenMP决定
核心
头文件 #include <omp.h>
编译
一般结构
标记并行块 #pragma omp parallel
标记单个并行单元 #pragma omp single/for/section(s)/task
可以简写为 #pragma omp parallel for
等等
外部设备 #pragma omp target
#include <omp.h>
int main() {
#pragma omp parallel
{
#pragma omp for
}
}
#include <stdio.h>
#include <omp.h>
int main(int argc, char** argv) {
int num_thread = 4;
omp_set_num_threads(num_thread); // 设定线程数
#pragma omp parallel num_threads(7) // 然后改成7线程了
{
int id = omp_get_thread_num(); // 查询本线程号
printf("hello from thread%d\n",id);
}
return 0;
}
for 循环
#pragma omp parallel for
for (char i = 'a'; i <= 'z'; i++) // i是循环变量
std::cout << i << std::endl; // 可以执行, 但是endl会冲
fact[i] = i * fact[i-1]; // 不会报错, 但是会出错. 竞态条件
return 0;
#pragma omp parallel for
for (int i = 0; i < numElements; i++) {
array[i] = initValue + i; // pass
// array[i] = initValue++; // error
}
shared / private 共有和私有数据
分类
共有shared
: 所有线程访问的都是相同的内存地址
私有private
: 每个线程都有自己的一份拷贝
默认情况下, 只有循环变量是私有, 其他变量默认共有
设定私有
int temp, array[10];
#pragma omp parallel for private(temp) shared(array)
// 其实share(array)不写也没关系
for (int i = 0; i < 10; i++) {
temp = array[i];
array[i] = doSomething(temp);
}
Reductions 归并
int sum = 0;
#pragma opm parallel for reduction(+:sum)
for (int i = 0; i < 100; i++) {
sum += array[i];
}
schedule 循环调度
用于for
循环
希望实现负载均衡, 不出现空闲时间. 需要进行分配上的优化
需要提供一些信息给OpenMP, 告知有可能的优化方式
默认循环时间相同, 把不同迭代等分到不同核心上, 同时尽可能减小内存访问冲突
所以需要综合考虑内存访问和负载均衡
#pragma omp parallel for schedule(<kind>, <chunk size>)
例子
#pragma omp parallel for schedule(static)
#pragma omp parallel for schedule(dynamic, 3)
#pragma omp parallel for schedule(guided, 4)
collapse 多层循环 (since openmp 3.0)
多层循环, 可以保持循环的层次
似乎只能是多层循环, 且两层循环层间不能有东西?
#pragma omp parallel for collapse(2)
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 6; j++) {
workintwocycles(i, j);
}
// 层间可以有东西吗?
}
task 单进程中多线程
single 仅进行一次
omp_lock_t 锁
定义于omp.h
, 等效于mutex
有5种操作
初始化锁 void omp_init_lock(omp_lock_t*)
销毁锁 void omp_destroy_lock(omp_lock_t*)
获得锁 void omp_set_lock(omp_lock_t*)
释放锁 void omp_unset_lock(omp_lock_t*)
尝试获取锁 int omp_test_lock(omp_lock_t*)
例子
#include <iostream>
#include <omp.h>
static omp_lock_t lock;
int main() {
omp_init_lock(&lock); //初始化互斥锁
#pragma omp parallel for
for(int i = 0; i < 5; ++i) {
omp_set_lock(&lock); //获得互斥器
std::cout << omp_get_thread_num() << "+" << std::endl;
std::cout << omp_get_thread_num() << "-" << std::endl;
omp_unset_lock(&lock); //释放互斥器
}
omp_destroy_lock(&lock); //销毁互斥器
return 0;
}
sections 局部并行/串行
一个section
内的block必须串码运行, 不同section
之间可以并行
如下, parallel sections {}
调出, section {}
指定串行内容
#pragma omp parallel sections
{
{work1();}
#pragma omp section
{work2();
work3();}
#pragma omp section
{work4();}
}
critical 互斥
#pragma omp critical(dataupdate)
{
datastructure.reorganize();
}
#pragma omp critical(dataupdate)
{
datastructure.reorganize_again();
}
atomic 原子性
ordered 保持顺序
barrier 强制同步
强制等待全部线程都完成后再操作
#pragma omp barrier
master
flush
simd
例子
保留区
老师上课提到的<directive>
parallel
for
sections
section
critical
atomic
collapse
parallel for
parallel sections
single
master
barrier
flush
threadprivate
task
simd
target
distribute
teams
cancel
尽量减少一下使用
参考文献
讨论区
现在还没有尝试用visual studio —
娄泽坤 2020/12/18 13:54
似乎有一些要注意的地方:
-