# 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 拓扑结构](/files/Uh739Rki37HN0kmJb4fH)

> 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](/files/dNtsAC8L7Bgm9BUesblK)

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

![图三：NUMA](/files/VZlYD7x9UdGyrjmK6l51)

在图一中，所有的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的负载。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://s3.shizhz.me/linux-sched/lb/lb-cpu-topo.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
