OpenMP 环境变量使用总结
OpenMP 环境变量使用总结
- OMP_CANCELLATION,在 OpenMP 规范 4.5 当中规定了取消机制,我们可以使用这个环境变量去设置是否启动取消机制,如果这个值等于 TRUE 那么就是开启线程取消机制,如果这个值等于 FALSE 那么就是关闭取消机制。
#include <stdio.h>
#include <omp.h>
int main()
{
int s = omp_get_cancellation();
printf("%d\n", s);
#pragma omp parallel num_threads(8) default(none)
{
if (omp_get_thread_num() == 2)
{
#pragma omp cancel parallel
}
printf("tid = %d\n", omp_get_thread_num());
}
return 0;
}
在上面的程序当中,如果我们启动取消机制,那么线程号等于 2 的线程就不会执行后面的 printf 语句。
➜ cmake-build-hun git:(master) ✗ export OMP_CANCELLATION=TRUE # 启动取消机制
➜ cmake-build-hun git:(master) ✗ ./cancel
1
tid = 0
tid = 4
tid = 1
tid = 3
tid = 5
tid = 6
tid = 7
- OMP_DISPLAY_ENV,这个环境变量的作用就是程序在执行的时候首先会打印 OpenMP 相关的环境变量。如何这个环境变量值等于 TRUE 就会打印环境变量的值,如果是 FLASE 就不会打印。
➜ cmake-build-hun git:(master) ✗ export OMP_DISPLAY_ENV=TRUE
➜ cmake-build-hun git:(master) ✗ ./critical
OPENMP DISPLAY ENVIRONMENT BEGIN
_OPENMP = '201511'
OMP_DYNAMIC = 'FALSE'
OMP_NESTED = 'FALSE'
OMP_NUM_THREADS = '32'
OMP_SCHEDULE = 'DYNAMIC'
OMP_PROC_BIND = 'FALSE'
OMP_PLACES = ''
OMP_STACKSIZE = '0'
OMP_WAIT_POLICY = 'PASSIVE'
OMP_THREAD_LIMIT = '4294967295'
OMP_MAX_ACTIVE_LEVELS = '2147483647'
OMP_CANCELLATION = 'TRUE'
OMP_DEFAULT_DEVICE = '0'
OMP_MAX_TASK_PRIORITY = '0'
OMP_DISPLAY_AFFINITY = 'FALSE'
OMP_AFFINITY_FORMAT = 'level %L thread %i affinity %A'
OPENMP DISPLAY ENVIRONMENT END
data = 0
- OMP_DYNAMIC,如果将这个环境变量设置为true,OpenMP实现可以调整用于执行并行区域的线程数,以优化系统资源的使用。与这个环境变量相关的一共有两个函数:
void omp_set_dynamic(int);
int omp_get_dynamic(void);
omp_set_dynamic 使用这个函数表示是否设置动态调整线程的个数,如果传入的参数不等于 0 表示开始,如果参数等于 0 就表示关闭动态调整。
我们现在来谈一谈 dynamic 动态调整线程个数以优化系统资源的使用是什么意思,这个意思就是 OpenMP 创建的线程个数在同一个时刻不会超过你系统的处理器的个数,因为 OpenMP 常常用在数据密集型任务当中,这类任务对 CPU 的需求大,因此为来充分利用资源,只会创建处理器个数的线程个数。
下面我们使用一个例子来验证上面锁谈到的内容。
#include <omp.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
// omp_set_dynamic(1);
#pragma omp parallel num_threads(33) default(none)
{
printf("tid = %d\n", omp_get_thread_num());
}
return 0;
}
上面的代码如果我们没有设置 OMP_DYNAMIC=TRUE 或者没有使用 omp_set_dynamic(1) 去启动态调整的话,那么上面的 printf 语句会被执行 33 次,但是如果你进行了设置,也就是启动了动态调整线程的个数的话,那么创建的线程个数就是 min(33, num_processors) ,后者是你的机器的处理器的个数,比如如果处理器的核的个数是 16 那么就只会有 16 个线程执行并行域当中的代码。
- OMP_NESTED,这个表示是否开启并行域的嵌套模式,这个环境变量要么是
TRUE或者FALSE,如果这个环境变量的值为TRUE那么能够嵌套的最大的并行域的数量受到环境变量 OMP_MAX_ACTIVE_LEVELS 的限制,与这个环境变量相关的一个动态库函数为void omp_set_nested(int nested);,表示是否开启嵌套的并行动态库。 - OMP_NUM_THREADS,这个表示设置并行域的线程个数,与这个环境变量相关的有 num_threads 这个子句和动态库函数
void omp_set_num_threads(int num_threads);也是相关的。他们的优先级为:num_threads > omp_set_num_threads > OMP_NUM_THREADS。这个环境变量的值必须是一个大于 0 的整数,关于他们的优先级你可以认为离并行域越远的就优先级越低,反之越高。 - OMP_STACKSIZE,这个环境变量的主要作用就是设置一个线程的栈空间的大小。
- OMP_WAIT_POLICY,这个参数的主要作用就是控制当线程没有拿到锁的时候是自旋获取锁还是进入内核被挂起。这个参数主要有两个值,active 或者 passive。
- PASSIVE,等待的线程不消耗 CPU ,而是进入内核挂起。
- ACTIVE,等待的线程消耗 CPU,一直自旋获取锁。
我们现在使用例子来验证上面的规则:
#include <stdio.h>
#include <omp.h>
int main()
{
omp_lock_t lock;
omp_init_lock(&lock);
#pragma omp parallel num_threads(16) default(none) shared(lock)
{
omp_set_lock(&lock);
while (1);
omp_unset_lock(&lock);
}
return 0;
}
在上面的代码当中有一个并行域,并行域中线程的个数是 16,我们首先使用 ACTIVE 来看一下这个进程的负载,根据前面我们的描述那么 16 个线程都会在自旋获取锁,这个过程将会一直使用 CPU,因此这个进程的负载 %CPU ,应该是接近 1600 % ,每个线程都是 100% 加起来就是 1600 % 。
➜ cmake-build-openmp export OMP_WAIT_POLICY=ACTIVE
➜ cmake-build-openmp ./wait_policy
我们使用 top 命令查看一下这个进程的 CPU 使用率。
top - 17:27:14 up 263 days, 2:11, 2 users, load average: 93.87, 87.59, 85.78
Tasks: 31 total, 2 running, 29 sleeping, 0 stopped, 0 zombie
%Cpu(s): 80.0 us, 0.7 sy, 0.0 ni, 19.2 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 13191648+total, 54673112 free, 15049648 used, 62193724 buff/cache
KiB Swap: 12499968+total, 11869649+free, 6303184 used. 11600438+avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
112290 root 20 0 133868 1576 1452 R 1600 0.0 11:52.84 wait_policy
根据上面的输出结果我们可以看到我们的预测是对的,所有的线程事都活跃的在使用 CPU。
现在我们再来看一下如果我们使用 PASSIVE 的情况会是怎么样的?根据前面的描述如果线程没有获取到锁那么就会被挂起,因为只能够有一个线程获取到锁,其余 15 个线程都将被挂起,因此 CPU 的使用率应该是 100 % 左右,这个线程就是那个获取到锁的线程。
➜ cmake-build-openmp export OMP_WAIT_POLICY=PASSIVE
➜ cmake-build-openmp ./wait_policy
我们再使用 top 命令查看一下对应的输出:
top - 17:27:53 up 263 days, 2:11, 2 users, load average: 92.76, 88.10, 86.03
Tasks: 31 total, 2 running, 29 sleeping, 0 stopped, 0 zombie
%Cpu(s): 53.3 us, 0.8 sy, 0.0 ni, 45.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 13191648+total, 54675824 free, 15046932 used, 62193728 buff/cache
KiB Swap: 12499968+total, 11869649+free, 6303184 used. 11600710+avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
112317 root 20 0 133868 1624 1496 R 99.3 0.0 0:04.58 wait_policy
从上面的输出结果来看也是符合我们的预期,只有一个线程在不断的使用 CPU。
GOMP_SPINCOUNT,这个环境变量的主要作用就是当 OMP_WAIT_POLICY 是 active 的时候,最多忙等待自旋多少次,如果自旋的次数超过这个值的话,那么这个线程将会被挂起。
当这个环境变量没有定义:
- OMP_WAIT_POLICY=PASSIVE,那么自旋次数为 0 。
- 如果 OMP_WAIT_POLICY 也是未定义的话,那么这个自旋次数将会被设置成 300,000 。
- OMP_WAIT_POLICY=ACTIVE,那么自旋的次数是 300 亿次。
另外如果 OpenMP 的线程的个数大于可用的 CPU 的核心的个数的时候,1000 和 100 次就是 GOMP_SPINCOUNT 的值,对应OMP_WAIT_POLICY=ACTIVE 和 OMP_WAIT_POLICY 没有定义。
OMP_MAX_TASK_PRIORITY,这个是设置 OpenMP 任务的优先级的最大值,这个值应该是一个大雨等于 0 的值,如果没有定义,默认优先级的值就是 0 。
OMP_MAX_ACTIVE_LEVELS,这个参数的主要作用是设置最大的嵌套的并行域的个数。
GOMP_CPU_AFFINITY,这个环境变量的左右就是将线程绑定到特定的 CPU 核心上。该变量应包含以空格分隔或逗号分隔的CPU列表。此列表可能包含不同类型的条目:任意顺序的单个CPU编号、CPU范围(M-N)或具有一定步长的范围(M-N:S)。CPU编号从零开始。例如,GOMP_CPU_AFFINITY=“0 3 1-2 4-15:2”将分别将初始线程绑定到CPU 0,第二个绑定到CPU 3,第三个绑定到CPU1,第四个绑定到CPU 2,第五个绑定到CPU 4,第六个到第十个绑定到ccu 6、8、10、12和14,然后从列表的开头开始重新分配。GOMP_CPU_AFFINITY=0将所有线程绑定到CPU 0。
我们现在来使用一个例子查看环境变量的使用。我们的测试程序如下:
#include <stdio.h>
#include <omp.h>
int main()
{
omp_lock_t lock;
omp_init_lock(&lock);
#pragma omp parallel num_threads(4) default(none) shared(lock)
{
while (1);
}
return 0;
}
上面的程序就是开启四个线程然后进行死循环。在我的测试环境中一共有 4 个 CPU 计算核心。我们现在执行上面的程序,对应的结果如下所示,下面的图是使用命令 htop 得到的结果:
➜ tmp ./a.out
────────────────────────────────────────────────────────────────────────────────
0[||||||||||||||||||||||||100.0%] Tasks: 118, 212 thr; 4 running
1[||||||||||||||||||||||||100.0%] Load average: 2.62 0.86 0.29
2[||||||||||||||||||||||||100.0%] Uptime: 04:21:10
3[||||||||||||||||||||||||100.0%]
Mem[||||||||||||||||||||575M/3.82G]
Swp[ 0K/3.82G]
PID USER PRI NI VIRT RES SHR S CPU%▽MEM% TIME+ Command
10750 lehung 20 0 27304 852 756 R 400. 0.0 2:30.53 ./a.out
从上面 htop 命令的输出结果可以看到 0 - 3 四个核心都跑满了,我们现在在来看一下如果我们使用 GOMP_CPU_AFFINITY 环境变量使用线程绑定的方式 CPU 的负载将会是什么样!下面我们将所有的线程绑定到 0 1 两个核心,那么根据我们之前的分析 0 号核心上将会有第一个和第三个线程,1 号核心将会有第二个和第四个线程在上面运行。
➜ tmp export GOMP_CPU_AFFINITY="0 1"
➜ tmp ./a.out
────────────────────────────────────────────────────────────────────────────────
0[||||||||||||||||||||||||100.0%] Tasks: 118, 213 thr; 4 running
1[||||||||||||||||||||||||100.0%] Load average: 2.29 1.10 0.41
2[| 1.3%] Uptime: 04:22:03
3[| 0.7%]
Mem[||||||||||||||||||||576M/3.82G]
Swp[ 0K/3.82G]
PID USER PRI NI VIRT RES SHR S CPU%▽MEM% TIME+ Command
10772 lehung 20 0 27304 840 744 R 200. 0.0 0:10.42 ./a.out
其实与上面的过程相关的两个主要的系统调用就是:
int sched_setaffinity(pid_t pid, size_t cpusetsize,
const cpu_set_t *mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize,
cpu_set_t *mask);
感兴趣的同学可能查看一下上面的两个函数的手册。
- OMP_SCHEDULE,这个环境变量主要是用在 OpenMP 关于 for 循环的调度上的,他的规则为 OMP_SCHEDULE=type[,chunk],其中 type 的取之可以为 static, dynamic, guided, auto 。并且 chunk size 是可选的,而且他的值是一个正整数。如果这个环境变量没有定义,默认的调度方式是 dynamic 并且 chunk size = 1 。
总结
在本篇文章当中主要给大家介绍了一些经常使用的 OpenMP 系统环境变量,设置环境变量有时候能够更加方便的设置程序,同时有些环境变量对应一些 OpenMP 的动态库函数。以上就是本篇文章的所有内容希望大家有所收获!
更多精彩内容合集可访问项目:https://github.com/Chang-LeHung/CSCore
关注公众号:一无是处的研究僧,了解更多计算机(Java、Python、计算机系统基础、算法与数据结构)知识。
OpenMP 环境变量使用总结的更多相关文章
- Vs 2008 对 OpenMP 的 支持 以及 OpenMP的环境变量及库函数
Visual C++® 2008对OpenMP的支持 VC++2008根据项目属性配置的指示进行 /openmp编译器切换,当配置了OpenMP支持后,编译器会提供_OPENMP定义,可以使用#ifd ...
- linux centos中添加删除修改环境变量,设置java环境变量
前言 安装完软件必要添加环境变量.指令很少,然而长时间不写就会不自信:我写的对吗?于是百度开始,于是发现又是各有千秋.好吧,好记星不如烂笔头.当然,最重要的是,百度出来的都他妈的是如何添加环境变量,只 ...
- JAVA环境变量和TomCat服务器配置
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选.对于一个初学者来说,可以这样 ...
- 在Linux虚拟机下配置jdk的环境变量
1.到Oracle公司的官网里下载好jdk,网址 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133 ...
- Linux环境变量设置
修改环境变量PATH 最近为root添加一个环境变量发现sudo su进去没有变化所以总结了一下所有设置环境变量的方法: 查看PATH:echo $PATH 直接在命令行修改,就可以使用,但是只有在当 ...
- 再次思考 classpath 环境变量 等
f:\aspectj1.8\lib\aspectjrt.jar;.;%JAVA_HOME%\lib;C:\Temp\IBM\SQLLIB\java\db2java.zip;C:\Temp\IBM\SQ ...
- Java环境变量-Linux环境
环境变量说明 JAVA_HOME 它指向jdk的安装目录,Eclipse/NetBeans/Tomcat等软件就是通过搜索JAVA_HOME变量来找到并使用安装好的jdk. PATH 作用是指定命令搜 ...
- .NET Core采用的全新配置系统[5]: 聊聊默认支持的各种配置源[内存变量,环境变量和命令行参数]
较之传统通过App.config和Web.config这两个XML文件承载的配置系统,.NET Core采用的这个全新的配置模型的最大一个优势就是针对多种不同配置源的支持.我们可以将内存变量.命令行参 ...
- JDK环境变量配置说明
摘要:被人问到,为什么要配置Path/ClassPath JAVA_HOME,突然感觉有些讲不清楚."新人有资格问一个怪问题,但是老鸟不能给一个烂回答".所以今天为了让自己进一步学 ...
- Linux系统修改PATH环境变量方法
在Linux安装一些软件通常要添加路径环境变量PATH.PATH环境变量通俗的讲就是把程序的路径"备案"到系统中,这样执行这些程序时就不需要输入完整路径,直接在bash输入程序名就 ...
随机推荐
- 齐博x2新用户手工注册接口
由于手工注册有点太落后了,并不推荐,所以我们也没有单独的为API接口开发一个注册的页面,大家可以统一使用PC或WAP的注册页来当接口使用.请求地址是:http://qb.net/index.php/i ...
- 齐博x1一段不错的小js提高一点点阅读体验 计算本文阅读所需的时长
如图所示很多比较大的站点都有这样的一个小玩意 就是本文有多少字 阅读需要多少时间. 一段小小的js就可以实现,当然了php也可以功能太小了不值得做钩子或者插件自己需要的话再模板加一下吧. <sc ...
- 二、docker安装
一.docker安装 Docker 是管理容器的工具, Docker 不等于 容器. 1.1.docker yum源设置 #step 1 download docker-ce.repo file [r ...
- 使用WSL2连接USB设备
目录 要求 安装 第一步 在Window安装usbipd 第二步 在WSL里安装USBIP 工具和硬件数据库 使用方法 22.3.19 微软官方文档:连接 USB 设备 | Microsoft Doc ...
- Elasticsearch Analyzer 内置分词器
Elasticsearch Analyzer 内置分词器 篇主要介绍一下 Elasticsearch中 Analyzer 分词器的构成 和一些Es中内置的分词器 以及如何使用它们 前置知识 es 提供 ...
- miniconda使用
基本指令 conda create -n xxx python=3.7 // 创建Python3.7的名为xxx虚拟环境 conda env list // 显示所有的虚拟环境 conda activ ...
- javax.script.ScriptException: Cannot find engine named: 'nashorn', ensure you set language field in JSR223 Test Element: JSR223 预处理程序
jmeter运行脚本报错,跟java版本有关,做个记录. 1. 问题记录: 执行登录接口测试,登录失败.点击jmeter右上角[黄色!],查看错误日志.显示如下: 2022-09-23 10:29:5 ...
- zk系列三:zookeeper实战之分布式锁实现
一.分布式锁的通用实现思路 分布式锁的概念以及常规解决方案可以参考之前的博客:聊聊分布式锁的解决方案:今天我们先分析下分布式锁的实现思路: 首先,需要保证唯一性,即某一时点只能有一个线程访问某一资源: ...
- xamarin.Android自动升级
在写这边文章的时候参考了很多其他人的博客,在此感谢各位.也是给其他的伙伴做一个总结.对于我们新手来说存在的问题还是比较多的. 一.搭建iis服务器 首先我们需要搭建一个可以提供app下载最新版本的网站 ...
- 【Devexpress】Gridcontorl动态创建列不显示的问题
通过代码创建列但是不显示,这个原因是因为代码创建的列Visible属性默认是false 所以需要设置为true就会显示了 gridColumn.Visible = true;