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 环境变量使用总结的更多相关文章

  1. Vs 2008 对 OpenMP 的 支持 以及 OpenMP的环境变量及库函数

    Visual C++® 2008对OpenMP的支持 VC++2008根据项目属性配置的指示进行 /openmp编译器切换,当配置了OpenMP支持后,编译器会提供_OPENMP定义,可以使用#ifd ...

  2. linux centos中添加删除修改环境变量,设置java环境变量

    前言 安装完软件必要添加环境变量.指令很少,然而长时间不写就会不自信:我写的对吗?于是百度开始,于是发现又是各有千秋.好吧,好记星不如烂笔头.当然,最重要的是,百度出来的都他妈的是如何添加环境变量,只 ...

  3. JAVA环境变量和TomCat服务器配置

    Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选.对于一个初学者来说,可以这样 ...

  4. 在Linux虚拟机下配置jdk的环境变量

    1.到Oracle公司的官网里下载好jdk,网址 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133 ...

  5. Linux环境变量设置

    修改环境变量PATH 最近为root添加一个环境变量发现sudo su进去没有变化所以总结了一下所有设置环境变量的方法: 查看PATH:echo $PATH 直接在命令行修改,就可以使用,但是只有在当 ...

  6. 再次思考 classpath 环境变量 等

    f:\aspectj1.8\lib\aspectjrt.jar;.;%JAVA_HOME%\lib;C:\Temp\IBM\SQLLIB\java\db2java.zip;C:\Temp\IBM\SQ ...

  7. Java环境变量-Linux环境

    环境变量说明 JAVA_HOME 它指向jdk的安装目录,Eclipse/NetBeans/Tomcat等软件就是通过搜索JAVA_HOME变量来找到并使用安装好的jdk. PATH 作用是指定命令搜 ...

  8. .NET Core采用的全新配置系统[5]: 聊聊默认支持的各种配置源[内存变量,环境变量和命令行参数]

    较之传统通过App.config和Web.config这两个XML文件承载的配置系统,.NET Core采用的这个全新的配置模型的最大一个优势就是针对多种不同配置源的支持.我们可以将内存变量.命令行参 ...

  9. JDK环境变量配置说明

    摘要:被人问到,为什么要配置Path/ClassPath JAVA_HOME,突然感觉有些讲不清楚."新人有资格问一个怪问题,但是老鸟不能给一个烂回答".所以今天为了让自己进一步学 ...

  10. Linux系统修改PATH环境变量方法

    在Linux安装一些软件通常要添加路径环境变量PATH.PATH环境变量通俗的讲就是把程序的路径"备案"到系统中,这样执行这些程序时就不需要输入完整路径,直接在bash输入程序名就 ...

随机推荐

  1. CentOS下一些软件的安装

    Git # 参数 -y 表示yes,不用询问直接安装 yum -y install git # 第一次安装需要一些配置 git config --global user.name "Your ...

  2. Go语言核心36讲33

    我们在前几次讲的互斥锁.条件变量和原子操作都是最基本重要的同步工具.在Go语言中,除了通道之外,它们也算是最为常用的并发安全工具了. 说到通道,不知道你想过没有,之前在一些场合下里,我们使用通道的方式 ...

  3. windows每日定时计划任务

    若要计划安全脚本 ,Sec.vbs,每天在下午 5:00 到上午 7:59 之间每隔 100 分钟在本地计算机上运行一次,请键入: schtasks /create /tn Security Scri ...

  4. layui文件上传+ThinkPHP

    1.前端html代码 <div class="layui-form-item"> <label class="layui-form-label" ...

  5. 【Java进阶】五分钟快速掌握JVM优化概念、常用命令、工具、JUC、多线程、GC等知识

    〇.概述 (一)资料 史上最全最详细的JVM优化方案:http://www.360doc.com/content/22/0513/10/34195792_1031121509.shtml (二)内容概 ...

  6. UIAutomator测试框架介绍

    uiautomator简介 UiAutomator是Google提供的用来做安卓自动化测试的一个Java库,基于Accessibility服务.功能很强,可以对第三方App进行测试,获取屏幕上任意一个 ...

  7. 个人电脑公网IPv6配置

    一.前言 自己当时以低价买的阿里ECS云服务器马上要过期了,对于搭建个人博客.NAS这样服务器的需求购买ECS服务器成本太高了,刚好家里有台小型的桌面式笔记本,考虑用作服务器,但是公网IPv4的地址实 ...

  8. IDEA引入本地jar包的几种方法

    有时候,项目需要引入一些第三方的依赖,这时候,就需要导入这些jar包.以下分享两种方式: 方式一.使用IDEA程序引入jar包 1.首先,点他! 2.然后,点他! 3.再然后,点他! 4.最后,在这里 ...

  9. 一文速览 Dubbo 3.0

    本文将带你快速了解 Dubbo3 的设计背景.总体架构与核心特性.与典型用户如阿里巴巴 HSF2 的关系等.也可以通过如下部分了解更多: 小白用户,快速浏览 Dubbo3 核心特性: 下一代通信协议 ...

  10. Django TypeError at /login/ 'bool' object is not callable

    代码: def login(request): if request.POST: username = request.POST.get('username') password = request. ...