本文将主要介绍在Intel® MIC多核架构上运行及优化OpenMP*多线程程序的相关技术,且将围绕offload及native两种运行时执行环境展开详解。
OpenMP编程模型包含了众多调优的编程接口及环境变量设置,本文将就此介绍如何更好地实现程序的高效运行。
1、使用offload模式时设置MIC_ENV_PREFIX来将Host环境的设置传播至MIC(target)计算节点:当将部分计算任务offload至协处理器时,用户可以通过使用MIC_ENV_PREFIX环境变量来限制Host机的环境变量对target端执行的影响,并且有选择地将Host端的环境设置扩展到target端。
值得注意的是,当在Host端时没有设置MIC_ENV_PREFIX时,主机端的缺省配置将直接影响到offload进程的执行环境,这种情况对性能影响较大,因为在主从端都使用OpenMP时,通常需要用户设置不同的处理器affinity策略及线程数。
2、offload模式提供了多种关键字来实现多功能的需求:
#pragma offload target (mic:0) Openmp for loop or function call ...
“:0”作为指定卡的编号,如果在缺省计算卡未指定时,调度单元将缺省指定卡的编号从0开始,从而使程序的性能有更好的预测性。当需要在多个卡之间均衡负载时,用户可以手动在offload区指定target为“mic:n” (n为指定的offload计算卡)。在了解了同步offload计算模型后,下一步需要关心的是运行时计算数据的问题,比如主从处理器之间的拷贝及在target协处理器上的数据分配等。
此时就需要了解跟offload数据有关的的IN, OUT, INOUT及NOCOPY 标示从而来限制数据的作用域,尤其当有很多数据需要可视但并非所有都会被用到时,限制数据的作用域从而减少主从间无谓的数据拷贝将带来很大的性能提升。另外用户还可以指定数据的长度、对齐地址及动态分配等行为,以下的代码展示了使用INOUT标示符来命名一个数据指针memp、指定其长度为200个元素及以8个字节的方式对齐;在INOUT标示符内,用户也可以使用其它的语法来定义offload数据的属性:
#pragma offload target(mic:0), inout(memp : length(200) align(8))
NOCOPY最常用于在程序中使用仅在target上可用的指针,且此标示符常与LENGTH, ALLOC_IF及FREE_IF并用。用户也可以用IN及LENGTH(0)标示符来保持Host端对offload数据的可访问性,从而可使程序员能够在target处理器上指定一块在多次offload之间持续存在的数据,即无需在每次offload时都重新拷贝数据。(关于更多的关键字使用的方法,用户可以参照https://software.intel.com/en-us/mic-developer。)
同步offload的特性支持:用户可使用SIGNAL及WAIT标示符来同步offload区域以及使用OFFLOAD_TRANSFER、OFFLOAD_WAIT编译标示来协调offload计算时的数据传输。
3、使用-openmp-report来了解编译器对程序中OpenMP区域的优化处理:
此命令行开关用于在stderr显示编译器在编译OpenMP程序时的诊断信息,比如使用-openmp-report=1来了解编译时对并行循环等区域的处理,此时也可结合-opt-report-phase=vec来查看更多相关编译时的优化信息。以下的例子显示了编译OpenMP程序时的部分诊断信息:
MICFtest.F90(104): (col. 7) remark: OpenMP DEFINED REGION WAS PARALLELIZED. MICFtest.F90(110): (col. 7) remark: OpenMP DEFINED LOOP WAS PARALLELIZED. MICFtest.F90(172): (col. 7) remark: OpenMP DEFINED REGION WAS PARALLELIZED. MICFtest.F90(186): (col. 7) remark: OpenMP DEFINED LOOP WAS PARALLELIZED. MICFtest.F90(199): (col. 7) remark: OpenMP DEFINED LOOP WAS PARALLELIZED. MICtest.cpp(534): (col. 5) remark: *MIC* OpenMP DEFINED REGION WAS PARALLELIZED. MICtest.cpp(538): (col. 5) remark: *MIC* OpenMP DEFINED LOOP WAS PARALLELIZED. MICtest.cpp(568): (col. 5) remark: *MIC* OpenMP DEFINED REGION WAS PARALLELIZED. MICtest.cpp(582): (col. 5) remark: *MIC* OpenMP DEFINED LOOP WAS PARALLELIZED. MICtest.cpp(594): (col. 2) remark: *MIC* OpenMP DEFINED LOOP WAS PARALLELIZED.
4、在不确定offload区域正在Host还是target上计算运行时,用户可以通过在代码中加入显示当前正执行的线程数目的方法来确定。以下的代码通过在并行区域加入omp_get_num_threads() 的函数接口调用来实现当前线程运行数目的返回,需要注意的是此时应该只需要一个线程来完成调用:
using namespace std; #pragma offload_attribute(push,target(mic)) //{ #include <iostream> #include <omp.h> void testThreadCount() { int thread_count; #pragma omp parallel { #pragma omp single thread_count = omp_get_num_threads(); } cout << "Thread count: "<< thread_count << endl; } #pragma offload_attribute(pop) //} int main (int argc, char **argv) { #pragma offload target(mic), if (argc > 1) testThreadCount(); }
在下一篇文章中将会介绍OpenMP参数在主从处理器上的设置,比如设置栈空间的大小、线程的affinity亲和度及OpenMP运行时库调度策略对运行于Intel® MIC上程序性能的影响。