3. CPU架构基础:CPU核心架构(流水线、缓存、分支预测)、x86与ARM对比、CPU适合的任务类型
好,咱们进入第三讲。说实话,很多做AI芯片的同学,一上来就盯着NPU的算力、MAC阵列,结果CPU这边的基本功反而漏了。我见过不少项目,NPU算力堆得挺高,但CPU调度一塌糊涂,整个系统性能就是上不去。所以这一讲,咱们把CPU的老底儿翻一翻。
3.1 CPU核心架构:流水线、缓存、分支预测
CPU的核心架构,说白了就是三个关键词:流水线、缓存、分支预测。这三个东西决定了CPU能跑多快、能跑多稳。
3.1.1 流水线(Pipeline)
流水线这个概念,我习惯用一个比喻来解释——就像工厂里的流水线。一条指令的执行,可以拆成多个步骤:取指、译码、执行、访存、写回。如果不做流水线,那得等一条指令完全跑完,下一条才能开始。这效率,你想想看,得多低。
流水线一上,每个时钟周期都能发射一条指令。理想情况下,CPI(每指令周期数)可以接近1。但现实没那么美好,流水线会遇到三种“险境”:
- 结构险境:硬件资源不够用。比如取指和访存都要用内存总线,撞车了。
- 数据险境:下一条指令要等上一条的结果。比如A = B + C,然后D = A + E,D必须等A算出来。
- 控制险境:遇到分支跳转,不知道该取哪条指令了。
我在项目中遇到过最头疼的就是数据险境。有一次做视频编解码的加速,流水线深度到了12级,结果数据依赖一多,流水线频繁停顿,性能比预期的差了30%。后来加了转发通路(Forwarding),才把损失降下来。
3.1.2 缓存(Cache)
缓存这东西,说白了就是“快慢之间的缓冲”。CPU主频动辄几GHz,但内存的访问延迟还在几十纳秒级别。这个差距,你想想看,CPU等内存就像你等快递——急死个人。
现代CPU一般有三层缓存:
| 层级 | 大小 | 延迟 | 特点 |
|---|---|---|---|
| L1 | 32KB - 64KB | ~1ns | 分指令和数据缓存,每个核心独享 |
| L2 | 256KB - 1MB | ~3-5ns | 每个核心独享或两个核心共享 |
| L3 | 8MB - 32MB | ~10-20ns | 所有核心共享 |
嗯,这里要注意一个关键点:缓存一致性。多核CPU里,每个核心都有自己的L1/L2缓存。如果核心A改了某个数据,核心B不知道,那就会读到脏数据。我曾经在一个多核推理引擎项目里,就因为缓存一致性问题,排查了整整两天。最后发现是某个共享变量没有加volatile,编译器优化后,核心B一直读寄存器里的旧值。
3.1.3 分支预测(Branch Prediction)
分支预测,就是CPU猜一猜“if-else”到底走哪条路。猜对了,流水线继续跑;猜错了,流水线要清空重来,损失十几个时钟周期。
现代CPU的分支预测器已经非常智能了,比如两级自适应预测器、TAGE预测器。但有些模式,预测器就是搞不定。比如:
- 随机分支:if (rand() % 2) —— 预测器没法猜,正确率只有50%。
- 数据依赖的分支:比如二分查找,每次比较的结果都不同。
我个人的经验是:在NPU和CPU协同工作时,尽量把分支密集的代码放在CPU上。NPU的流水线很浅,甚至没有分支预测,遇到分支就直接停顿了。而CPU有强大的分支预测器,处理分支是它的强项。
3.2 x86与ARM对比
x86和ARM,这两家的恩怨情仇能写一本书。我这里只挑跟异构计算最相关的几点说。
| 对比维度 | x86 | ARM |
|---|---|---|
| 指令集 | CISC(复杂指令集),指令长度可变 | RISC(精简指令集),指令长度固定 |
| 功耗 | 高(典型TDP 15W-125W) | 低(典型TDP 2W-15W) |
| 性能 | 单核性能强,主频高 | 多核能效比高,主频适中 |
| 生态 | Windows/Linux,软件兼容性好 | 移动端、嵌入式、服务器(AWS Graviton) |
| 内存模型 | 强一致性(TSO) | 弱一致性(需要内存屏障指令) |
这里我想重点说一下内存模型。x86用的是TSO(Total Store Order),说白了就是“写操作不会乱序”。你写A再写B,别的核心看到的一定是先A后B。但ARM是弱一致性模型,写操作可能乱序。这意味着,如果你在ARM上写多核程序,不加内存屏障(DMB/DSB指令),数据同步就会出问题。
我记得有一次,把一个x86上的多线程程序直接移植到ARM上跑,结果数据老是错乱。查了半天,发现是ARM的弱一致性搞的鬼。后来在每个共享变量的写操作后面加了DMB指令,问题才解决。
3.3 CPU适合的任务类型
讲到这里,你可能会问:CPU到底适合干什么?我总结了三类:
- 控制密集型任务:比如任务调度、状态机、协议解析。这些任务分支多、逻辑复杂,CPU的分支预测和乱序执行正好派上用场。
- 小规模数据计算:比如标量运算、指针操作。NPU擅长的是大规模并行矩阵运算,但如果你只是算几个点的坐标变换,用CPU反而更快——省去了数据搬运的开销。
- 不规则数据结构处理:比如链表、树、图。这些数据结构访问模式随机,缓存命中率低,NPU的SIMD/SIMT架构根本跑不动。CPU的乱序执行和预取机制还能应付一下。
我个人的习惯是:在NPU推理之前,用CPU做预处理(比如图像解码、数据归一化);推理之后,用CPU做后处理(比如NMS非极大值抑制、结果排序)。NPU只负责最核心的矩阵运算,其他杂活全扔给CPU。
好了,这一讲就到这里。下一讲咱们聊聊NPU的架构,看看它跟CPU到底有什么本质区别。