以下技术是用于改善 OpenMP 应用程序性能的一些常规技术。
将同步降至最低。
避免或尽量不使用 BARRIER、CRITICAL 段、ORDERED 区域和锁定。
使用 NOWAIT 子句可以消除冗余或不必要的屏障。例如,在并行区域末端总是有一个隐含的障碍。将 NOWAIT 添加到区域最后的 DO 中可以消除一个冗余的屏障。
使用已命名的 CRITICAL 段进行细粒度锁定。
使用显式 FLUSH 时要非常小心。刷新将造成数据高速缓存恢复到内存,而随后的数据访问可能需要从内存重新加载,从而会降低效率。
缺省情况下,空闲线程将在特定的超时期限后进入休眠状态。缺省超时期限对于您的应用程序而言可能过短,从而导致线程过早或过晚地进入休眠状态。可以使用 SUNW_MP_THR_IDLE 环境变量覆盖缺省超时期限,甚至可以使空闲线程永不进入休眠状态并始终处于活动状态。
尽可能在最高级别(如外部 DO/FOR 循环)执行并行化。在一个并行区域中封闭多个循环。通常,使并行区域尽可能大以降低并行化开销。例如:
This construct is less efficient: !$OMP PARALLEL .... !$OMP DO .... !$OMP END DO .... !$OMP END PARALLEL !$OMP PARALLEL .... !$OMP DO .... !$OMP END DO .... !$OMP END PARALLEL than this one: !$OMP PARALLEL .... !$OMP DO .... !$OMP END DO ..... !$OMP DO .... !$OMP END DO !$OMP END PARALLEL
在并行区域中使用 PARALLEL DO/FOR 指令,而不是工作共享 DO/FOR 指令。与可能包含几个循环的常规并行区域相比,可以更有效地实现 PARALLEL DO/FOR。例如:
This construct is less efficient: !$OMP PARALLEL !$OMP DO ..... !$OMP END DO !$OMP END PARALLEL than this one: !$OMP PARALLEL DO .... !$OMP END PARALLEL
在 Solaris 系统中,使用 SUNW_MP_PROCBIND 将线程绑定到处理器。处理器绑定与静态调度一起使用时,将有益于展示某个数据重用模式的应用程序,在该应用程序中,由并行区域中的线程访问的数据将位于上一次所调用并行区域的本地缓存中。请参见2.3 处理器绑定。
尽可能使用 MASTER,而不是 SINGLE。
MASTER 指令作为不带隐式 BARRIER 的 IF 语句来实现:IF(omp_get_thread_num() == 0) {...}
SINGLE 指令的实现方式类似于其他工作共享构造。跟踪哪个线程首先到达 SINGLE 会增加额外的运行时开销。如果未指定 NOWAIT,则存在一个隐式 BARRIER。这样效率较低。
选择适当的循环调度。
STATIC 不会造成同步开销,并且可以在数据装入高速缓存时保持数据的局域性。但是,STATIC 可能导致负载失衡。
由于 DYNAMIC 和 GUIDED 要跟踪已经分配了哪些块,因此会发生同步开销。虽然这些调度会导致数据局域性较差,但是可以改善负载平衡。使用不同的块大小进行实验。
使用 LASTPRIVATE 时要非常小心,因为它有可能导致很高的开销。
从并行构造返回时,数据需要从专用区复制到共享存储区。
编译器代码检查哪个线程逻辑上执行最后一个迭代。这会在并行 DO/FOR 中每个块的末尾添加额外的工作。如果块数很多,开销将会增加。
使用有效的线程安全内存管理。
应用程序可以在编译器生成的代码中显式或隐式使用 malloc() 和 free(),以支持动态/可分配数组、向量化内例程等。
libc 中的线程安全 malloc() 和 free() 具有由内部锁定造成的高同步开销。可以在 libmtmalloc 库中找到更快的版本。请用 -lmtmalloc 进行链接以使用 libmtmalloc。
在数据量较少的情况下,OpenMP 并行循环可能性能不佳。使用 PARALLEL 构造中的 if 子句指示循环仅应在预期可以提高一定性能的情况下并行运行。
如果可能,请合并循环。例如:
将以下两个循环
!$omp parallel do do i = ...
statements_1
end do !$omp parallel do do i = ...
statements_2
end do
合并为一个循环
!$omp parallel do do i = ...
statements_1
statements_2
end do
如果应用程序缺乏超出某个级别的可伸缩性,请尝试使用嵌套并行操作。有关 OpenMP 中嵌套并行操作的更多信息,请参见1.2 本章所使用的特殊惯例。