写在前面:这是本系列的第十九篇。

背景回顾:并发编程的基础机制(线程、互斥锁、条件变量、信号量等)足够实现高效的并发/并行编程,但它们的正确使用完全由使用者负责——这毫无疑问是个过于乐观的假设。因此,编程语言的设计者们在不同的应用场景下,实现了许多特定的并发/并行编程机制(如 OpenMP、JS 的事件模型、Go 的协程等)。

与此同时,人们也开始发现,对于一些特别的应用场景(如图形渲染、深度学习),传统的 CPU 和多线程也许并不是最“高效”的解决方法。

本讲内容:我们将跳出纯软件的范畴,探讨 CPU 的物理局限,以及加速器(GPU)和人工智能背后的极客并行编程。

在这里插入图片描述

熟悉又陌生的 CPU (Spicy 🌶️)

什么是 CPU?

概念:无情执行指令的机器

  • 比如我们在 mini-rv32ima 中看到的 MiniRV32IMAStep() 循环。
  • 指令集(Instruction Set)是硬件能够理解的语言,是计算机执行操作的基础;汇编语言则是它的符号化表示。

实际的现代 CPU:Instruction-level Parallelism (指令级并行)

  • 现代 CPU 有多个核心,共享内存,并采用了极其复杂的弱内存模型 (Relaxed Memory Model)。为了保证正确性,必须引入 fence, atomic 等指令实现内存一致性控制。
  • 但你可能没有想到的是:其实你写的每一行单线程代码,一直都在(被自动地)并行编程!
  • 每个 CPU 核心内部,其实都藏着一个极度复杂的“硬件编译器”
    • 它在运行时进行动态的数据流分析和指令调度(乱序执行)。
    • 在顶级的服务器 CPU 上,可能同时有上千条指令在并行执行
    • 科普:IPC(Instruction Per Clock,每时钟周期指令数)。CPU 的最终算力 = IPC × 频率。

这意味着什么?

在“能效”和“性能”之间,CPU 坚定地选择了后者。

  • 跑得越快,浪费得越多。每一个门电路的翻转都会产生热量
  • CPU 里的那个硬件“编译器”(分支预测、乱序调度单元)会消耗巨量的电能。
  • 这些能量使得你的计算任务能“尽快完成”(极低的延迟),但这绝不等于“单位时间内完成尽可能多的计算”(吞吐量不高)。

Dark Silicon(暗硅时代)与功耗墙

P=C⋅V2⋅fP = C \cdot V^2 \cdot fP=CV2f

(功耗 = 电容 ×\times× 电压的平方 ×\times× 频率)

  • “功耗墙”: 纵使我们能把晶体管做得再小,把电路做得再大,散热和热功耗的物理极限也死死地限制了 CPU 的性能上限。芯片上很大一部分区域因为功耗限制,甚至不敢通电(这就是暗硅)。

面对功耗墙:寻找新出路

如何在降低电压 VVV 和频率 fff 的同时,用“芯片面积”换取“性能”?

出路 1:让一条指令能处理更多的数据

  • SIMD (Single Instruction, Multiple Data):
    • CPU 执行“一条指令”所浪费的解析和调度能量大致是固定的。
    • 如果这条指令能同时处理更多的数据,浪费的比例就越小!

出路 2:用更多、更简单的处理器

  • 发展多处理器系统、异构多处理器(大小核架构)。
  • 在同等的芯片面积下,如果把复杂的乱序执行单元砍掉,处理器越简单,能塞进去的数量就越多!

极致演进:SIMD 与数据并行

Single Instruction, Multiple Data (SIMD)

可以一次性让一条指令对连续的操作数做相同的运算
比如,用一条指令对 4 个 float 同时做 4 次乘法。

经典的 Intel MMX (MultiMedia eXtension) 技术就是为此而生的:

(奔腾 MMX 处理器的辉煌时代)

实现方式:增加一些“超大”的寄存器

  • 引入了 64-bit 的 mm 寄存器。
  • 增加了几十条专门实现 “packed register” 操作的指令。

军备竞赛:MMX →\rightarrow SSE →\rightarrow AVX →\rightarrow AVX-512

  • 寄存器宽度疯狂翻倍: 64 (mm) →\rightarrow 128 (xmm) →\rightarrow 256 (ymm) →\rightarrow 512 (zmm)。
  • 数据类型支持:int8/16/32 扩展到 float32float64
  • 更多的高级运算 (三操作数模式):
    • Shuffle (洗牌): c[i]=a[b[i]]c[i] = a[b[i]]c[i]=a[b[i]]
    • FMA (融合乘加): a×b+ca \times b + ca×b+c,一条指令瞬间完成!

为什么 SIMD 没能完全解决问题?

  • SIMD 指令依然是在 CPU 内部调度的,它必须参与到 CPU 复杂的缓存和动态流水线中。
  • 寄存器宽度不能做得太宽,否则遇到短数组就会造成极大的浪费,并行度终究有限。

我们实在是太想要性能了!只能选择横向扩展:

  • 单 CPU →\rightarrow 多 CPU →\rightarrow 大小核 CPU。

同等面积可以放置更多“高能效”计算单元:

  • 比如 Apple M1: 4 Performance + 4 Efficiency。
  • 核心 Trick:降低频率 = 降低所需电压(功耗与电压的平方成正比),从而实现极致的“热功耗分配”。

GPU 和 GPGPU 的崛起 (Spicy 🌶️)

生于娱乐,长于智能。

记得刚才的想法吗?

  • 用更多更简单的处理器!
  • 同等面积,处理器越简单,数量越多。
  • 我们甚至不需要处理器有完整的 CPU 指令集计算能力!

于是诞生了“领域专用加速器”:

  • ISP: Image Signal Processing (手机相机处理)
  • GPU: Graphics Processing Unit (图形渲染)
  • DPU: Data Processing Unit (网络/数据处理)

终于有一件事可以让专用处理器来做了:画图!

对屏幕像素 (i,j)(i, j)(i,j) 的计算,天生就是 “Massive Parallel” (海量并行) 的!

for (int y = 0; y < H; ++y) {
    for (int x = 0; x < W; ++x)
        putchar(f(x, y) ? '*' : ' ');
    putchar('\n');
}

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐