OpenMP 是由计算机硬件和软件厂商共同制定的一组 面向共享内存多线程并行接口具体并行实现由编译器负责。同一套OpenMP程序代码,可运行在 WindowsMacOSLinux 等多个操作系统及硬件平台,具有良好的可移植性。

OpenMP程序的性能及行为与环境变量密切相关,本文简要介绍与性能相关的几个环境变量。

OpenMP环境变量

OMP_NUM_THREADS

OMP_NUM_THREADS 应该是最常用的环境变量了,其设置parallel区域的初始线程数,大多数OpenMP实现的默认值是可用逻辑CPU数量(可能有上限,例如32)。现代CPU一般开启了超线程技术,为了最大性能,建议将值设置为不超过物理CPU数。

具体parallel区域的执行线程数,除了该环境变量控制,还与 omp_set_num_threads 设置的值、 制导语句的 num_threads 值、OMP_DYNAMIComp_set_dynamic 等有关系。

使用示例:

# 默认使用4个线程
OMP_NUM_THREADS=4

# 第一层默认4个线程,第二层2个,第三层1个
OMP_NUM_THREADS=4,2,1

OMP_SCHEDULE

OMP_SCHEDULE 用来指定循环的调度方式,默认值一般是 static 或者 dynamic,其它可用值包括guidedauto/runtime,其中auto/runtime不是真实调度方式。staticdynamicguided的区别为:

  • static: 将循环划分为若干个大致相等的块,并将每个块分配给一个线程,适用于循环迭代次数可以预先确定的情况。需要注意该调度方式也允许指定块大小 chunk_size推荐用于每个循环计算量基本相同的场景,开销最小
  • dynamic:动态地将循环划分为若干块,并将块分配给不同的线程,适用于循环迭代次数不能预先确定,或者每个任务的计算量差异较大的情况,能实现更好的负载均衡;和 static 不同,任务执行的线程和顺序都是不确定的;
  • guided:和 dynamic 类似,但开始区间很大,然后逐渐减少执行区间,直到块大小小于或者等于指定的 chunk_size,和 dynamic 相比运行时开销更大;

使用示例

OMP_SCHEDULE=static

OMP_SCHEDULE=dynamic

OMP_SCHEDULE="dynamic,4"

OMP_SCHEDULE="guided,3"

OMP_DYNAMIC

OMP_DYNAMIC 设置是否允许动态调整线程数量。当值为 true 时,运行时库会根据系统资源状况动态调整线程数目;当值为 false 时,动态调整被关闭。除了该环境变量,还可以通过 omp_set_dynamic 函数设置是否允许动态调整线程数。

使用示例:

OMP_DYNAMIC=true

OMP_NESTED

OMP_NESTED 设置是否允许嵌套并行。当值为 true 时,嵌套并行性允许;当值为 false 时,嵌套的并行块被当作串行部分执行,大多数实现的缺省值为 false。

使用示例:

OMP_NESTED=true

OMP_PROC_BIND

OMP_PROC_BIND 用于指定线程的绑定策略,在OpenMP 4.0之前值为true或者false,表示是否将CPU与线程绑定。OpenMP 4.0开始,OMP_PROC_BIND可用的值还有close、master和spread的组合。其中 close 表示线程应该尽可能靠近,master表示线程尽可能靠近主线程,spread则是线程尽可能分开。

使用示例:

OMP_PROC_BIND=spread

OMP_PROC_BIND="spread, spread, close"

OMP_PLACES

OMP_PLACES 是OpenMP 4.0新增的环境变量,常和 OMP_PROC_BIND 组合使用指定线程绑定策略。OMP_PLACES 指定线程可用的CPU资源,取值可以比较复杂,但是提供了三个简单的名称:

  • threads:每个位置对应主机上的物理线程,开启超线程的情况下,threads表示虚拟线程;
  • cores:每个位置对应主机上的一个CPU核心;
  • sockets:每个位置对应主机上的一块CPU,一般个人电脑上只有一块CPU,服务器/工作站有多块CPU;

如果可用的位置不够,具体的线程绑定与实现有关。

使用示例:

OMP_PLACES=threads

OMP_WAIT_POLICY

OMP_WAIT_POLICY 用于指定线程的默认行为,可用的值有 activepassive,大多数实现默认值是 passive。当值为 active 时,即使线程没有分配任务,也会通过自旋锁等方式消耗CPU。

使用示例:

OMP_WAIT_POLICY=active

OMP_DISPLAY_ENV

OMP_DISPLAY_ENV 是OpenMP 4.0新加进来的环境变量,用来打印OpenMP环境变量的初始值,主要用来调试,默认值是false。如果想查看环境变量是否设置正确,可将值设置为true,会打印类似如下信息:

OPENMP DISPLAY ENVIRONMENT BEGIN
_OPENMP = '201511'
OMP_DYNAMIC = 'TRUE'
OMP_NESTED = 'FALSE'
OMP_NUM_THREADS = '4'
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 = 'FALSE'
OMP_DEFAULT_DEVICE = '0'
OMP_MAX_TASK_PRIORITY = '0'
OPENMP DISPLAY ENVIRONMENT END

主流编译器对OpenMP标准支持

由于OpenMP的并行实现由编译器完成,因此使用的编译器是否支持相应标准非常重要,对于不支持的特性语句,编译器一般是直接忽略。这里参考官网简要介绍主流编译器对OpenMP标准支持程度,数据更新于2022年11月。

  • GCC:GCC 4.9版本开始完全支持OpenMP 4.0标准,GCC 6对C/C++完全支持OpenMP 4.5标准,GCC 9对C/C++开始支持OpenMP 5.0,GCC 11对Fortran完全支持OpenMP 4.5,对C/C++开始支持OpenMP 5.0, GCC 12开始支持OpenMP 5.1, GCC 13开始支持OpenMP 5.2;
  • Intel编译器:12.0完全支持OpenMP 3.1,15.0开始支持OpenMP 4.0,17.0开始支持OpenMP 4.5, 2021.1开始支持OpenMP 5.1
  • LLVM/CLang:Clang 3.9开始支持OpenMP 4.5,Clang 8.0支持offload到GPU,Clang 11.0开始默认启用OpenMP 5.0标准,同时部分OpenMP 5.1特性;
  • MSVC:最拉垮的,完全支持的只有OpenMP 2.0标准,VS 2022 17.2开始完全支持OpenMP 2.5标准以及部分OpenMP 3.1标准;OpenMP中的SIMD特性需要加 -openmp:experimental 才能开启
  • NVCC:完全支持OpenMP 3.1,部分支持OpenMP 4.0、4.5、5.0、5.1等标准。

注意事项

1. 当环境变量的值是非法值时,其实际值由实现决定;

2. 环境变量的值不区分大小写,并且和顺序无关;

3. 性能建议:请根据作业的具体情况设置默认调度方式,强烈建议设置 OMP_PLACESOMP_PROC_BIND进行线程绑定。

参考

1. OpenMP官网

2. 一些常规性能建议