一些笔记整理
1. 基础认知
1.1 如何编写并行程序
基本思想:将要完成的任务分配给多个核。
两种广泛的方法:任务并行和数据并行
- 任务并行
将待解决问题所需要执行的各个任务分配到各个核上执行。(执行不同的指令)
- 数据并行
将待解决问题所需要处理的数据分配给各个核,每个核在分配到的数据集上执行大致相似的操作。
1.2 两种并行内存系统
- 共享内存系统
各个核能够共享到计算机的内存。
(比如Pthreads和OpenMP就是为此设计)
- 分布式内存系统
每个核拥有自己的私有内存,核之间的通信是显式的,必须使用类似网络中发送消息的机制。
(MPI就是为分布式内存编程而设计)
1.3 目前关注的扩展
- 消息传递接口MPI(Message-Passing Interface)
- POSIX线程Pthreads(POSIX threads)
- OpenMP
1.4 指令级并行(ILP)
Instruction-Level parallelism
通过让多个处理器部件或者功能单元同时执行指令来提高处理器的性能。
认知:指令级并行很难利用,因为程序中有许多部分之间存在依赖关系。
两种主要的方法:流水线和多发射。
1.4.1 流水线
此处不详细表述。
通过将功能分成多个单独的硬件或者功能单元,并把它们按顺序串接起来提高性能。
1.4.2 多发射
通过复制功能单元来同时执行程序中的不同指令。
- 静态多发射
功能单元在编译时调度
ps: 先编译成固定顺序,CPU再按顺序执行
- 动态多发射
功能单元在运行时间调度
ps:处理器自动判断哪些指令可以并行
系统必须找出能够同时执行的指令,其中一种最重要的技术是:预测。
1.5 硬件多线程
线程级并行TLP:通过同时执行不同线程来提供并行性。与ILP相比,TLP提供了粗粒度的并行性,即同时执行的程序基本单元(线程)比细粒度的程序单元(单条指令)更大或更粗。
1.5.1 细粒度多线程
处理器在每条指令执行完后切换线程,从而跳过被阻塞的线程。
缺陷:执行很长一段指令的线程,在执行每条指令的时候都需要等待。
1.5.2 粗粒度多线程
为了避免上述问题,只切换那些需要等待较长时间才能完成操作(比如从主存中加载)而被阻塞的线程。
ps:只有线程遇到一定时间的阻塞时,才会切换执行其他的线程。
优势:不需要线程间的立即切换,切换的次数少了。
但仍有切换,阻塞要等待,导致延迟。
1.5.3 同步多线程
模拟多核的原理。
略。
1.6 并行硬件
如果我们能够通过修改源代码来开发并行性,或者必须修改源代码来开发并行性,那么我们认为这种硬件是并行硬件。
Flynn分类法经常用来对计算机体系结构进行分类,依据是它能够同时管理的指令流数目和数据流数目。
SISD(经典冯诺依曼架构)、SIMD、MISD(不做讨论)、MIMD
典型的冯·诺依曼系统是 单指令流单数据流SISD系统。它一次执行一条指令,一次存取一个数据项。
1.6.1 SIMD系统
单指令多数据流 Single Instruction, Multiple Data
通过对多个数据执行相同的指令,从而实现在多个数据流上的操作。
ps:就如同一个控制单元和多个ALU。
它适合对处理大型数组的简单循环实行并行化。通过将数据分配给多个处理器,然后让各个处理器使用相同的指令来操作数据子集实现并行,即数据并行。
经典的SIMD系统是:向量处理器。此外,图形处理单元GPU和台式机的CPU也利用了SIMD计算方面的知识。
SSE、AVX、CUDA、Xeon Phi
1.6.2 MIMD系统
多指令多数据流 Multiple Instruction, Multiple Data
该系统支持同时多个指令流在多个数据流上操作。
MIMD系统通常包括一组完全独立的处理单元(核),每个处理单元都有自己的控制单元和ALU。而且它通常是异步的,各个处理器能够按他们自己的节奏运行,不同处理器上的系统时间之间没有关联。
- 共享内存系统
一组自治的处理器通过互连网络与内存系统相互连接,每个处理器能够访问每个内存区域。处理器通过访问共享的数据结构,从而进行隐式的通信。
一致内存访问系统UMA 和 非一致内存访问系统 NUMA
详情见《并行程序设计导论》P23
- 分布式内存系统
每个处理器有自己私有的内存空间,处理器-内存
对之间通过互连网络相互通信。处理器之间通过发送消息或者使用特殊的函数来访问其他处理器地内存,从而进行显式的通信。
最广泛使用的分布式内存系统称为集群(clusters)。而事实上,这些系统中的节点,通常都是有一个或者多个多核处理器的共享内存系统。这种系统并非纯粹的分布式内存系统,称为混合系统。
1.6.3 互连网络
互连网络无论是在分布式内存系统还是在共享内存系统中,都扮演了一个决定性的角色。一个缓慢的互连网络会严重降低除了简单并行程序外所有程序的整体性能。
共享内存系统的互连网络和分布式内存系统的互连网络是有区别的。
1.6.3.1 共享内存互连网络
目前最常用的两种互连网络是总线和交叉开关矩阵。
- 总线(BUS)
总线是由一组 并行通信线
和 控制对总线访问的硬件
组成的。
核心特征:
连接到总线上的设备共享通信线。
优点:低成本、灵活性,多个设备能够以较小的额外开销连接到总线上。
缺点:因为通信线是共享的,随着连接的设备增多,争夺总线概率增大,预期性能下降,处理器会经常等待访问内存。
因此,随着内存系统规模的增大,总线会迅速被交换互连网络取代。
- 交叉开关矩阵(Crossbar)
交换互连网络使用交换器来控制相互连接设备之间的数据传递。
交叉开关矩阵中,线表示双向通信线路,方块表示核或者内存模块,圆圈表示交换器。
交叉开关矩阵允许在不同设备之间同时进行通信,所以比总线快。但交换器和链路带来的开销也相对高。
一个小型的基于总线系统
比 相等规模的 基于交叉开关矩阵系统
便宜。
1.6.3.2 分布式内存互连网络
分布式内存互连网络通常分成两种:直接互连与间接互连。
a. 直接互连
在直接互连中,每个交换器与一个 处理器-内存
对 直接相连,交换器之间也相互连接。
- 环和二维环面网络
环比简单的总线高级,因为它允许多个通信同时发生。在处理器必须等待其他处理器才能完成通信的情况下,制定通信方案会更容易。
在环中,每个交换器需要支持3个链路。// 在图中,连接到每一个圆圈的线,有3条,对应着3条双向通信线路,即3个链路。
环面网络更加昂贵,因为交换器更加复杂,每个交换器需要能够支持5个链路。
如果有p个处理器,在环面网络中链路的数目为3p,而在环中为2p。// 解释: 因为直接互连意味着一个交换器连一个处理器-内存对,因此有p个处理器就有p个交换器。
- 等分宽度和等分带宽
衡量同时通信的链路数目
或者连接性
的一个标准——等分宽度。
想象并行系统被分成两部分,每部分都有一半的处理器或者节点,这两部分之间能够同时发生的通信数目(考虑最坏情况)。
如果认为以上说法不好判断的话,对于等分宽度的另一种计算方式为:
最少去除多少条线可以让两部分断开连接,去除的链路数就是等分宽度。
示例:对于正方形的二维环面网络,有p = q^2 个节点(q为偶数)
等分宽度为
2p 2\sqrt{p} 2p
砍掉”中间“的水平链路和”回绕“的水平链路。
链路的带宽:是指它传输数据的速度。通常用兆位每秒或者兆字节每秒来表示。
等分带宽通常用来衡量网络的质量。
等分带宽 = 等分宽度 * 每条线的带宽
示例:在一个环中。等分宽度为2。如果链路的带宽为10亿位每秒,那么环的等分带宽就是20亿位每秒(2000兆位每秒)。
- 全相连网络
这是最理想的直接互连网络,即每个交换器与每一个其他的交换器直接连接。它的等分宽度为p^2/4。但是这个需要的链路太多了。
- 超立方体
一种已经用于实际系统中的高度互连的直接互连网络。
它是递归构造的。
维度为d的超立方体有p = 2^d个节点,并且,在d维超立方体中,每个交换器与一个处理器和d个交换器直接连接。
这样,超立方体的等分宽度为p/2,所以它比环或者二维环面网格连接性更高,但需要更加强大的交换器,因为每个交换器必须支持 1 + d = 1 + log2(p)
条连线。所以,这是更贵的。
b. 间接互连
(简单理解)
交换器不一定与处理器直接连接。
它们通常由一些单向连接和一组处理器组成,每个处理器有一个输入链路和一个输出链路,这些链路通过一个交换网络连接。
交叉开关矩阵 和 omega网络。
延迟:从发送源开始发送数据,到目的地开始接收数据 之间的时间。
带宽:开始接收数据之后,接收数据的速度
一个互连网络的延迟为L秒,带宽为b字节每秒,则传输一个n字节的消息需要时间
time = L + n/b
一个新手的常识性问题:
Q:为什么不是所有的MIMD系统都是共享内存的?——大多数程序员觉得通过共享数据结构隐式地协调多个处理器的工作,比显式地发送信息更加吸引人。(巧了,原来不是我一个人这么觉得。)
A:这里有一些问题。其中主要的硬件方面的问题是互连网络扩展的代价。当向总线增加处理器时,访问总线发生冲突的可能性骤升,所以总线适用于处理器数目较少的系统。而大型的交叉开关矩阵非常昂贵。分布式内存互连网络相对便宜(哪怕是超立方体、环面网格)。因此分布式内存系统比较适合于那些需要大量数据和计算的问题。
1.6.4 Cache一致性
Cache一致性问题:在多核系统中,各个核的Cache存储了相同变量的副本,当一个处理器更新Cache中该变量的副本时,其他处理器应该知道该变量已更新,即其他处理器中Cache中的副本也应该更新。
(这种不可预测的行为与系统使用写直达策略还是写回策略无关。)
两种方法来保证Cache一致性:
-
监听Cache一致性协议
-
基于目录的Cache一致性协议
1.6.4.1 监听Cache一致性协议
这个想法来源于基于总线的系统:当多个核共享总线时,总线上传递的信号能够被所有连接到总线的核“看”到。当核0更新它Cache中的x的副本时,它把这个更新消息在总线上广播,此时若核1正在监听总线,则它能够得知x已经更新,并将自己Cache中的x副本标记为非法。
实际的监听协议,与上述大致原理的差别:广播会通知其他核,包含x的整个Cache行已经更新,而