3. 多核处理器

357 阅读14分钟

多核处理器的发展

image.png

多核处理器的发展是计算机演进的关键环节。Intel、AMD、IBM 等芯片大厂均推出过多核处理器产品。时至今日,多核乃至众核架构已成为计算机领域的常见配置,深刻影响着计算技术的发展方向。

我们可以简单了解一下这些厂家是如何发展的。

Intel2006年推出了酷睿2代架构Duo,取代了奔腾D系列,采用65nm或者45nm工艺,引入了酷睿微架构,显著提升了性能与能效,支持多任务处理,是个人电脑单核向多核过渡的重要产品,广泛应用于主流台式机与笔记本电脑。

随后2007年Intel推出了Quad酷睿2四核,将多核技术进一步普及,针对高性能需求,可以通过增加核心数来提升多线程处理能力,为后续酷睿i系列的多核发展奠定了基础,后边的i5、i7、i9级别的产品,基本都是多核处理器。

AMD在2003年就发布了Opteron,是面向服务器与工作站的64位处理器,率先支持x86-64架构,打破了英特尔在服务器领域的垄断,以高性价比、多核心和优秀的并行处理能力应用在企业级计算上。

Sun UltraSPARC支持Sloaris操作系统,主要用于工作站、服务器以及高端嵌入式系统,特点是采用超标量架构、多线程技术,随后Sun被甲骨文收购,逐渐退出市场。

IBM2006年前后也推出了异构处理器,用于PlayStation 3游戏机等高性能领域。

这些CPU分别在不同领域推动了计算机多核技术的发展,体现了从单核到多核,从通用计算到异构并行计算的演进趋势。

以酷睿i7芯片为例,我们来看一下这个处理器架构。

image.png

图中包含多个处理器核心,它们是处理器执行指令、处理任务的主要单元,这些Core之间共享三级缓存,用于存储数据和指令,减少核心对内存的直接访问,提升数据读取速度,降低延迟;内存控制器则负责管理处理器和内存之间的数据传输,协调内存访问,确保数据高效读写;位于架构两侧,是处理器与外部设备通信的高速接口,支持高带宽、低延迟的数据传输,增强系统扩展性。最终MiscI/O用于连接其他外部设备,实现处理器与外部环境的交互。

在Core中,最上边一层为乱序调度与指令提交,以及执行单元。充分利用执行单元资源,允许指令不按程序顺序执行,但是按照程序顺序提交结果,确保执行正确性,最终交由执行单元完成算术、逻辑等运算操作。

中间一层为L1数据缓存、指令解码、寄存器重命名与微码,还有内存顺序与执行。L1存储常用数据,快速提供给执行单元,减少数据等待事件;指令解码等将指令转换为处理器可执行的未操作,通过寄存器重命名解决指令间的数据依赖,微码则处理复杂指令的分解与执行;最终内存排序与执行确保内存操作按照正确的顺序执行,协调数据依赖关系,同时管理指令的执行流程。

最下边是L1指令缓存与取指、分支预测、虚拟内存以及L2缓存与中断处理。

这些实际上都是我们在计算机体系结构中经常见到的方案,它们都被集成在这一颗处理器上,最终保证了它良好的计算性能。

对称多处理(Symmetric multiprocessing,SMP)

SMP是一种多处理器的计算机软硬件架构,由两个或者更多相同的处理器连接到单一共享的主内存,并且能够完全访问所有输入/输出设备。并且由单一操作系统控制,该系统平等地对待所有处理器。

image.png

SMP就好比一群人协作干活。团队的多个成员共用一套工具,并且都是平等使用,同时还有一个管理者(OS)把任务对称地分给每个处理器,让它们一起并行处理。

如今大多数多处理器系统都采用SMP架构,对于多核处理器,SMP架构将每个核心视为独立的处理器进行处理。

图中红色的线条代表系统总线(System Bus),是处理器、主存、缓存和IO之间通信的通道。

绿色模块是总线仲裁器(Bus arbiter),负责管理和协调各个组件对系统总线的访问,避免冲突。

多个紫色处理器都单独连接一个黄色的缓存,用于快速存储和读取数据,提升处理效率。蓝色模块是主存,所有处理器共享,负责存储系统和程序的主要数据;灰色模块代表处理器与外部设备(硬盘、键盘)交互的接口。

把当前的SMP组合起来,再用网络线缆连接起来,就构成了计算机集群,计算机集群是由多个计算机系统通过网络连接组成的组合,它们协同工作以提供更高的计算能力,可靠性和扩展性。

与SMP架构不同,计算机集群中的每个节点都有属于自己的独立内存,节点之间通过网络通信协作,而非共享单一主内存。

多线程

ILP对于程序员拥有较高的透明性,程序员无需过多关注底层细节即可利用,但是在某些应用中,ILP利用非常有限,当处理器因为缓存未命中而停顿等待时,功能单元的利用率会大幅下降。

而此时多线程(Multithreading)则允许多个线程以重叠的方式共享单个处理器的功能单元,多线程在一组线程间共享大部分处理器核心资源,仅复制私有状态(例如寄存器和程序计数器),为每个线程复制独立的状态,意味着为每个线程创建单独的寄存器文件、程序计数器PC和页表。

线程切换比进程切换高效地多。

Why?

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,也被称为 “轻量级进程”。线程共享所属进程的内存地址空间和资源(如文件、内存等),但拥有独立的执行路径,包括自己的程序计数器、寄存器集和堆栈。简单理解,进程像是一个 “工作项目”,线程则是这个项目里的 “具体任务执行者”,多个线程协作完成进程的目标。

那为什么线程要比进程切换更快?

共享地址空间! 同一进程内的线程共享相同的虚拟内存空间和页表。切换线程时,无需像进程切换那样更换页表、重新映射内存地址,减少了内存管理的开销。

切换数据量少! 线程切换只需保存和恢复少量私有数据(如寄存器、程序计数器、堆栈指针等),而进程切换需要处理更多资源(如独立的内存空间、文件描述符等),涉及的数据量和操作更复杂。

缓存利用率高! 由于线程共享进程的内存空间,CPU 缓存中的数据在切换线程时仍可能有效,无需像进程切换那样频繁刷新缓存,提升了效率。

我们来具体看一下线程的执行情况。

image.png

先看左边,有两个指令流A和B,令流 A 包含 add a,b,c、mul d,b,e 等指令,指令流 B 有 fadd a,b,c、fmul d,a,e等。在取指令之后进行指令解码,这个地方对指令进行了数据依赖分析,为了方便在后续进行乱序执行。

在执行的郭恒中,我们可以看到指令的执行顺序发生了变化,但是最终进行写回重排序,确保指令结果按照程序顺序写回,保证最终结果的正确性。

我们再来思考一个问题,硬件是如何支持多线程的?

硬件如何支持多线程

细粒度多线程在每个时钟周期都会进行线程间的切换,这使得多个线程的指令执行呈现交错状态,它的优点是无论短时间还是长时间的停顿,细粒度多线程都能有效隐藏由此产生的吞吐量损失。当一个线程发生停顿,哪怕时间很短,但是其他线程的指令依然可以立即执行,充分利用处理器资源,避免资源闲置。

但是这样会导致单个线程的执行变慢,对于一个本来没有任何停顿、顺利执行的线程来说,会因为其他线程指令的交错执行而被延迟。 Sun Niagara处理器就是细粒度多线程的一个典型实现案例。

粗粒度多线程 粗粒度多线程相比较细粒度而言,仅在代价较高的停顿,例如L2或者L3缓存未命中的时候才会切换线程。它同样具有较高的启动开销,同时克服吞吐量损失的能力有限,尤其是针对持续时间较短的停顿,无法有效利用处理器资源。

同步多线程SMT 是一种细粒度多线程的变体,它有一个更通用的名称——超线程(Hyper-Threading),基于多发射、动态调度的处理架构。

它的原理是借助寄存器重命名和动态调度技术,允许来自独立线程的多条指令,无需考虑彼此之间的依赖关系即可执行。指令间的依赖关系的解决由处理器的动态调度能力处理,从而更加高效地利用处理器资源,提升并行处理能力。

详细有关SMT的内容,大家可以参考一下这个博客——说说超线程那些事儿

多线程多到极致会发生什么?

GPU,GPU实际上就是由数量非常多的线程组成。

image.png

如图所示,在CPU中,例如AMD Phenom处理器中,具有6个核心,4-wide的单指令多数据技术,具备一个寄存器状态集,SIMD适用于通用计算,能够提升数据并行处理能力。

Intel i7-core也是拥有6核心,同样是4-wide SIMD,它具有两个状态集,通过多状态集优化线程切换与执行效率,兼顾多任务与计算性能。

Sun UltraSPARC T2具有8个核心,每个核心都有8个状态集,没有SIMD,但是同样可以多线程处理,因为多状态集可以支持高并发线程,但是并非数据并行类。

在GPU部分,AMD和Nvidia是GPU领域的龙头企业,AMD Radeon HD6970系列拥有24个核心,每个核心都有64-wide状态集(通常8-16个),16-wide SIMD。核心数量多,SIMD宽度大,状态集丰富,专为大规模并行计算设计,如图形渲染中处理像素数据。

CPU核心数量相对较少,侧重复杂指令处理与通用性,通过SIMD、多状态集等优化特定场景效率;GPU核心数多,强调并行处理与数据吞吐量,依赖宽SIMD和丰富的状态集,适合图形渲染、科学计算、DeepLearning等大规模并行任务。

多线程类型汇总

通过上述学习,我们了解到四种超标量处理器架构在多线程支持上的差异:

  1. 无多线程支持的超标量处理器(Superscalar):执行时会存在大量空白,因为处理器常常会因为指令或者数据等待而闲置,资源利用率低,无法利用多线程隐藏停顿。
  2. 粗粒度多线程(Coarse-grained multithreading,Coarse MT):仅在代码较高的停顿时(例如二级或者三级缓存未命中)切换线程。线程切换后有连续的执行端,但是切换启动开销大,无法频繁切换,对于闲置时隙利用较好,但是对于短停顿的处理能力有限。
  3. 细粒度多线程(Fine-grained Multithreading,Fine MT):每个时钟周期都进行线程切换,指令执行高度交错,可以有效隐藏短时间和长时间的停顿损失,但会减缓单个线程的执行速度,因为就绪线程会被其他线程指令打断。
  4. 同步多线程(Simultaneous Multithreading,SMT):作为细粒度多线程的变体,基于多发射、动态调度架构。图中不同颜色代表不同线程,它们的混合更加密集,利用寄存器重命名和动态调度,允许独立线程的指令无需顾虑依赖关系即可执行,更高效地利用执行时隙,最大化并行处理能力。

image.png

我们可以看一下多线程环境下时分复用的指令流处理流程,我们可以看到stream A和stream B交替执行,

image.png

大量的线程交错执行,让硬件设备始终保持一个较大的吞吐量,尽管单个线程的执行时间加长了,但是整体的执行时间却减少了。

数据并行性 Data Parallelism

最常见的数据并行是向量运算(Vector Operation),它可以对向量进行批量处理,通常是64位浮点型,但是也有整型向量,一般来讲向量长度可能为64,也可能为128.

image.png

图中展示了标量和向量的对比。我们可以看到,一次可以对多个数据进行并行操作,同样是加法,前者每次只能加一次,而后者可以加N次。

右图展示了向量运算的硬件结构。

主内存通过向量加载-存储单元与向量寄存器交互,负责向量的读取和存储。向量寄存器连接到多个运算单元,支持多种向量运算。标量寄存器和向量处理有不同的路径。

image.png

如下图,我们可以清晰的看到,SIMD可以一次性使用多个运算单元。

最终我们来对比一下他们之间的差异:

image.png

总结

今天我们学习到了指令级并行、线程级并行、向量级并行。

指令级并行对程序员透明,无需修改代码即可利用硬件特性提升性能,有效利用处理器流水线,提升单个核心的指令吞吐量,但是受到数据依赖和控制依赖限制,并行潜力有限,硬件设计复杂,需要通过编译器优化和硬件调度减少依赖冲突。

线程级并行可利用多核处理器,隐藏内存访问等延迟,适合多任务场景,提升系统整体效率,但是线程切换有开销,存在数据竞争、死锁等问题,需要同步机制来保持数据一致性,并且过多线程会导致上下文切换频繁,降低性能。

向量级并行能够大幅提升数据处理效率,尤其适合科学计算、图形渲染、多媒体任务等数据密集型任务,减少指令执行次数,但是应用场景有限,要求数据具有规则结构,难以处理不规则或者离散数据;编程模型相对复杂,需要显式利用向量指令或者库函数。

完毕!