• 沒有找到結果。

Chapter 

14  _mm512_packstorehi_ps ((void*)(&C[i +16]), _C ); 

15  } 

16  #endif  17  } 

SIMD  指令与汇编指令类似,可读性较差,并且严重依赖于硬件,可移植性较差。SIMD  指令可以选择性使用,如代码量较少,计算却十分密集的地方。

9.2.6 负载均衡优化 

9.2.6.1  什么是负载均衡

负载是指多个任务之间的工作量分布情况,负载均衡是指各任务之间的工作量平均分配。

负载均衡在并行计算里指的是将任务平均分配到并行执行系统中各个计算资源上, 使之充分发 挥计算能力, 没有空闲或等待, 也不存在负载过度。 好的并行方法可以发挥好的负载均衡效果,

负载不均衡将会导致计算效率的下降以及糟糕的扩展性。 因此, 实现负载均衡是并行计算中的 重要方面,尤其针对 MIC,其核数众多,负载均衡对其性能的影响更为明显。

通常情况下,实现负载均衡有两种方案:静态负载均衡和动态负载均衡。静态负载均衡需 要人工将工作区域分割成多个可并行的部分,并保证分割成的各个部分(工作量)能够均衡地 分布到各个处理器上运行, 也就是说工作量在多个任务之间均衡地进行分配, 使并行程序的加 速性能最高;动态负载均衡是在程序运行过程中进行任务的动态分配以达到负载平衡的目的。

实际情况中存在着很多静态负载均衡解决不了的问题,比如,在一个循环中,每次循环的计算 量均不同,且不能事先预知。一般来说,动态负载均衡的系统总体性能比静态负载均衡要好,

但代码实现上更复杂。 

9.2.6.2  CPU/MIC 协同计算负载均衡优化方法 

CPU/MIC 协同计算应用程序中包含 3 个层次的负载均衡:

(1)计算设备(CPU 或 MIC)内部各线程/进程之间的负载均衡。

(2)CPU/MIC 协同计算时,一个节点内 CPU 设备与 MIC 设备之间的负载均衡。

(3)集群计算时,节点之间的负载均衡。

图 9­11 展示了 CPU/MIC 协同计算的负载均衡层次结构。 

1.设备内负载均衡

设备内的负载均衡可以采用 OpenMP 中的三种负载均衡策略:

(1)schedule(static  [,chunk]):静态调度,线程每次获得 chunk 个迭代次数,并以轮询的 方式进行。如果不指明 chunk,则以平均分配的方式进行,这是默认的调度方式。

9 Chapter

图 9­11  CPU/MIC 协同计算负载均衡

(2)schedule(dynamic [,chunk]):动态调度, 动态地将迭代分配到各个线程,不使用 chunk  参数时将迭代逐个地分配到各个线程,使用 chunk 参数时,每次分配给线程的迭代次数为指定 的 chunk 次。

(3)schedule(guided [,chunk]):导引调度是一种采用指导性的启发式自调度方法。开始时 每个线程分配到较大的迭代块, 之后分配到的迭代块会逐渐递减。 迭代块的大小会按指数下降 到指定的 chunk 大小,如果没有指定 chunk 参数,那么迭代块大小最小降到 1。 

OpenMP 的调度策略使用范围如表 9­3 所示。

表 9­3  OpenMP 中不同调度算法的使用范围

调度算法 使用范围 

Static  固定任务量,并且每次迭代任务量相同 

Dynamic  任务量不固定,每次迭代任务量均不同 

Guided  这是 dynamic 调度算法的特殊情况,导引调度算法可以减少调度的开销 

2.CPU/MIC 设备间的负载均衡

由于 CPU 与 MIC 的计算能力不等, 因此 CPU 与 MIC 之间分配的计算量也不能相同,CPU  与 MIC 之间的负载均衡最好的方式是采用动态负载均衡的方法。下面我们分别对任务划分和 数据划分的情况下设备间负载均衡应该采用的优化方法进行介绍。

(1)任务划分:对于任务划分的应用程序,在 CPU 与 MIC 之间的负载均衡可以采用动

9

Chapter

态负载均衡的优化方法,例如有 N 个任务,一个节点内有 2 个 MIC 卡,即三个设备(CPU 和  2 个 MIC),动态负载均衡的方法是每个设备先获取一个任务进行计算,计算之后立即获取下 一个任务,不需要等待其他设备,直到 N 个任务计算完成。这种方式只需要设定一个主进程,

负责给各个计算进程分配任务。

(2)数据划分:由于我们需要一次性在设备上分配内存空间,因此,对于数据划分的应 用程序,我们无法采用动态负载均衡,需要采用静态数据划分方法,然而静态数据划分方法使 异构设备间的负载均衡变得困难,有时甚至无法实现。对于一些迭代应用程序,我们可以采用 学习型的数据划分方法,如让 CPU 和 MIC 分别做一次迭代相同计算量的计算,然后通过各自 的运行时间计算出 CPU 与 MIC 的计算能力比例,然后再对数据进行划分。 

3.节点间的负载均衡

节点间的负载均衡与传统 CPU 集群上的负载均衡一致,即可以采用静态负载均衡和动态 负载均衡的方法。 

9.2.6.3  线程亲和性优化

前面讲到的负载均衡, 是指将任务分配到线程上的方式, 使得每个线程的运行时间尽量相 等。 但是线程并不是凭空存在的, 线程的执行需要运行在处理器的核心上。 本小节讨论的问题,

是如何将线程分配在不同的处理器核心上, 使得每个线程的运行时间尽量相等, 达到负载均衡 的目的。

线程亲和性的负载均衡优化涉及到几个情况:

(1)线程只认识逻辑核,因此使用超线程技术的 1 个物理核也会被视为 2 个计算核心。

(2)这种优化思路,主要解决 cache 利用和物理核心负载均衡的问题,但二者互相制约。

(3)这方面的优化,通常用于不能利用全部物理核心或任务区域性很明显的情况。

综合以上情况,可以发现,线程会根据亲和性设置的不同,被分配到不同的逻辑核心上。

如果线程间有数据相关,当逻辑核心处于同一物理核心时,线程间可以利用同一物理核心的  cache,提高运行速度。但此时计算集中于同一物理核心,在这个物理核心负载较高的同时,其 他核心却处于低负载的状态,仍然影响了性能。而这种情况,在没有利用全部逻辑核心时,体 现得更为明显。

对线程亲和性的负载均衡优化,需要设置环境变量:KMP_AFFINITY,其语法及解释请 参见第 5 章中,OpenMP 环境变量条目中的相关解释。

通过第 5 章的学习,我们知道线程亲和性可以设置 scatter、compact 和 balanced(MIC 专 属)等 3 种方式。 

scatter  模式将线程优先分配到负载较轻的物理核心上,这种方式可以较好地达到负载均 衡,但由于相邻线程不处于同一物理核心当中,因此如果相邻线程间有数据共享的话,则不能 利用 cache 进行加速。而在 MIC 中,虽然可以读取其他核心的 L2 Cache,但会导致可用 cache  容量减少,而且从其他核心读取的速度也慢于从本地 cache 中读取数据。

9 Chapter 

compact 模式将线程按顺序分配至逻辑核心上,这种方式可以使相邻线程尽量处于同一物 理核心当中,如果相邻线程间有数据共享的话,这种方式可以尽可能地利用 cache。如果相邻 线程间计算量差距不大,则很有可能造成严重的负载不均衡。但是,如果是一种比较特殊的任 务分配方式,例如奇数线程负载较轻,偶数线程负载较重,则这种方式反而可以达到较好的负 载均衡。 

balanced 模式是 MIC 上特有的模式,虽然与 scatter 模式类似,也是尽量将线程分配到负 载较轻的物理核心,但与之不同的是,balanced 模式在兼顾均衡性的同时,也会尽量将相邻线 程分配在同一物理核心当中。这种方式在负载均衡和 cache 利用上做到了一定的平衡。

需要注意的是, 这三种方式都是静态划分的方式, 因此需要根据程序运行的实际负载情况,

有针对性地进行设置,才能达到比较好的效果。

相關文件