在英特尔® 集成众核 (英特尔® MIC) 架构上使用 OpenMP* 的最佳设计方案

下载文章

下载 在英特尔® 集成众核 (英特尔® MIC) 架构上使用 OpenMP* 的最佳设计方案 [PDF 401KB]

摘要

本文是对“面向 Linux* 的英特尔® Composer XE”文档的补充。 本文对在为英特尔集成众核 (Intel MIC) 架构编写卸载和本地程序时使用 C/C++ 和 Fortran 的 OpenMP* 扩展的最佳方法进行了概括。

概述

最佳设计方案 (BKM) 中包含了技术、说明和暗示,有助于用户在使用这些系统时获得最大利益。 本文中描述的 BKM 旨在帮助用户在英特尔® 集成众核 (Intel® MIC) 架构上实施并优化程序,同时说明如何针对并行编程 OpenMP 使用通用语言扩展。 本文及其相关文章将对在英特尔 MIC 架构上的卸载执行环境和本地执行环境中运行 OpenMP 的有关技术和技巧进行描述。 OpenMP 提供了一个简单易学的模型,同时辅以精选的默认设置,使本已简单的模式更加易于理解。 这些模式都拥有丰富的选项,支持通过定制的方式满足各种变化的需求。 这些文章将对这些选项中的一些进行探索,以发现它们最有效的应用方式。 每个例子都已经通过英特尔 Composer XE 2013 编译器进行了验证。

在英特尔 MIC 架构上使用所需的环境设置和配置

BKM: 使用卸载中的 MIC_ENV_PREFIX 将主机环境自动传送至目标环境

卸载目标协处理器的执行程序时,用户可以对暴露在目标环境上的主机环境进行限制,同时通过使用 MIC_ENV_PREFIX 环境变量有选择性地将主机环境中定义的设置传送至目标环境。

适用性: 已卸载的 C、C++ 和 Fortran。

详细说明: 如果是设置在主机上(通常设置到“MIC”上), 只有那些以前缀为“_”的定义字符串开头的主机端用户定义的壳式环境变量才能传送至目标环境。 本规则存在一个例外情况,即 MIC_LD_LIBRARY_PATH,该变量在主机(将被卸载程序搜索的主机上的目标库的位置)上有着独特的意义 — 在已卸载程序的目标环境中不存在 LD_LIBRARY_PATH。 本文将以各种示例的方式说明如何使用 MIC_ENV_PREFIX,在此请注意我们当前需要做的就是每次在使用某个定义前缀匹配主机环境变量向目标环境传送之前都需要为该定义前缀附加一个下划线 (“_”) 。

如果 MIC_ENV_PREFIX 没有设置在主机上, 几乎整个主机环境都需要复制到卸载过程的环境上。 此处很容易使人产生困惑,尤其是当您正在目标环境和主机环境上同时使用 OpenMP 时,这是因为您可能需要将某些属性(诸如关联和线程数)设定为不同的属性,并且能反映出具体的架构。

OpenMP 指令和 Offload 指令结合使用的基本方法

BKM: 卸载必要细节

卸载声明启动后可以通过选项进行修改,以便处理各种功能。

适用性: 已卸载的 C、C++ 和 Fortran

详细说明: 卸载声明中最简单的一种如下所示(C 和 Fortran):

 
1 #pragma offload target (mic:0)
2     Openmp for loop or function call ...
3  
4 !$DIR OFFLOAD TARGET (mic:0)
5 CALL offloadfunc(...)

这些“最简单的”示例仍然包含了一个可选组件,即 “:0”,该组件虽然在此处不作要求,但却可以用来设定一个特定的卡单位数(这种情况下即为“卡 0”)。如果没有指定默认单位(即仅有 “mic” ),那么调度程序将始终使用 “卡 0”。这样就能确保获得预见性更好的性能行为。 如果您想在几个卡中平衡负载,您就需要进行手动操作,具体的方法是在卸载中指定一个 “mic:n” 目标,此时 n 指的是您卸载时指向的目标卡。 理解了同步卸载之后,用户接下来就需要对数据进行处理:将它们拷贝出来或者拷贝回去,或者将它们在目标环境或一些组合环境中进行分配。 理解卸载数据的关键就是需要认识到 IN、OUT、INOUT 和 NOCOPY 等指令并没有扩展数据的正常范围;它们只是限制了这种范围。 如果存在许多可见数据,但是其中一些数据可能不会被使用,此时对主机和目标环境间拷贝的数据数量进行限制将有助于显著提升性能。

借助一个适当的机制对传送至目标环境和自目标环境传送的数据进行过滤,而用户则需要对数据的有关事项进行指定,诸如数据的长度、数据调整和动态分配行为。 以下示例说明了如何通过使用 INOUT 说明符对一个数据指针 (memp)进行命名,并为其赋予长度(有 200 个元素的尺寸是由 memp 的类型决定的)和进行数据调整(8-字节)。 INOUT 并不会改变标识符的卸载行为,但是却能在已卸载数据的其他属性被指定的范围内提供少量的句法:

 
1 #pragma offload target(mic:0), inout(memp : length(200) align(8))

第四个关键字 NOCOPY 最常用在使用仅位于目标端的指针的程序中,且通常和 LENGTH、ALLOC_IF and FREE_IF 指令配合使用。 此外,还有一个使用 IN 和 LENGTH(0) 指令维持主机端与已卸载数据间连接的机制,该机制使得程序员可以在多个卸载中使用目标环境上的永久数据,同时不必在每次卸载时重新拷贝这些数据。 (如欲了解更多有关这些关键字如何使用的信息,请参见编译器参考文件,或者访问 因特尔® 至强融核™ 社区门户网站。)

同时提供了异步卸载特性,其中主机和卸载目标端之间严格的连续非重叠执行可以通过使用 SIGNAL 和 WAIT 说明符得到缓解,以便对卸载、 OFFLOAD_TRANSFER 和 OFFLOAD_WAIT 注记进行同步,进而能够对数据传输进行单独处理并使其独立于卸载计算之外。 这些事项在编译器参考文件中也做了详细的描述。

有关这些主题的更多信息,包括实际使用的例子,将在其他文章中做进一步描述。

BKM: 使用–openmp-report 观察编译器如何实现 OpenMP 域和循环

-openmp-report 是一个命令行开关,通过该开关可以引发一个标准错误报告,进而提供诊断信息说明编译器如何在 C/C++ 或 Fortran 源编译过程中处理 OpenMP 结构。 使用 –openmp-report=1 设置学习如何处理编译单元内的并行循环、域和字段。 确保对各种同步结构添加诊断。 同时请看 vec-report

适用性: 已卸载和本地的 C、C++ 和 Fortran。

示例: 确保在使用 –openmp-report 开关时启用 –openmp。 通过配有 OpenMP 并行域和循环结构的代码您会获得类似这种报告,或者其他指出故障循环或域的报告;通过行编号您可以将报告回连至特定的 OpenMP 结构。

 
01 MICFtest.F90(104): (col. 7) remark: OpenMP DEFINED REGION WAS PARALLELIZED.
02 MICFtest.F90(110): (col. 7) remark: OpenMP DEFINED LOOP WAS PARALLELIZED.
03 MICFtest.F90(172): (col. 7) remark: OpenMP DEFINED REGION WAS PARALLELIZED.
04 MICFtest.F90(186): (col. 7) remark: OpenMP DEFINED LOOP WAS PARALLELIZED.
05 MICFtest.F90(199): (col. 7) remark: OpenMP DEFINED LOOP WAS PARALLELIZED.
06  
07 MICtest.cpp(534): (col. 5) remark: *MIC* OpenMP DEFINED REGION WAS PARALLELIZED.
08 MICtest.cpp(538): (col. 5) remark: *MIC* OpenMP DEFINED LOOP WAS PARALLELIZED.
09 MICtest.cpp(568): (col. 5) remark: *MIC* OpenMP DEFINED REGION WAS PARALLELIZED.
10 MICtest.cpp(582): (col. 5) remark: *MIC* OpenMP DEFINED LOOP WAS PARALLELIZED.
11 MICtest.cpp(594): (col. 2) remark: *MIC* OpenMP DEFINED LOOP WAS PARALLELIZED.

BKM: 不确定卸载字段运行在目标端还是主机上? 计算线程数。

如果您不确定您正在测试的代码是否正在正确的目标端上运行,此时就需要添加一段能够返回线程组中的线程数的临时代码。

适用性: 已卸载的 C、C++ 和 Fortran。

示例: 此处通过示例代码说明如何通过使用 omp_get_num_threads() 报告运行在一个并行字段中的线程数。 记得在一个并行字段内使用 get_num_threads 调用,但是需要确保仅有一个线程进行调用,以避免额外的工作。

C/C++

 
01 using namespace std;
02  
03 #pragma offload_attribute(push,target(mic)) //{
04  
05 #include <iostream>
06 #include <omp.h>
07  
08 void
09 testThreadCount()
10 {
11     int thread_count;
12  
13     #pragma omp parallel
14     {
15         #pragma omp single
16         thread_count = omp_get_num_threads();
17     }
18     cout << "Thread count: " << thread_count << endl;
19 }
20  
21 #pragma offload_attribute(pop) //}
22  
23 int
24 main (int argc, char **argv)
25 {
26     #pragma offload target(mic), if (argc > 1)
27     testThreadCount();
28 }

Fortran

 
01       !DIR$ ATTRIBUTES OFFLOAD : mic ::testThreadCount
02       subroutine testThreadCount()
03         use omp_lib
04         integer         :: thread_count
05  
06         !$omp parallel
07           !$omp single
08             thread_count = OMP_GET_NUM_THREADS()
09           !$omp end single
10         !$omp end parallel
11  
12         WRITE (*, '(A,I4)') "The number of threads is ", thread_count
13       end subroutine testThreadCount
14  
15 program threadcnt
16       integer           :: arg_num
17  
18       arg_num = COMMAND_ARGUMENT_COUNT ()
19  
20       !DIR$ ATTRIBUTES OFFLOAD : mic ::testThreadCount
21       !DIR$ OFFLOAD target(mic), if (arg_num .ge. 1)
22       call testThreadCount()
23  
24 end program threadcnt

OMP_GET_NUM_THREADS() 在“最里面的封闭并行域”之内的线程状态上运行,但是存在不需要这种语境的 OMP 调用: 您可以使用 OMP_GET_MAX_THREADS() 获取线程数的一个上限,同时不需要一个并行域。 您是否只想关心代码运行的位置,而不关心线程数? 其实很简单! 只需将 testThreadCount() 代码块替换成以下代码,该代码将在该函数的两个副本中有选择性地使用:

 
1 #ifdef __MIC__  //{
2 cout << “running on target” << endl;
3 #else //}{
4 cout << “running on host” << endl;
5 #endif //}

在本地环境和卸载环境中使用 OpenMP 的区别

BKM: OpenMP 参数在卸载执行和本地执行时有着不同的默认值

由于系统在运行由卸载编译器调用的程序(与交叉编译并运行在本地目标环境中程序相比)时会保留一个内核供自己处理使用,因此一些 OpenMP 可用的参数将随这两种执行环境的不同而不同。

适用性: 已卸载和本地的 C、C++ 和 Fortran。

详细说明: 为获得可用线程数对以上显示的示例程序进行简单扩展时就会发现这些差异。 当作为一个卸载程序运行时,该程序就能生成如下所示的数字(请注意这些示例是在一个早期的原型协处理器上捕捉到的;借助现代的生产部件您将会看到数值更高的数字):

 
01 Thread count: 124
02 Threads_max: 124
03 Proc count: 124
04 Is dynamic: 0
05 Is nested: 0
06 Sched kind: 1
07 Sched modifier: 0
08 Thread limit: 2147483647
09 Max Active levels: 2147483647
10 Active Level: 1

而在同一台机器上以本地程序的方式运行同一个程序时将会得到以下结果:

 
01 Thread count: 128
02 Threads_max: 128
03 Proc count: 128
04 Is dynamic: 0
05 Is nested: 0
06 Sched kind: 1
07 Sched modifier: 0
08 Thread limit: 2147483647
09 Max Active levels: 2147483647
10 Active Level: 1

此处的变化源于在本地运行时所有内核的可用性。 这是在内核线程表中执行和通过一个在 OpenMP 运行时环境中组建的关联图进行模仿导致的结果。 您可以通过在运行一个卸载程序之前设定环境变量 KMP_AFFINITY=verbose 查看这个关联图。 这会生成许多输出内容,其中一些内容如下所示:

 
01 OMP: Info #156: KMP_AFFINITY: 124 available OS procs
02 OMP: Info #157: KMP_AFFINITY: Uniform topology
03 OMP: Info #179: KMP_AFFINITY: 1 packages x 31 cores/pkg x 4 threads/core (31 total cores)
04 ...
05 OMP: Info #147: KMP_AFFINITY: Internal thread 0 bound to OS proc set {1}
06 OMP: Info #147: KMP_AFFINITY: Internal thread 1 bound to OS proc set {5}
07 OMP: Info #147: KMP_AFFINITY: Internal thread 2 bound to OS proc set {9}
08 OMP: Info #147: KMP_AFFINITY: Internal thread 3 bound to OS proc set {13}
09 OMP: Info #147: KMP_AFFINITY: Internal thread 4 bound to OS proc set {17}
10 OMP: Info #147: KMP_AFFINITY: Internal thread 5 bound to OS proc set {21}
11 ...

此处您会看到默认关联会显示出 "fine" 属性,确保每个线程紧密绑定至一个特定的逻辑 CPU,保证操作系统不会在整个机器中移动该线程。 在以上示例中,共有 31 个可用内核提供了 124 个线程。 首先登录到代码卡中并设定 KMP_AFFINITY=verbose,然后再以本地程序的方式运行该代码,此时就会显示还有一个额外内核(四个额外逻辑 CPU)是可用的:

 
01 OMP: Info #156: KMP_AFFINITY: 128 available OS procs
02 OMP: Info #157: KMP_AFFINITY: Uniform topology
03 OMP: Info #179: KMP_AFFINITY: 1 packages x 32 cores/pkg x 4 threads/core (32 total cores)
04 ...
05 OMP: Info #147: KMP_AFFINITY: Internal thread 0 bound to OS proc set {1}
06 OMP: Info #147: KMP_AFFINITY: Internal thread 1 bound to OS proc set {5}
07 OMP: Info #147: KMP_AFFINITY: Internal thread 2 bound to OS proc set {9}
08 OMP: Info #147: KMP_AFFINITY: Internal thread 3 bound to OS proc set {13}
09 OMP: Info #147: KMP_AFFINITY: Internal thread 4 bound to OS proc set {17}
10 OMP: Info #147: KMP_AFFINITY: Internal thread 5 bound to OS proc set {21}
11  
12 ...
此时所有 32 个内核都处于可用状态,同时额外的硬件线程在关联图中显示为 0、125、126 和 127。这种独特的编号方式是由 高级可编程中断控制器 (APIC) 列举硬件线程的方式决定的 — 如 /proc/cpuinfo(过滤到相关位)的内容所示:
 
01 processor       : 0
02 apicid          : 124
03  
04 processor       : 1
05 apicid          : 0
06  
07 processor       : 2
08 apicid          : 1
09  
10 processor       : 3
11 apicid          : 2
12 ...
13 processor       : 123
14 apicid          : 122
15  
16 processor       : 124
17 apicid          : 123
18  
19 processor       : 125
20 apicid          : 125
21  
22 processor       : 126
23 apicid          : 126
24  
25 processor       : 127
26 apicid          : 127

无论在任何特定设备上有多少内核,这种硬件线程映射的结构总是存在的: 使用卸载会迫使系统从关联图中移除一个内核。 从关联图中移除的内核即为那个硬件线程编号不相邻的内核 — 总是最高的三个加上硬件线程 0。该内核同时被英特尔® 协处理器卸载基础架构(英特尔® COI)和其他与卸载有关的服务使用。

在卸载和本地执行之间 OpenMP 程序可能会发现它们自己处于非常不同的环境之中。 本机端通常使用的环境时非常简单的。 借助一个小型的 environ 表格制作器,很轻松就能打印出主进程的环境(添加 LD_LIBRARY_PATH 以提供一个通向 OpenMP 运行时对象的路径)。

 
01 ~ # /tmp/getmyenvnat
02 ENV: "USER=root"
03 ENV: "LD_LIBRARY_PATH=/tmp"
04 ENV: "HOME=/"
05 ENV: "SSH_TTY=/dev/pts/0"
06 ENV: "LOGNAME=root"
07 ENV: "TERM=ansi"
08 ENV: "PATH=/usr/bin:/bin"
09 ENV: "SHELL=/bin/sh"
10 ENV: "PWD=/"
11 ENV: "SSH_CONNECTION=192.168.1.99 48111 192.168.1.100 22"

然而,在用户不对传送进行限制的情况下已卸载环境几乎完全就是主机环境的一个副本。 低级通讯库英特尔 COI 和英特尔® 对称通讯基础架构(英特尔® SCI)每次在未拷贝 LD_LIBRARY_PATH 时都会插入一个标识符,但是主机路径会保持完好无损,同时附加有 “/usr/bin:/bin”。 通过在主机环境中定义 MIC_ENV_PREFIX 很轻松就能清除该主机所有的环境碎片:

 
1 $ export MIC_ENV_PREFIX=MIC
2 $ ./getmyenv mic
3 ENV: "COI_LOG_PORT=65535"
4 ENV: "ENV_PREFIX=MIC"
5 ENV: "PATH=/usr/bin:/bin"
6 ENV: "SCIF_SOURCE_NODE=0"
7 ENV: "__KMP_REGISTERED_LIB_1017=blahblahblahblah-libiomp5.so"

在定义了 MIC_ENV_PREFIX 之后,目标环境将和主机环境可能引发的任何奇怪的负面影响隔离开来,但是对特殊的变量而言,只有对由用户定义的 MIC_ENV_PREFIX 和一个下划线组成的主机变量名称添加一个前缀之后才能准许使用这种变量,如以上示例所示。 这种有效的处理方式可以通过 ENV_PREFIX 的定义得到证明。 将 MIC_ENV_PREFIX 改变成其他参数,那个变量就会消失。

借助这种机制,我们就可以对单独的环境值进行定义以使其适合在主机或目标环境上运行。 然而对于被斥变量 LD_LIBRARY_PATH 来说,这种情况并不适用。 如果您在运行编译器初始化脚本之后再查看您的主机环境,您就会发现 LD_LIBRARY_PATH 和 MIC_LD_LIBRARY_PATH 都已经得到了定义,其中前一个参数可以识别主机库环境而后一个参数可以为已卸载代码查找到目标库的路径。

针对英特尔 MIC 架构上的 OpenMP 的主题建议

BKM: 在修改 OpenMP 堆栈大小 (OMP_STACKSIZE) 时,请注意高线程数的不同选项、默认值和作用

细心的读者将注意到可以使用许多可用的控件对主机和目标上运行线程的堆栈大小进行调整,同时也会注意到这些控件彼此之间可以交互但却不共享同一个默认值。

适用性: 已卸载和本地 C、C++ 和 Fortran。

详细说明: 就 KMP_STACKSIZE 而言,其值可以添加 B/K/M/G/T 等后缀,用以标示字节、千字节、兆字节等:

 
1 export KMP_STACKSIZE=4M

如果未指定 KMP_STACKSIZE 的单位,此处将以字节表示。 KMP_STACKSIZE 可以覆写任何 OMP_STACKSIZE 设置,同时假如两个参数都已设定,运行时将为用户生成一个警告信息。 OMP_STACKSIZE 使用同样的 B/K/M/G/T 后缀符号,但是在未指定的情况下,OMP_STACKSIZE 单位将取千字节为默认值。 为保险起见,请为这些参数始终指定指定一个单位后缀。

如果从主机卸载计算同时 MIC_ENV_PREFIX 并未定义的情况下,堆栈尺寸环境变量在引发目标进程时就会从主机拷贝到目标环境上(连同其它的环境)。 而在 MIC_ENV_PREFIX 已经定义的情况下,用户可以在主机环境中对主机和目标值分别进行设定:

 
1 export MIC_ENV_PREFIX=MIC
2 export OMP_STACKSIZE=8M
3 export MIC_OMP_STACKSIZE=2M

提示: 英特尔至强融核处理器通常具备多达 60 个内核,每个内核支持四个线程,因此支持的硬件线程超过了 240 个。 默认的 OMP 堆栈大小是 4MB,因此默认情况下该平台上的线程可以为所有的堆栈分配近 1 GB 的本地内存! (以上示例可以将这种资源限制减半同时将主机端上的默认限制加倍。)

除这些环境变量以外,运行时库也会对 MIC_STACKSIZE 的主机环境设置作出反应。 这会对卸载代码运行时所处的目标进程中的堆栈的大小进行控制,因此这种情况仅适用于卸载代码。 这种堆栈的默认尺寸是 12 MB,但是仅允许存在一个这样的堆栈。 同样,本地应用运行在一个默认堆栈大小为 8 MB 的进程中。

BKM: 处理器关联可能会对您的算法的性能产生重大影响,因此理解可用的关联类型和算法的行为将有助于您在目标架构上使用关联,以便最大限度地改善性能。

硬件线程被集成到内核中,并在内核中共享资源。 根据算法的行为不同,这种资源的共享可能对特定算法有益,也可能有害。 理解您的算法的行为将有助于您选择出最佳的关联。 您可以在环境 (KMP_AFFINITY) 中或通过一个函数调用 (kmp_set_affinity) 来指定关联。

适用性: 已卸载和本地 C、C++ 和 Fortran

详细说明: 关联从在目标上运行主 OpenMP 线程的进程开始。 通过一个关联图对最后一个专用于卸载系统函数的内核进行隐藏,而已下载程序则对该关联图进行继承。 本地程序可以使用所有的内核,这使得平衡线程所需的运算稍有不同。

一些算法可以利用线程间的数据共享和共享高速缓存加速计算。 其他的算法需要使用许多仅能一次访问的数据,要求对线程进行扩展以便最大限度地使用可用的带宽。 同时还有一些算法存在于中间的某个位置。 借助 MP_NUM_THREADS and KMP_AFFINITY(以及它们的对应函数),用户和程序员可以适当地对他们的线程进行配置:

 
1 export MIC_ENV_PREFIX=MIC
2 export MIC_OMP_NUM_THREADS=60
3 export MIC_KMP_AFFINITY=verbose,granularity=fine,scatter

在一个 31 个内核的带有已卸载应用的目标处理器上,按照以上顺序进行主机环境设置将把硬件线程数限制到仅为可用数量的一半,同时将这些线程尽可能广泛地分布到所有的内核上。 该实例将 MIC_ENV_PREFIX 设置为可以仅为目标端按照以下代码行对线程的数量和它们的分布进行有选择性地设置。 粒度被设置为 fine,以便每个 OpenMP 线程被限制为仅为一个硬件线程。 或者,通过设置 core 粒度将分配给一个内核的 OpenMP 线程分成每四个一组,每组都能自由控制它们各自的内核。

关联类型 COMPACT 和 SCATTER 可以将 OpenMP 线程集合到尽可能少的内核上,也可以将这些线程分散开,以便相邻的线程编号位于不同的内核上。 尽管如此,有时也需要通过一种在相同内核上留有相邻线程编号的方式将有限数量的线程分布到所有的内核上。 为此,我们还提供了一种新的关联类型 BALANCED,并通过它实现上述目的。 通过使用以上所示的 verbose 设置,您可以决定如何分布 OpenMP 线程编号。 只需通过运行一个配有已卸载 OpenMP 组件的程序就能生成关联报告。 我们可以针对多个关联类型运行该程序,查看它们如何变化:

关联: fine、compact

关联: fine、scatter

关联: fine、balanced

由于 MIC_OMP_NUM_THREADS 被设置为 60 而本示例中的内核数达到了 31,这就意味着最后几个内核只能得到一个 OpenMP 线程。 或者,本示例中也可以将 MIC_OMP_NUM_THREADS 设置

 

为 62,这样就能将线程 59 移至内核 29 同时把线程 60 和 61 留在内核 30 上。

在本地执行的情况下,所有相同的规则仍然适用,因此我们不需要为环境变量加 “MIC_” 前缀和使用 MIC_ENV_PREFIX。 我们必须承认使用另一个可用的内核同时将 OMP_NUM_THREADS 提升到 64 将有助于在 balanced 关联类型中为所有的内核提供两个线程。 有关最后一个内核的硬件线程基本编号方案中任何不规则情况都通过关联掩码进行隐藏。

除了这些关联类型以外,更多的外来映射可以通过使用低级别关联接口实现。 针对这些低级别的关联 API,我们需要请您注意以下方面: 如果将代码移植到已经具备明确关联图的英特尔 MIC 架构上,就不可能获得新架构的正确关联图;最好是在能够通过运行实验对关联进行重新优化之前将关联重新设定至一些基本的映射。

BKM: 使用 KMP_PLACE_THREADS 进

 

行扩展测试

借助英特尔 MIC 架构特有的环境变量 KMP_PLACE_THREADS,我们就能更轻松地对一个英特尔至强融核协处理器的子集进行分配并用于扩展测试,同时维持正确的关联。 通过使用 KMP_PLACE_THREADS,您还能对程序使用的机器的部分进行限制,然后再在该空间内设置一个简单的关联。

 适用性: 已卸载和本地 C、C++ 和 Fortran

详细说明: 例如,为了在我们改变内核的总数和每个内核的线程数时能够研究程序的扩展情况,我们可以使用类似 KMP_PLACE_THREADS=30c,2t 一样的设置运行 30 个内核,每个内核提供两个线程。 我们然后可以使用 KMP_AFFINITY=scatter or KMP_AFFINITY=compact 在 60 个硬件线程内用不同的方式列举线程。 因此就没有必要再额外设置 OMP_NUM_THREADS,因为运行时默认将使用它能看到的所有硬件线程(在这种情况下是 60)。

如果您确实设置了 OMP_NUM_THREADS 和 KMP_PLACE_THREADS,请注意:如果通过 OMP_NUM_THREADS 设置的线程数超过了 KMP_PLACE_THREADS 提供的硬件线程数,您将会遭遇过度订阅问题同时很可能会体验到较差的性能;如果未超过,那么您很可能会发现不同的内核将具备不同的线程数,这将给性能的研究带来一定的影响。

关于作者

Robert Reed 是英特尔开发人员产品事业部技术计算、分析工具和运行时分部的一名软件性能工程师。 Robert 曾在 Tektronix 公司从事过多年的编程工作,积累了丰富的编程技巧。 离开 Tektronix 公司后,Robert 曾为多家公司做过承包和咨询工作。在加入了英特尔之后,Robert 凭借自己杰出的性能优化和分析才能为多个部门提供了实质性的帮助,这其中就包括架构特有的 DGEMM 功能模拟和高端 3D 编辑器。 Robert 自始至终都在参与着英特尔 MIC 架构项目的各项工作。 工作之余,Robert 喜欢唱歌和观看有关波特兰内容的现代戏剧。

英特尔、Intel 标识、VTune、Cilk、至强和 Xeon 是英特尔公司在美国和其他国家(地区)的商标。

* 文中涉及的其它名称及商标属于各自所有者资产。

英特尔公司 © 2013 年版权所有。 所有权保留。

性能提示

更多有关性能和性能指标评测结果的完整信息,请访问www.intel.com/benchmarks

在性能检测过程中涉及的软件及其性能只有在英特尔微处理器的架构下方能得到优化。 诸如 SYSmark 和 MobileMark 等测试均系基于特定计算机系统、硬件、软件、操作系统及功能。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。

 

* 文中涉及的其它名称及商标属于各自所有者资产。

 

Per informazioni complete sulle ottimizzazioni del compilatore, consultare l'Avviso sull'ottimizzazione