# 2.8.2 CPU的拓扑结构

CPU的拓扑结构可以理解为在硬件层面上多个CPU的排列方式, 对于运行的任务而言，拓扑结构决定了其从一个CPU迁移到另一个CPU将要付出的代价。在详细讨论拓扑结构之前，我们先介绍几个与CPU相关的概念：

* Socket: 对应主板上CPU插槽
* Core: 独立的一个执行单元，硬件上实现真正并行计算的组件，例如我们说一个CPU是“四核八线程”，其中每个核就对应一个Core
* Hyper-Threading: 逻辑上的一个执行单元，多个超线程共享一个核，例如“四核八线程”，就是每两个线程共享一个核。由于多个超线程共享一个物理执行单元，因此超线程并不是硬件层面上独立的并行计算单元，只是在OS级别的并发单元。

例如本人机器的CPU 型号是 Intel i5-8265U, 一个物理处理器，4核8线程，其拓扑结果如下：

![图一：Intel i5-8265U 拓扑结构](https://640510796-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Me-3_JXLYv-4hrEXyDb%2Fuploads%2FJYoJjGu8i9zJ1aj5ImD3%2Fcpu_topo.png?alt=media\&token=17486d56-8714-4d6d-b7b7-c5ee2493b081)

> Linux 中通过命令 `lstopo` 即可查看CPU 的拓扑结构，通过命令 `hardinfo` 可以查看系统的各类硬件信息。

> 这里我们并没有提及CPU, CPU是最早出现的概念，早期与Socket是1:1的对应关系，但发展到现在基本已经是一个泛化和抽象的概念了，严格地说已经变成了一个日常用语。但在OS层面，CPU依然表示系统的一个执行单位，每个线程都会对应一个系统CPU. 例如上图中，系统会显示有8个CPU.
>
> Note: 系统CPU 的信息记录在 `/proc/cpuinfo` 中，可以通过 `cat` 命令查看。

通过该图可以发现：P#0与P#4就是两个超线程，并且共享Core L#0. 还可以看出同一个Core中的所有超线程共享所有的CPU缓存（L1, L2, L3），而不同的Core 只共享L3。

对多核CPU而言，如何访问主存（RAM）也是一个重要议题，主要涉及到两种拓扑结构：

* SMP(Symmetric Multiprocessing) 被译为[对称多处理](https://en.wikipedia.org/wiki/Symmetric_multiprocessing), 简单说就是每个CPU在访问主存时的代价都是一样的，主存作为全局的一个资源被所有CPU所共享。示意图如下：

![图二：SMP](https://640510796-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Me-3_JXLYv-4hrEXyDb%2Fuploads%2FtDdgpN71CMx3eHMJEzd7%2Fcpu_smp.png?alt=media\&token=e96c4883-de23-4aea-b729-a1da98c02185)

* NUMA(Non-uniform Memory Access) 被译为[非均匀访存模型](https://en.wikipedia.org/wiki/Non-uniform_memory_access), 系统将CPU分为不同的集群，每个集群叫着一个节点（Node），每个节点都有自己的本地内存（Local RAM），CPU 可以访问任何节点的内存，但访问本地内存的速度要远高于访问非本地内存的速度。示意图如下：

![图三：NUMA](https://640510796-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Me-3_JXLYv-4hrEXyDb%2Fuploads%2FwIRVkuJpbB4vOFyMsNhA%2Fcpu_numa.png?alt=media\&token=ce076cf8-b16d-42fd-917a-bede12c8430d)

在图一中，所有的CPU 都处在一个NUMA节点中。

当CPU访问数据时，只有当所有的缓存（L1, L2, L3）都没有命中时才会访问主存（RAM），如果主存也没有数据则会发生缺页中断（page fault）并从磁盘加载数据。通过上面的介绍我们知道：从缓存到主存，不同的CPU能够共享的层级是不一样的。也就是说，当迁移任务时，选择不同的目标CPU 就意味着要付出不同的代价。通常而言我们认为：

1. 同一个超线程集（Hyperthreaded Set）中的CPU能够共享所有的缓存、主存，所以将任务在不同线程之间迁移可以认为没有性能损耗
2. SMP架构或者同一个NUMA节点内，不同的Core 可能只会共享部分缓存，但访问主存的时间都是一样的。因此如果对任务进行跨核迁移的话，新CPU的缓存将需要重新warm up
3. NUMA架构下，不同节点（Node）会有自己的本地主存（Local RAM），CPU访问其他节点的主存比访问本地主存需要更多时间。因此不到万不得已，调度器不会跨节点地进行任务迁移。

从同一个超线程集合，到NUMA中跨节点的CPU,我们可以认为CPU之间的“物理距离”在不断增加，这种“物理距离”形成了一种层级结构。根据上述分析可以知道，当调度器做任务迁移时，所选择的目标CPU越“近”则性能损耗就越小。因此内核需要某种方式来有效地组织CPU,这种组织方式能够根据“距离”将CPU分组，并形成合理的层级结构，使得调度器能够高效调整各个CPU的负载。
