现在的单处理器系统使用指令级的并行机制(ILP)在执行流水线的不同硬件功能中同时执行多条指令。
现在的共享内存多处理器系统使用ILP机制,但是还可以利用线程级的并行机制(TLP)。TLP不仅可以允许并行执行指令,而且可以并行执行线程,这可以帮助大幅度提高多线程应用运行的速度。TLP的最终目标是以更少的时间完成更多的工作,减少整个应用的运行时间。
虽然添加更多的处理器将会提高共享内存的多处理器系统的性能,但这个解决方案更为昂贵。从另一个角度来看,利用Intel超线程和OpenMPTM 应用编程接口(API)等技术实现的多任务、多线程和群集,也会达到提高性能的目的。
一、利用超线程技术来支持TLP
超线程技术用来消除单处理器和多处理器系统之间的差异,即在单处理器系统中支持TLP机制。超线程技术可以使得在操作系统和相关的应用看来,一个物理处理器像是两个(逻辑)处理器。多处理器系统也可以利用多线程技术的优势,使每个处理器看作是两个处理器。因此,一个Intel Xeon处理器平台将被看作是一个双处理器的系统,一个双Intel Xeon处理器的平台将被看作是一个四处理器的系统,依此类推。
在传统的多处理器平台中,多个CPU使用他们自己的体系状态和执行资源,但TLP机制不同,每个支持超线程的处理器包括两个体系状态,可以共享一组处理器执行资源。超线程技术为多线程应用提供更多的的计算能力,同时还可以更好地利用硬件资源。
二、利用OpenMP提供更多的并行机制
OpenMP是一个便携的可扩展的标准,为程序员提供了一个简单和灵活的接口,可以方便地为共享内存的多处理器平台增加并行机制。计算机硬件、软件和工具制造商,例如Intel,DEC,Silicon Graphics,Kuch & Associates和IBM早在15年前就联合定义了OpenMP标准。OpenMP在所有的架构上都支持使用C/C++和FORTRAN进行共享内存并行编程,包括基于Microsoft? WindowsNT? 和UNIX? 操作系统的构架。OpenMP还使用编译器指令和库函数,帮助并行应用程序员使用C/C++和FORTRAN创建多线程应用。
三、何时使用OpenMP
对于包含有多个耗时的循环的应用,OpenMP特别有用,它可以将工作划分为多个线程。任一应用中划分粗糙的循环级别的并行机制的数量往往比较有限,限制了应用程序的可扩展性。一个并行区域可能嵌入在其它并行区域之内,但是它们缺省的执行方式是必须使用一个线程组来串行执行。
OpenMP允许程序员使用划分良好的循环级并行机制来扩展应用,实现多处理。它们可以添加划分粗糙的并行机制,同时仍然能保留以前在扩展方面所做的投资。使用这种增量式的开发战略,程序员可以避免转向消息传递或其它并行编程模型时所具有的风险。
四、如何编写OpenMP程序
要开发新的应用,程序员必须分析原始问题,将它分解为多个使用共享和本地数据的任务,确定数据之间的依赖性,然后重新组织任务进入执行单元的顺序,这可以使用并行编程环境来实现编码(见 )。对于现有的应用,程序员可以使用一个配置分析工具,例如Intel VTuneTM 性能分析器,找到程序中消耗时间较多的地方。
图1. 编写并行程序的方法
程序员可以使用一个并行应用程序库来完成他们的大多数工作,调用定义得很好的库函数。对于其它类型的应用,可以使用并行API。程序员在使用多线程时应当遵循如下策略:
1.使用线程池
2.活动线程不要使用太多(可用线程的数量等于处理器的数量)
3.使用粗糙划分的线程,而不是精细划分的线程
4.最小化同步
5.不要使用"错误共享"缓存线
五、OpenMP编程模型的运作方式
OpenMP使用fork-join并行机制,程序首先顺序执行,然后转换成为并行程序。这个程序开始时先是一个主线程,然后在遇到用户定义的并行区域时创建
图2. Fork-join并行机制
在并行区域之内,多个线程可以执行相同的代码块,或使用工作共享结构体并行执行不同的代码段。在结构体的末尾的障碍点将出现线程同步。虽然非结构化的块可以拥有多个入口和多个出口(见 图3 ),但结构化的块仅仅在顶部有一个入口,在末尾有一个出口(见 图4 )。唯一的例外就是程序结束 - 在FORTRAN和C/C++中分别是STOP和exit()命令,这些命令是唯一允许在块内使用的分支。在结构化块的出口处,进行一次内存flush操作,在这次操作中,所有的线程都将可见的变量回写到内存中,从而将所有的线程返回到一个公共的已知状态。
图4. 结构化块举例
六、使用OpenMP API
程序员可以使用omp parallel pragma在OpenMP中创建线程。例如,下面的代码展示了如何创建一个三线程的并行区域:
float array[N];
int ID;
//function requests 3 threads
omp_set_num_threads(3);
#pragma omp parallel
{
//function returns thread ID
ID = omp_get_thread_num();
div_func(array, ID);
}
printf("Done./n");
每个线程将都将执行结构化块内的代码,最后,每个线程将冗余地调用div_func(array,ID),其中ID=0到2(见 图5 )。所有的线程都共享一份数组变量,并将在打印语句(障碍)之前重新集合。
七、共享数据与私有数据的比较
因为OpenMP是一个共享内存的编程工具,在并行区域之前定义的大多数变量都是全局变量 - 将在线程之间共享。但是,有些变量属于线程私有的,例如并行区域调用的子程序的局部变量或一个语句块内的自动变量。指令中的语句可以声明共享或私有数据。在中,声明的变量array、j和k在所有线程之间共享,而temp对于每个线程来说是局部变量。
图6. 共享数据和私有数据的比较实例
八、工作共享结构体
for工作共享结构体将循环划分到一组线程中。工作共享结构体不仅可以使多线程的编码更为高效,而且可以简化多线程的代码。在演示了这一效果。
图7. 工作共享结构体举例 图8. 并行区域中工作共享和OpenMP的效率
缺省情况下,障碍存在于omp for pragma的末尾。程序员可以使用nowait语句来取消这一障碍。
九、减少循环的依赖性
循环必须独立于工作共享才能有效。参考 的公共循环。变量res创建了一个循环依赖性,并行操作将无法运行。因此,程序员必须使用一个减小语句,见 ,才能并行执行循环。
图9. 循环依赖性举例 图10. 使用OpenMP减少的循环依赖性的举例
在图10 的循环中,每个线程都拥有自己的res拷贝,它累计结果。在循环之后,私有拷贝将综合到一份共享拷贝中。
十、使用OpenMP编译
Intel C++和FORTRAN编译器(都是6.0版)中包括最新的支持OpenMP(1.0版的API)的功能。如图11 所示,这些编译器中包括一个自动并行机制开关(-Qparallel)和OpenMP开关(-Qopenmp)。程序员可以使用-Qparallel开关来编写他们不想使用OpenMP指示符来优化的代码。
图11. Intel编译器中用于自动并行和OpenMP指示符的优化开关
例如,如果软件提供商没有时间和人员来跟踪原始源代码和创建OpenMP并行结构体,但想利用共享内存多处理系统中的并行机制来编程,他们可以在重新编译代码的时候使用-Qparallel开关。-Qparallel开关使编译器可以自动发现能够并行处理的代码区域。但是,让编译器作出这种决定在完善性和效率上可能比不上手动使用OpenMP进行优化所具有的性能。
自动并行化和SPECviewperf基准
程序员在使用SPECviewperf? 7.0基准时,必须使用自动并行开关,这种方式能在不更改源码的情况下优化代码。
图12. 使用自动并行开关和超线程技术的SPECviewperf性能
SPECviewperf基准测试结果表明使用自动并行开关来替代标准(没有并行处理)代码将会获得很高的性能增益。在单处理器配置中使用超线程技术对性能有积极的影响;从理论少上看,它的性能几乎与两个物理处理器系统的性能相当。正如SPECviewperf基准的ugs-10模型所得到的结果,不是所有的测试将会从自动并行处理中受益,所能获得性能增益是与具体代码相关的。每个程序代码可能会受益,也可能不能受益。但是如果速度对于最终用户来说很重要,使用自动并行开关来重新编译将是值得一做的。
十一、OpenMP指令和Linpack基准
如果时间和资源允许,推荐使用OpenMP结构体和-Qopenmp开关。程序员对自己的代码总是特别了解,他们清楚那些代码区域会从OpenMP中受益。表明在具有两个2.0GHz的Intel Xeon处理器的平台在支持和不支持超线程技术运行的情况下,使用OpenMP指示符所带来的Linpack基准性能的变化。这些测试结果展示了在使用OpenMP并行技术和超线程技术时代码的行为。如果取消使用超线程,标准代码和使用OpenMP的代码两者之间几乎没有差异。但是,如果启用了超线程,标准代码的性能将会下降,OpenMP代码的性能将超过标准代码的性能。同时使用OpenMP代码和超线程技术的性能也将超过没有使用超线程的标准代码和OpenMP代码的性能。
图13. 使用OpenMP并行和超线程技术的Linpack性能
增强处理器性能
OpenMP是一些易于使用的API,用来编写多线程程序。它不再需要用户处理重复分区、数据共享以及线程计划和同步的底层细节。配置工具,甚至更为简单的测试和发现错误,都会有助于确定哪些代码将会从OpenMP中受益。而且,如果组合使用OpenMP和Intel Xeon处理器的超线程技术,将会是获得更强大的计算能力的经济高效的选择。