openMP飞速入门
openMP 入门指南
参考超算习堂 (easyhpc.net)
主要关注普通用法和锁的使用就行了
parallel
#pragma omp parallel [for|section]
{
//并行运行句子
}
#pragma omp parallel num_threads(6) //申请6个线程同时处理
{
}
for
- 与 parallel连用
#pragma omp parallel for
{
}
- 在并行块中重复使用
#pragma omp parallel
{
for
for
}
section 和 sections
sections 声明分块 section指定并行块中的分块 每个分块都会并行执行
#pragma omp [parallel] sections
{
#pragma omp section
{
}
}
private firstprivate lastprivate threadprivate
因为在并行块中重新定义申请同名变量,等同于只能在并行快分为内使用。
private解决线程的私有变量,每个线程拥有自己的同名变量
#include <stdio.h>
#include <omp.h
int main(int argc, char* argv[])
{
int i = 20;
#pragma omp parallel for private(i)
for (i = 0; i < 10; i++)
{
printf("i = %d\n", i);
}
printf("outside i = %d\n", i);
return 0;
}
firstprivate 继承之前的同名变量的值
#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[])
{
int t = 20, i;
#pragma omp parallel for firstprivate(t)
for (i = 0; i < 5; i++)
{
t += i;
printf("t = %d\n", t);
}
printf("outside t = %d\n", t);
return 0;
}
lastprivate传递值给之后的同名的变量
#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[])
{
int t = 20, i;
#pragma omp parallel forlastprivate(t)
for (i = 0; i < 5; i++)
{
t += i;
printf("t = %d\n", t);
}
printf("outside t = %d\n", t);
return 0;
}
threadprivate 将变量复制一个私有拷贝给各个线程,各线程有自己的私有全局对象, 最后的写入对象才会对全局写入造成影响. 可同时读写 threadprivate只能用于全局变量或静态变量
#include <stdio.h>
#include <omp.h>
int g = 0;
#pragma omp threadprivate(g)
int main(int argc, char* argv[])
{
int t = 20, i;
#pragma omp parallel
{
g = omp_get_thread_num(); //并行的线程有自己的副本
}
#pragma omp parallel
{
printf("thread id: %d g: %d\n", omp_get_thread_num(), g);
}
return 0;
}
shared子句
声明内的并行线程共享, 写操作时尽量转为私有变量进行保护
#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[])
{
int t = 20, i;
// YOUR CODE HERE
#pragma omp parallel for shared(t) //不是副本,大家都可以读写
// END OF YOUR CODE
for (i = 0; i < 10; i++)
{
if (i % 2 == 0)
t++;
printf("i = %d, t = %d\n", i, t);
}
return 0;
}
reduction子句
一个操作符, 多个参数, 每个线程创建该参数的私有拷贝, 并行结束后迭代指定运算符,更新参数值
reduction(operator: list, list )
#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[])
{
int i, sum = 10;
#pragma omp parallel for reduction(+: sum)
for (i = 0; i < 10; i++)
{
sum += i;
printf("%d\n", sum);
}
printf("sum = %d\n", sum);
return 0;
}
copyin子句
将main主线程中变量(threadprivate变量) 拷贝刀各线程私有变量中,线程可以访问主线程变量.
#include <stdio.h>
#include <omp.h>
int g = 0;
#pragma omp threadprivate(g)
int main(int argc, char* argv[])
{
int i;
#pragma omp parallel for
for (i = 0; i < 4; i++)
{
g = omp_get_thread_num();
printf("thread %d, g = %d\n", omp_get_thread_num(), g);
}
printf("global g: %d\n", g);
// YOUR CODE HERE
#pragma omp parallel for copyin(g)
// END OF YOUR CODE
for (i = 0; i < 4; i++)
printf("thread %d, g = %d\n", omp_get_thread_num(), g);
return 0;
}
static子句调度
parallel没带schedule,默认static 均分调度,每个线程n/t次迭代,相差1次左右
pragma omp parallel for schedule(method)
#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[])
{
int i;
// YOUR CODE HERE
#pragma omp parallel for schedule(static)
// END OF YOUR CODE
for (i = 0; i < 10; i++)
{
printf("i = %d, thread %d\n", i, omp_get_thread_num());
}
return 0;
}
Size 指定最小迭代次数
静态调度,分配给线程size的最小迭代次数, 每个线程最多可能相差size次迭代(可以有线程不迭代)
pragma omp parallel for schedule(static, size)
#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[])
{
int i;
// YOUR CODE HERE
#pragma omp parallel for schedule(static, 3)
// END OF YOUR CODE
for (i = 0; i < 10; i++)
{
printf("i = %d, thread %d\n", i, omp_get_thread_num());
}
return 0;
}
dynamic子句动态内存分配
无法预先确定哪个线程先后与时间, 取决于系统资源和线程的调度情况
#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[])
{
int i;
// YOUR CODE HERE
#pragma omp parallel for schedule(dynamic)
// END OF YOUR CODE
for (i = 0; i < 10; i++)
{
printf("i = %d, thread %d\n", i, omp_get_thread_num());
}
return 0;
}
常用参数
omp_get_num_procs() 可用处理器数
omp_get_thread_num()当前的线程号
omp_get_num_threads() 进入并行前要创建 线程的数量
omp_set_num_threads() 创建指定数目线程
omp_in_parallel() 判定并行状态
omp_get_max-threads() 这个最大数量是指在不使用num_threads的情况下,OpenMP可以创建的最大线程数量
互斥锁的用法
void omp_init_lock(omp_lock) 初始化互斥锁
void omp_destroy_lock(omp_lock) 销毁互斥锁
void omp_set_lock(omp_lock) 获得互斥锁
void omp_unset_lock(omp_lock) 释放互斥锁
bool omp_test_lock(omp_lock*) 该函数可以看作是omp_set_lock的非阻塞版本。
#include <stdio.h>
#include <omp.h>
static omp_lock_t lock;
int main(int argc, char* argv[])
{
int i;
omp_init_lock(&lock);
#pragma omp parallel for
for (i = 0; i < 5; ++i)
{
// YOUR CODE HERE
omp_set_lock(&lock);
// END OF YOUR CODE
printf("%d+\n", omp_get_thread_num());
printf("%d-\n", omp_get_thread_num());
// YOUR CODE HERE
omp_unset_lock(&lock);
// END OF YOUR CODE
}
omp_destroy_lock(&lock);
return 0;
}
#include <stdio.h>
#include <omp.h>
static omp_lock_t lock;
int main(int argc, char* argv[])
{
int i;
omp_init_lock(&lock);
#pragma omp parallel for
for (i = 0; i < 5; ++i)
{
// YOUR CODE HERE
if (omp_test_lock(&lock))
// END OF YOUR CODE
{
printf("%d+\n", omp_get_thread_num());
printf("%d-\n", omp_get_thread_num());
omp_unset_lock(&lock);
}
else
{
printf("fail to get lock\n");
}
}
omp_destroy_lock(&lock);
return 0;
}
omp_set_dynamic
运行时动态调整并行区域的线程数
void omp_set_dynamic(int)
当参数为0时,动态调整被禁用。
当参数为非0值时,系统会自动调整线程以最佳利用系统资源
omp_get_dynamic
int omp_get_dynamic() = 1 可以动态调整线程数, 0 不可以调整