内容


IBM XL 编译器中的 OpenMP 支持

Comments

简介

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 V3.1 中的新特性

OpenMP API V3.1 对现有特性进行了扩展,使用户能够调优其应用程序。这次修订还放宽了一些限制,以便更灵活地表达并行编程中的不同场景。

任务并行性扩展

在早期的 OpenMP 规范版本中,并行性大部分是规则的,比如可确定迭代数量的循环并行性,或者独立单元数量固定的并行单元结构。任务并行性是在 V3.0 中引入的,用于支持不规则的并行性。task 结构实现了不规则算法(比如指针跟踪或递归算法)的并行化。但是,当问题规模变得越来越小时,创建要计算的任务的成本可能变得比任务本身的计算大得多。我们引入了 finalmergeable 子句来控制任务是否需要立即执行,或者是否需要创建它的数据环境。

示例 1:生成最终任务

#pragma omp parallel { #pragma omp single { while (list) { #pragma omp task final(list->size < THRESHOLD) { compute(list->next); } } } }

示例 1 显示,任务生成后在一个并行区域中运行。如果大小小于 THRESHOLD,生成的任务将是最终任务。这个最终任务将立即运行。这不需要任何调度成本。

示例 2:生成可合并任务

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_levelsomp_get_max_active_levels)。

示例 3:嵌套并行结构

#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 中所示的输出。

示例 4:嵌套并行程序的编译和输出

$ 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 结构现在包含更多原子操作。添加了 readwritecapture 子句来分别支持读、写和捕获操作。现有的原子更新表也可使用 update 子句来表达。

请参阅示例 5 了解一些原子结构。

示例 5: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/strideprocs 子选项,以允许更精细地控制 OpenMP 线程如何绑定到处理器。此特性是一个 IBM 扩展。如果您应用程序的可移植性很重要,可仅使用 OMP_PROC_BIND 环境变量来控制线程绑定。

其他增强

一些增强有助于在各种不同场景中更灵活地表达并行性。

在 Fortran 中,允许将 intent(in) 虚拟参数用在 firstprivate 子句上。这可避免在传递到并行结构之前,在过程中创建临时变量。现在允许在 firstprivatelastprivate 子句中指定 Fortran 指针。

在 C/C++ 中,添加了缩减运算符 minmax 来执行相应的运算。

精选的 OpenMP V4.0 特性和其他增强

面向 Linux on Power 小端发行版的 XL C/C++ V13.1.2 和 XL Fortran V15.1.2 添加了一些 OpenMP V4.0 特性 – 原子结构扩展和 OMP_DISPLAY_ENV 环境变量。

原子结构扩展

原子交换可使用 atomic 结构和 capture 子句来表达。下面的示例演示了执行原子交换的原子结构,其中捕获了 x 的原始值并进行了更新。

示例 6:一个原子交换操作

#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 运行时设置

这些实现默认设置了 OpenMP 内部控制变量 (ICV)。这些 ICV 可通过在源代码中设置环境变量或调用运行时例程来修改。尽管可以调用相应的运行时例程来查询 ICV 的设置,但此过程涉及到将调用插入到源代码中并重新编译应用程序。此过程可能很耗时。OpenMP V4.0 添加了 OMP_DISPLAY_ENV 环境变量来让 OpenMP 运行时显示 ICV 设置。此特性可帮助程序员检查运行时设置来调试代码。程序员也可使用此特性来在链接时(如果运行时库是静态链接的)或运行时(如果它是动态链接的)检查所使用的运行时库的版本。另一个场景是,由于新平台上不同的默认设置,移植代码可能导致一些意外的行为。此特性也有助于程序员快速比较设置,识别差异,并相应地进行调整。

示例 7:使用 OMP_DISPLAY_ENV 环境变量来显示运行时设置

$ 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 环境变量显示运行时设置,包括特定于供应商的信息

$ 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 特性,为用户提供了更多途径来表达原子操作,帮助在调试或移植代码到不同平台时向用户传达运行时设置。

参考资料

  1. OpenMP Architecture Review Board:OpenMP 应用程序编程接口 3.1 版(2011 年 7 月)
  2. OpenMP Architecture Review Board:OpenMP 应用程序编程接口 4.0 版(2013 年 7 月)
  3. 编译器参考手册 – XL C/C++ for Linux 13.1.2,针对小端发行版
  4. 编译器参考手册 – XL Fortran for Linux 15.1.2,针对小端发行版
  5. 访问 developerWorks Linux 专区,了解关于信息管理的更多信息,获取技术文档、how-to 文章、培训、下载、产品信息以及其他资源。
  6. 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux
ArticleID=1012000
ArticleTitle=IBM XL 编译器中的 OpenMP 支持
publish-date=07282015