IBM® XL 编译器使用硬件平台的特征来最大限度提升应用程序的性能。这些编译器还支持使用特定于性能的特性,帮助程序员优化和调优其应用程序。因为大多数现代计算机都包含多核硬件,所以并行化成为提高应用程序性能的常见方法之一。
OpenMP 应用程序编程接口 (API) 是一种行业标准,允许用户使用编译指示 (pragma) 和指令来标注其顺序代码,以利用并行性。该规范的基础语言是 Fortran、C 和 C++。此规范由 OpenMP 语言委员会开发,该委员会由供应商、用户和研究人员组成。有关更多信息,请参阅 OpenMP 网站。
回页首
面向 Linux on Power 小端发行版的 XL C/C++ 和 XL Fortran 编译器中的 OpenMP 支持
面向 Linux on Power 小端发行版的 IBM XL C/C++ V13.1.2 和 XL Fortran V15.1.2 支持 OpenMP API V3.1 并行编程规范,以及(在本文发表时)最新的 OpenMP API V4.0 中一些选定的特性。OpenMP 提供了一个简单且灵活的接口来执行并行应用程序开发。OpenMP 规范由 3 个部分组成:编译器指令/指示、运行时库函数和环境变量。符合 OpenMP 规范的应用程序可跨平台移植。此规范支持同时作为并行程序(多个执行线程和一个完整的 OpenMP 支持库)和顺序程序(指令/指示被忽略,链接了存根库)运行的应用程序。
OpenMP 并行化通过 –qsmp
编译器选项来启用。如果指定了 –qsmp=omp
,会对编译程序应用严格的 OpenMP 合规性。有关更多信息,请参阅 XML 编译器的编译器参考手册(在参考资料部分)。
回页首
OpenMP API V3.1 对现有特性进行了扩展,使用户能够调优其应用程序。这次修订还放宽了一些限制,以便更灵活地表达并行编程中的不同场景。
在早期的 OpenMP 规范版本中,并行性大部分是规则的,比如可确定迭代数量的循环并行性,或者独立单元数量固定的并行单元结构。任务并行性是在 V3.0 中引入的,用于支持不规则的并行性。 task
结构实现了不规则算法(比如指针跟踪或递归算法)的并行化。但是,当问题规模变得越来越小时,创建要计算的任务的成本可能变得比任务本身的计算大得多。我们引入了 final
和 mergeable
子句来控制任务是否需要立即执行,或者是否需要创建它的数据环境。
点击查看代码清单
关闭 [x]
面向 Linux on Power 小端发行版的 XL C/C++ 和 XL Fortran 编译器中的 OpenMP 支持
#pragma omp parallel { #pragma omp single { while (list) { #pragma omp task final(list->size < THRESHOLD) { compute(list->next); } } } }
示例 1 显示,任务生成后在一个并行区域中运行。如果大小小于 THRESHOLD
,生成的任务将是最终任务。这个最终任务将立即运行。这不需要任何调度成本。
void compute(struct S *p) { #pragma omp task final(level < DEPTH) mergeable { compute(p->next); } }
在示例 2 中,递归函数用于遍历要计算的列表。在它遍历到某个级别时,由于较小的计算规模,可能创建一个新任务不划算。指定 mergeable
子句是为了告诉编译器不为可合并的任务创建新数据环境,以减少任务生成成本。这两种新扩展有助于调优任务并行化性能,在计算规模变小时减少成本。
OMP_NUM_THREADS
环境变量指定并行区域中使用的线程数量。但是,如果有一个嵌套的并行区域,那么没有直接的方式来控制内部并行区域中使用的线程数量。如果程序员不小心,可能发生线程过度订阅,这会影响性能。在 OpenMP V3.1 中, OMP_NUM_THREADS
环境变量经过扩展,用来指定嵌套并行区域中使用的线程数量。这些值可由一个逗号分隔的列表来指定,例如 OMP_NUM_THREAD=8,4
。添加了一组查询例程,例如嵌套并行区域的级别 ( omp_get_level
)、祖先线程编号 ( omp_get_ancestor_thread_num
)、给定级别的团队大小 ( omp_get_team_size
) 和嵌套的活动并行区域的级别 ( omp_get_active_level
)。还包含用于获取和设置活动级别最大数量的运行时例程( omp_set_max_active_levels
和 omp_get_max_active_levels
)。
点击查看代码清单
关闭 [x]
面向 Linux on Power 小端发行版的 XL C/C++ 和 XL Fortran 编译器中的 OpenMP 支持
#include <omp.h> int main() { #pragma omp parallel { if (omp_get_thread_num()==0) printf("outer parallel:%d/n", omp_get_num_threads()); #pragma omp parallel { if (omp_get_thread_num()==0) printf("inner parallel:%d/n", omp_get_num_threads()); } } }
使用 XL C/C++ 编译器编译示例 3,会获得示例 4 中所示的输出。
点击查看代码清单
关闭 [x]
面向 Linux on Power 小端发行版的 XL C/C++ 和 XL Fortran 编译器中的 OpenMP 支持
$ xlc –qsmp nested_par.c –o nested_par $ export OMP_NESTED=true $ export OMP_NUM_THREADS=4,2 $ ./nested_par outer parallel:4 inner parallel:2 inner parallel:2 inner parallel:2 inner parallel:2
根据 OMP_NUM_THREADS
环境变量中指定的 “ 4,2
”,外部并行区域由 4 个线程运行,内部区域由 2 个线程运行。
此外,面向 Linux on Power 小端发行版的 XL C/C++ V13.1.2 和 XL Fortran V15.1.2 编译器支持 OpenMP 样式的嵌套并行性,该并行性可通过将 OMP_NESTED
环境变量设置为 true
来启用。默认情况下,嵌套并行性是禁用的。此设置适用于整个程序。程序员可调用 omp_set_nested
运行时例程来选择性地在代码中的某些并行区域上启用嵌套并行性。
借助此特性,可基于运行环境轻松调整应用程序,避免创建比期望的更多的线程,进而影响性能。
atomic
结构现在包含更多原子操作。添加了 read
、 write
和 capture
子句来分别支持读、写和捕获操作。现有的原子更新表也可使用 update
子句来表达。
请参阅示例 5 了解一些原子结构。
点击查看代码清单
关闭 [x]
面向 Linux on Power 小端发行版的 XL C/C++ 和 XL Fortran 编译器中的 OpenMP 支持
! atomic read of variable x !$omp atomic read v = x !$omp end atomic ! atomic write of variable x !$omp atomic write x = y !$omp end atomic ! atomic capture: pre-update value of x is captured and then updated !$omp atomic capture v = x x = x + 1 !$omp end atomic
一些应用程序可能需要专门的资源来实现需要的性能。将线程从一个处理器迁移到另一个处理器,可能导致意外的性能影响。我们引入了 OMP_PROC_BIND
环境变量来允许用户启用或禁用运行环境在处理器之间转移 OpenMP 线程。
IBM XL 编译器拥有 XLSMPOPTS
环境变量的 startproc/stride
或 procs
子选项,以允许更精细地控制 OpenMP 线程如何绑定到处理器。此特性是一个 IBM 扩展。如果您应用程序的可移植性很重要,可仅使用 OMP_PROC_BIND
环境变量来控制线程绑定。
一些增强有助于在各种不同场景中更灵活地表达并行性。
在 Fortran 中,允许将 intent(in)
虚拟参数用在 firstprivate
子句上。这可避免在传递到并行结构之前,在过程中创建临时变量。现在允许在 firstprivate
和 lastprivate
子句中指定 Fortran 指针。
在 C/C++ 中,添加了缩减运算符 min
和 max
来执行相应的运算。
回页首
面向 Linux on Power 小端发行版的 XL C/C++ V13.1.2 和 XL Fortran V15.1.2 添加了一些 OpenMP V4.0 特性 – 原子结构扩展和 OMP_DISPLAY_ENV 环境变量。
原子交换可使用 atomic
结构和 capture
子句来表达。下面的示例演示了执行原子交换的原子结构,其中捕获了 x
的原始值并进行了更新。
#pragma omp atomic capture { v = x; x = y; }
此外,原子捕获结构支持更多表达式格式。这使用户能够灵活地表达他们的代码。
面向 Linux on Power 小端发行版的 XL C/C++ V13.1.2 和 XL Fortran V15.1.2 中的 OpenMP 原子操作已重新实现,以提高性能。旧实现使用锁来确保独占访问一个内存位置。新实现使用了 IBM PowerPC® 架构上提供的未使用锁机制的硬件指令。因此,原子操作更高效,可提高拥有这些操作的应用程序的总体性能。
这些实现默认设置了 OpenMP 内部控制变量 (ICV)。这些 ICV 可通过在源代码中设置环境变量或调用运行时例程来修改。尽管可以调用相应的运行时例程来查询 ICV 的设置,但此过程涉及到将调用插入到源代码中并重新编译应用程序。此过程可能很耗时。OpenMP V4.0 添加了 OMP_DISPLAY_ENV
环境变量来让 OpenMP 运行时显示 ICV 设置。此特性可帮助程序员检查运行时设置来调试代码。程序员也可使用此特性来在链接时(如果运行时库是静态链接的)或运行时(如果它是动态链接的)检查所使用的运行时库的版本。另一个场景是,由于新平台上不同的默认设置,移植代码可能导致一些意外的行为。此特性也有助于程序员快速比较设置,识别差异,并相应地进行调整。
点击查看代码清单
关闭 [x]
面向 Linux on Power 小端发行版的 XL C/C++ 和 XL Fortran 编译器中的 OpenMP 支持
$ export OMP_DISPLAY_ENV=true $ ./a.out OPENMP DISPLAY ENVIRONMENT BEGIN OMP_DISPLAY_ENV='TRUE' _OPENMP='201107' OMP_DYNAMIC='FALSE' OMP_MAX_ACTIVE_LEVELS='5' OMP_NESTED='FALSE' OMP_NUM_THREADS='96' OMP_PROC_BIND='FALSE' OMP_SCHEDULE='STATIC,0' OMP_STACKSIZE='4194304' OMP_THREAD_LIMIT='96' OMP_WAIT_POLICY='PASSIVE' OPENMP DISPLAY ENVIRONMENT END
在示例 7 中, OMP_DISPLAY_ENV
变量设置为 true
,OpenMP 运行时显示拥有相应环境变量的 ICV 的所有默认设置。如果环境变量设置为 verbose
,显示中会包含更多特定于供应商的设置。
示例 8:使用 OMP_DISPLAY_ENV 环境变量显示运行时设置,包括特定于供应商的信息
点击查看代码清单
关闭 [x]
面向 Linux on Power 小端发行版的 XL C/C++ 和 XL Fortran 编译器中的 OpenMP 支持
$ export OMP_DISPLAY_ENV=verbose $ ./a.out OPENMP DISPLAY SWITCHES BEGIN LOMP_AUTO_PASSIVE_HALF_THREAD='1' LOMP_CACHE_LINE_SIZE='256' LOMP_CHECK_STACKS='1' LOMP_CLEANUP_ON_PROCESS_EXIT='0' LOMP_CLEANUP_TO_FORCE_RESCAN='0' LOMP_COUNTER_BARRIER='0' LOMP_DEBUG='0' LOMP_DEFAULT_DELAY='1000' LOMP_DEFAULT_SPIN='64' LOMP_DEFAULT_YIELD='64' LOMP_ENABLE_INLINING='1' LOMP_ENABLE_WAIT_PASSIVE_BARRIER='0' LOMP_ENABLE_WAIT_PASSIVE_WORKER='1' LOMP_FUSSY_INIT='0' LOMP_GUIDED_SHARED='1' LOMP_ILDE_THREAD_EXIT='0' LOMP_XL_LEGACY='0' LOMP_G_LEGACY='0' LOMP_AUTOPAR_LEGACY='1' LOMP_LOOP_CACHE='0' LOMP_MASTER_BARRIER_MSYNC='0' LOMP_MAX_THREAD='65535' LOMP_PARALEL_DISABLE_FAST_PATH='0' LOMP_PROC_BIND_40='1' LOMP_PROC_BIND_WHEN_OFF='0' LOMP_PROC_BIND_WHEN_ON='1' LOMP_SEQENTIAL_FAST='1' LOMP_TASK_DISABLE_STEAL='0' LOMP_TEST='1' LOMP_WAIT_LOW_PRIO='1' LOMP_WAIT_WITH_YIELD='1' OMPT_TIER='0' LOMP_TARGET_PPC='0' LOMP_TARGET_CUDA='0' LOMP_ARCH_POWER='8' LOMP_BARRIER_SWMR_DEGREE='2' LOMP_BARRIER_WITH_IO_SYNC='1' OPENMP DISPLAY SWITCHES END OPENMP DISPLAY RUNTIME BEGIN LOMP_VERSION='0.35 for OpenMP 3.1' BUILD_LEVEL='OpenMP Runtime Version:13.1.2(C/C++) and 15.1.2(Fortran) Level:150417 ID:_v1mpguSSEeSbzZ-i2Itj4A' TARGET='Linux, 64 bit LE' OPENMP DISPLAY RUNTIME END OPENMP DISPLAY ENVIRONMENT BEGIN OMP_DISPLAY_ENV='VERBOSE' _OPENMP='201107' OMP_DYNAMIC='FALSE' OMP_MAX_ACTIVE_LEVELS='5' OMP_NESTED='FALSE' OMP_NUM_THREADS='96' OMP_PROC_BIND='FALSE' OMP_SCHEDULE='STATIC,0' OMP_STACKSIZE='4194304' OMP_THREAD_LIMIT='96' OMP_WAIT_POLICY='PASSIVE' XLSMPOPTS=' DELAYS=1000' XLSMPOPTS=' NOSTACKCHECK' XLSMPOPTS=' PARTHDS=96' XLSMPOPTS=' PARTHRESHOLD= inf' XLSMPOPTS=' PROFILEFREQ=16' XLSMPOPTS=' SCHEDULE=STATIC=0' XLSMPOPTS=' SEQTHRESHOLD= inf' XLSMPOPTS=' SPINS=64' XLSMPOPTS=' STACK=4194304' XLSMPOPTS=' USRTHDS=0' XLSMPOPTS=' YIELDS=64' OPENMP DISPLAY ENVIRONMENT END
示例 8 显示了来自 XL 编译器运行时的输出。第一部分显示了 OpenMP 运行时中的内部设置。第二部分包含特定于构建版本的信息。如果您的系统包含多个 OpenMP 运行时版本,此信息有助于识别应用程序在哪个运行时版本中链接。第三部分是 ICV 设置和 IBM 扩展设置。
回页首
XL 编译器中的 OpenMP API 支持,为程序员提供了一种通过在其 C、C++ 或 Fortran 程序中标注编译指示或指令来并行化其顺序应用程序的途径。使用 XL 编译器来并行化应用程序时可利用多核硬件,例如 IBM POWER8™。面向 Linux on Power 小端发行版的 XL C/C++ V13.1.2 和 XL Fortran V15.1.2 中添加的一些 OpenMP V4.0 特性,为用户提供了更多途径来表达原子操作,帮助在调试或移植代码到不同平台时向用户传达运行时设置。
回页首