本章说明 OpenMP 2.5 规范中依赖实现的特定行为。有关与最新编译器发行版有关的最新信息,请参见 Sun Developer Network 门户网站中的编译器文档,网址为:http://developers.sun.com/sunstudio。
内存模型
当多个线程异步访问同一变量时,无法保证这些线程执行的内存访问互为原子操作。
一些依赖实现的因素和依赖应用程序的因素会对访问是否为原子操作产生影响。某些变量占用的内存空间可能比目标平台上最大的原子内存操作所占用的空间大。某些变量的存储方式可能是未对齐的或者其对齐方式是未知的,因此编译器或运行时系统可能需要使用多个 load/store 操作来访问变量。有时,使用多个 load/store 操作会让代码序列的运行速度更快。
内部控制变量
OpenMP 运行时库维护以下内部控制变量:
nthreads-var-存储为以后的并行区域请求的线程数。
dyn-var-控制是否为以后的并行区域要使用的线程数启用动态调整。
nest-var-控制是否为以后的并行区域启用嵌套并行操作。
run-sched-var-使用 RUNTIME 调度子句存储要用于循环区域的调度信息。
def-sched-var-为循环区域存储实现定义的缺省调度信息。
运行时库为每个线程维护每个 nthreads-var、dyn-var 和 nest-var 的单独副本。另一方面,运行时库还维护应用于所有线程的每个 run-sched-var 和 def-sched-var 的一个副本。
nthreads-var 的缺省值为 1。也就是说,如果没有显式 num_threads() 子句、对 omp_set_num_threads() 例程的调用或 OMP_NUM_THREADS 环境变量的显式定义,则组内的缺省线程数为 1。
调用 omp_set_num_threads() 仅修改调用线程的 nthreads-var 值,并应用于该调用线程遇到的同一嵌套级别或内部嵌套级别的并行区域。
如果所请求的线程数大于实现可支持的线程数,或者该值不是正整数,则在将 SUNW_MP_WARN 设置为 TRUE 时或通过调用 sunw_mp_register_warn() 注册回调函数时,将发出一条警告消息。
支持嵌套并行操作。可以由多个线程来执行嵌套并行区域。
nest-var 的缺省值为 False。即,缺省情况下禁用嵌套并行操作。要启用它,请设置 OMP_NESTED 环境变量或调用 omp_set_nested() 例程。
调用 omp_set_nested() 仅修改调用线程的 nest-var 值,并应用于该调用线程遇到的同一嵌套级别或内部嵌套级别的并行区域。
缺省情况下,支持的最大活动嵌套级别数为 4。可通过设置环境变量 SUNW_MP_MAX_NESTED_LEVELS 更改该最大值。
dyn-var 的缺省值为 True。即,缺省情况下启用动态调整。要禁用动态调整,请设置 OMP_DYNAMIC 环境变量或调用 omp_set_dynamic() 例程。
调用 omp_set_dynamic() 仅修改调用线程的 dyn-var 值,并应用于该调用线程遇到的同一嵌套级别或内部嵌套级别的并行区域。
如果启用了动态调整,则会将组内的线程数调整为以下值中的最小值:
用户请求的线程数
1 + 池中可用线程数
可用虚拟处理器数
另一方面,如果禁用了动态调整,则组内的线程数为以下值中的最小值:
用户请求的线程数
1 + 池中可用线程数
在异常情况下(如缺少系统资源),提供的线程数将少于上述值。在这些情况下,如果将 SUNW_MP_WARN 设置为 TRUE,或通过调用 sunw_mp_register_warn() 注册回调函数,则会发出一条警告消息。
有关线程池和嵌套并行操作执行模型的更多信息,请参阅第 2 章。
循环调度
def-sched-var 的缺省值为 STATIC 调度。要为循环区域指定其他调度,请使用 SCHEDULE 子句。
run-sched-var 的缺省值也是 STATIC 调度。可通过设置 OMP_SCHEDULE 环境变量更改该缺省值。
如果未指定 chunksize,则 SCHEDULE(GUIDED) 的缺省块大小为 1。OpenMP 运行时库通过以下公式来计算使用 GUIDED 调度的循环的块大小: chunksize = unassigned_iterations / (weight * num_threads) 其中:unassigned_iterations 是循环中尚未分配给任何线程的迭代数;weight 是可由用户在运行时使用 SUNW_MP_GUIDED_WEIGHT 环境变量(2.3 OpenMP 环境变量)指定的浮点常量。如果未指定,则当前缺省设置假定 weight 为 2.0;num_threads 是用于执行循环的线程数。weight 值的选择会影响分配给循环中线程的迭代的初始块和后续块的大小,并且直接影响负载平衡。实验结果表明,weight 采用缺省值 2.0 通常效果比较好。不过,使用其他 weight 值可能会令某些应用程序受益。
使用 POSIX 或 Solaris 线程显式线程化的程序可以包含 OpenMP 指令或调用包含 OpenMP 指令的例程。
通过设置 SUNW_MP_WARN 环境变量(2.3 OpenMP 环境变量),可启用由 OpenMP 运行时库执行的运行时有效性检查。
例如,由于线程在不同的屏障处等待,以下代码会陷入无限循环,必须从终端使用 Control-C 来终止代码执行:
% cat bad1.c #include <omp.h> #include <stdio.h> int main(void) { omp_set_dynamic(0); omp_set_num_threads(4); #pragma omp parallel { int i = omp_get_thread_num(); if (i % 2) { printf("At barrier 1.\n"); #pragma omp barrier } } return 0; } % cc -xopenmp -xO3 bad1.c % ./a.out 运行程序 At barrier 1. At barrier 1. 程序以无限循环方式挂起 Control-C 以终止执行 |
但是,如果在执行前设置了 SUNW_MP_WARN,则运行时库会检测到该问题:
% setenv SUNW_MP_WARN TRUE % ./a.out WARNING (libmtsk): Environment variable SUNW_MP_WARN is set to TRUE. Runtime error checking will be enabled. At barrier 1. At barrier 1. WARNING (libmtsk): Threads at barrier from different directives. Thread at barrier from bad1.c:8. Thread at barrier from bad1.c:13. Possible Reasons: Worksharing constructs not encountered by all threads in the team in the same order. Incorrect placement of barrier directives. WARNING (libmtsk): Runtime shutting down while some parallel region is still active. |
C 和 C++ 编译器还提供了一个函数,可用于在检测到错误时注册回调函数。当检测到错误时,将调用已注册的回调函数,并将指向错误消息字符串的指针作为参数传递给它。
int sunw_mp_register_warn(void (*func) (void *) )
要访问此函数的原型,需要添加 #include <sunw_mp_misc.h>
例如:
% cat bad2.c #include <omp.h> #include <sunw_mp_misc.h> #include <stdio.h> void handle_warn(void *msg) { printf("handle_warn: %s\n", (char *)msg); } void set(int i) { static int k; #pragma omp critical { k++; } #pragma omp barrier } int main(void) { int i, rc; omp_set_dynamic(0); omp_set_num_threads(4); if (sunw_mp_register_warn(handle_warn) != 0) { printf ("Installing callback failed\n"); } #pragma omp parallel for for (i = 0; i < 20; i++) { set(i); } return 0; } % cc -xopenmp -xO3 bad2.c % a.out WARNING (libmtsk): Environment variable SUNW_MP_WARN is set to TRUE. Runtime error checking will be enabled. handle_warn: WARNING (libmtsk): at bad2.c:15. BARRIER is not permitted in the dynamic extent of FOR / DO. |
如果 OpenMP 运行时库检测到错误,则将 handle_warn() 安装为回调函数。此示例中的回调函数只打印从库传递给它的错误消息,但可用于捕获特定错误。
关于特定构造:
sections 构造
sections 构造中的结构化块在执行段区域的组成员中分配,从而使这些线程执行的段数大致相等。
single 构造
single 构造的结构化块将由首先遇到单个区域的线程执行。
atomic 构造
此实现通过在 CRITICAL 构造中封闭目标语句来替换所有 ATOMIC 指令和 pragma。
OpenMP 库例程的绑定线程集:
omp_set_num_threads 例程
当从显式并行区域内调用时,omp_set_num_threads 区域的绑定线程集为调用线程。
omp_get_max_threads 例程
当从显式并行区域内调用时,omp_get_max_threads 区域的绑定线程集为调用线程。
omp_set_dynamic 例程
当从任何显式并行区域内调用时,omp_set_dynamic 区域的绑定线程集仅为调用线程。
omp_get_dynamic 例程
当从显式并行区域内调用时,omp_get_dynamic 区域的绑定线程集仅为调用线程。
omp_set_nested 例程
当从显式并行区域内调用时,omp_set_nested 区域的绑定线程集仅为调用线程。
omp_get_nested 例程
当从显式并行区域内调用时,omp_get_nested 区域的绑定线程集仅为调用线程。
Fortran 95 特定的问题:
threadprivate 指令
如果要在两个连续的活动并行区域之间保持的线程(初始线程除外)的 threadprivate 对象中的数据值条件不能全部成立,则第二个区域中的可分配数组的分配状态可能为“当前未分配”。
shared 子句
如果将共享变量传递到非内在过程,可能导致该共享变量的值在过程引用之前被复制到临时存储中,并在过程引用之后又从临时存储中复制到实际参数存储中。仅当 OpenMP 2.5 规范的 2.8.3.2 节中的条件 a、b 和 c 同时成立时,才会发生这种向临时存储复制数据以及从临时存储向外复制数据的情况。
包含文件和模块文件
此实现中同时提供了包含文件 omp_lib.h 和模块文件 omp_lib。
在 Solaris 中,采用参数的 OpenMP 运行时库例程是通过通用接口扩展的,因此可以适应不同 Fortran KIND 类型的参数。