本文主要介绍OpenMP并行编程的环境变量和实战、主要对比理解嵌套并行的效果。
个人简介:一个全栈工程师的升级之路!
个人专栏:高性能(HPC)开发基础教程
CSDN主页 发狂的小花
人生秘诀:学习的本质就是极致重复!
目录
一、OpenMP是什么?
1 OpenMP的主要特点
2 Linux下OpenMP版本查看
3 OpenMP 环境变量
二、OPenMP实战
1 parallel
2 parallel for
3 最大线程数获取(核数)
4 嵌套的使用(重点)
4.1 简单单并行
4.2 双并行不允许嵌套
4.3 双并行允许嵌套
OpenMP(Open Multi-Processing)是一个用于C、C++和Fortran编程语言的并行编程模型。
是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,用于共享内存并行系统的多线程程序设计的一套指导性注释(Compiler Directive)。
支持OpenMP的编译器包括Sun Compiler,GNU Compiler和Intel Compiler等。它提供了一套简单而强大的API,使得开发人员可以轻松地在多核处理器上编写并行程序。
OpenMP通过将一个大的任务分解成多个小的任务,并将这些任务分配给不同的线程来并行执行,从而提高程序的性能。它提供了一些指令和函数,用于控制线程的创建、同步和通信等操作。
OpenMP提供了一种高层的抽象描述,用于并行算法。程序员可以通过在源代码中添加专用的pragma来明确表达他们的意图,这样编译器就可以自动将程序进行并行化,并在需要的地方添加同步、互斥和通信机制。当选择忽略这些pragma或者编译器不支持OpenMP时,程序可以退化为普通的串行程序,代码仍然可以正常运行,只是无法利用多线程来加速程序执行。
作为高层抽象,OpenMP并不适合需要复杂的线程间同步和互斥的场合。OpenMP的另一个缺点是不能在非共享内存系统(如计算机集群)上使用。在这样的系统上,MPI使用较多。
OpenMP官网 OpenMP官网
OpenMP规范 OpenMP规范
1. 简单易用:OpenMP提供了一组简单的API,使得开发人员可以轻松地编写并行代码。
2. 可移植性:OpenMP可以在各种硬件平台上运行,包括多核CPU、GPU和分布式系统等。
3. 高性能:OpenMP可以利用多核处理器的并行计算能力,提高程序的执行效率。
4. 共享内存模型:OpenMP使用共享内存模型来实现线程之间的通信和同步,避免了数据竞争的问题。
5. 支持多种编程范式:OpenMP支持多种编程范式,包括数据并行、任务并行和指令级并行等。
Linux的GCC编译器支持OpenMP,版本的查看使用如下:
echo |cpp -fopenmp -dM |grep -i open
执行后打印例如:
#define _OPENMP 201511
可以到OpenMP Specification 查看对应的版本映射,201511代表2015年11月发布的OpenMP版本。
在性能优化-OpenMP基础教程(一)中主要介绍了OpenMP的指令和函数,这里补充OpenMP的环境变量。
环境变量 | 描述 |
---|---|
OMP_NUM_THREADS | 指定并行区域中使用的线程数 |
OMP_PROC_BIND | 控制线程与处理器之间的绑定关系 |
OMP_PLACES | 指定线程在处理器上的放置方式 |
OMP_SCHEDULE | 控制循环迭代的调度策略 |
OMP_STACKSIZE | 指定线程栈的大小 |
OMP_DYNAMIC | 控制是否启用动态调度 |
OMP_DEBUG | 控制是否启用OpenMP调试功能 |
OMP_WAIT_POLICY | 控制线程等待其他线程完成的策略 |
OMP_FLUSH_INTERVAL | 指定刷新内存缓存的时间间隔 |
OMP_PROC_BIND | 控制线程与处理器之间的绑定关系 |
OMP_PLACES | 指定线程在处理器上的放置方式 |
OMP_SCHEDULE | 控制循环迭代的调度策略 |
OMP_STACKSIZE | 指定线程栈的大小 |
OMP_DYNAMIC | 控制是否启用动态调度 |
OMP_DEBUG | 控制是否启用OpenMP调试功能 |
OMP_WAIT_POLICY | 控制线程等待其他线程完成的策略 |
OMP_FLUSH_INTERVAL | 指定刷新内存缓存的时间间隔 |
编译仅仅需要在g++或者gcc 后面加编译选项 -fopenmp 。需要调用OpenMP的某些接口时,需要在代码中包含
gcc test.c -fopenmp -o test
g++ test1.cpp -fopenmp -o test1
编译制导指令parallel,用来创建并行域,后面紧跟需要创建并行域的代码,紧跟的才有用,可以使用{}括起来,空行不算代码。
#include
#include"omp.h"
using namespace std;
int main()
{
#pragma omp parallel
{
cout << "parallel Test" << endl;
}
{
cout << "serial Test" << endl;
}
return 0;
}
运行结果:
由于笔者电脑有八个核,所以打印8次。可以明显看出只有紧跟的代码才并行处理。OpenMP可以非常简单的编写并行程序,这是它的优势。
注意:输出也可能是乱码,因为多线程共享标准输出引起的竞争条件。
paraller 仅仅只是让系统有了并行域,创建了多个线程执行相同的内容,并没有提高效率。使用parallel for可以让内容分配给不同的线程去执行,注意是将一个任务划分为多个子任务让多核系统去执行,这样就提高了效率,这才是OpenMP的核心。parallel for 可以默认使用系统的多核线程数,也可以用num_threads(number)指定线程数。
parallel for 只作用于紧跟的for循环,但是这个for循环是可以嵌套的。
注意parallel for 需要搭配for 循环使用。
#include
#include"omp.h"
using namespace std;
int main()
{
#pragma omp parallel for
for (int i = 0;i < 8;i++)
{
printf("ThreadID: %d i = %d 当前并行域线程数: %d \n",omp_get_thread_num(),i,omp_get_num_threads());
}
printf("****************************\n");
#pragma omp parallel for num_threads(4)
for (int i = 0;i < 8;i++)
{
printf("ThreadID: %d i = %d 当前并行域线程数: %d \n",omp_get_thread_num(),i,omp_get_num_threads());
}
{
cout << "serial Test" << endl;
}
return 0;
}
运行结果:
#include
#include
int main()
{
#ifdef _OPENMP
printf("Max threads nums: %d \n",omp_get_max_threads());
#else
#endif
return 0;
}
运行结果:
omp_set_nested(1) 设置允许嵌套使用,作用域在下面所有代码,不包括设置之前,只有当设置为omp_set_nested(0)时,嵌套允许才会被取消。
omp_get_nested() 测试当前并行域是否支持嵌套使用
#include
#include
int main()
{
// omp_set_nested(1); // 设置允许嵌套并行可用
#pragma omp parallel num_threads(2)
{
printf("第一级, thread %d of %d\n", omp_get_thread_num(),omp_get_num_threads());
// #pragma omp parallel num_threads(2)
{
printf("第二级, thread %d of %d\n", omp_get_thread_num(),omp_get_num_threads());
}
}
return 0;
}
运行结果:
分析可知2个线程,都执行第一级和第二级,两个线程都执行一次第一级和第二级。
#include
#include
int main()
{
// omp_set_nested(1); // 设置允许嵌套并行可用
#pragma omp parallel num_threads(2)
{
printf("第一级, thread %d of %d\n", omp_get_thread_num(),omp_get_num_threads());
#pragma omp parallel num_threads(2)
{
printf("第二级, thread %d of %d\n", omp_get_thread_num(),omp_get_num_threads());
}
}
return 0;
}
运行结果:
分析可知,第一级部分获得两个线程执行,第二级一个线程执行两次,这是不允许嵌套并行。
#include
#include
int main()
{
omp_set_nested(1); // 设置允许嵌套并行可用
#pragma omp parallel num_threads(2)
{
printf("第一级, thread %d of %d\n", omp_get_thread_num(),omp_get_num_threads());
#pragma omp parallel num_threads(2)
{
printf("第二级, thread %d of %d\n", omp_get_thread_num(),omp_get_num_threads());
}
}
return 0;
}
运行结果:
分析可知,第一级在嵌套并行外部,因此在允许嵌套并行的情况下,执行两次;第二级在嵌套并行内,因此外部的每个线程会产生两个线程,这样就有4个线程执行第二级,这是允许嵌套执行的效果。
我的分享也就到此结束啦
如果我的分享也能对你有帮助,那就太好了!
若有不足,还请大家多多指正,我们一起学习交流!
未来的富豪们:点赞→收藏⭐→关注,如果能评论下就太惊喜了!
感谢大家的观看和支持!最后,☺祝愿大家每天有钱赚!!!下一节继续对OpenMP的更深层次的编程进行讲解!