Advanced Architecture Parallelism is All You Need

这篇文章把我在 Advanced Architecture (UCSD CS240A) 里学到的核心机制,统一放到一条主线上:架构设计的第一性原理之一是“并行性”

1. What is Parallelism?

  • ILP(Instruction-Level Parallelism):同一线程内,让更多独立指令并发执行(流水线、超标量、OoO)。
  • MLP(Memory-Level Parallelism):同一线程内,让多个 cache miss 并发在路上(non-blocking cache、MSHR、prefetch)。
  • TLP(Thread-Level Parallelism):多个线程/核并发(SMT、多核)。

这三类并行里,最“容易被浪费”的资源是:
(1)前端取不到指令、(2)后端被依赖/长延迟卡住、(3)访存把一切拖慢。

2. Pipeline

经典 5 级流水:IF → ID → EX → MEM → WB
直觉上它是“时间并行”:同一时刻不同指令在不同 stage。

2.1 Hazard

  • 数据相关:RAW / WAR / WAW
  • 结构冲突:端口/执行单元不够
  • 控制相关:分支把取指路径切断

最关键的观察是:流水线把 CPI 往 1 拉,但 hazard 会把bubble塞回去
所以后续的 OoO / predictor / cache 其实都在对付“bubble”。

3. Branch

分支的本质问题:它让“将来执行哪条路径”不确定,前端只能干等。
所以现代 CPU 几乎默认投机执行:先猜、先跑,错了再回滚

3.1 Branch Predictor

  • 2-bit 饱和计数器:稳定但能修正短期波动
  • gshare:全局历史(ghistory) xor PC
  • 2-level local history: 根据local pattern (pc -> local pattern寄存器组) 对应的计数器预测
  • tournament:用 chooser 在不同预测器之间动态选择
  • TAGE:现代CPU常用的

3.2 BTB / RAS

  • BTB(Branch Target Buffer):给出目标地址(taken 时直接改 PC),并常用于快速识别分支
  • RAS(Return Address Stack):专门优化 return(比一般 predictor 更准)

4. Out of Order

如果说流水线是“stage 并行”,超标量是“同周期多发射(issue)”,那 OoO 的核心是:

执行可以乱序,提交必须顺序。
这样既能把独立指令提前跑,又能保证程序语义干净(处理exception、可回滚投机)。

4.1 rename → schedule → execute → commit

flowchart TD

  A[Fetch / Decode] --> B[Rename<br/>Map Table + Free List]

  B --> C[Dispatch<br/>IQ/RS + ROB alloc]

  C --> D[Issue<br/>Ready select]

  D --> E[Execute<br/>FUs / LSU]

  E --> F[Writeback<br/>bypass + wakeup]

  F --> G[Commit in order<br/>ROB retire]

  G --> H[Architectural state updated]

OoO 的“并行能力”理解成由两个东西决定:

  1. 窗口有多大(ROB/IQ/LSQ 容量、前端供给能力)
  2. 窗口里能否把假相关去掉(寄存器重命名)

4.2 Register Renaming

  • RAW 是真依赖(数据必须等)
  • WAR/WAW 是名字冲突(“写谁”的问题),可以靠重命名消掉

典型实现(MIPS R10K):

  • Map Table:architectural reg → physical reg
  • Free List:可用物理寄存器池
  • ROB:按程序顺序记录投机结果,用于顺序提交与回滚

4.3 ROB

ROB记录目前已经完成的指令,按照顺序提交,如果前面的指令还在运行,则等待。

5. Cache + Memory System

单线程性能经常不是被算力限制,而是被访存延迟与带宽限制。
Cache 的主旨是:利用局部性让“慢内存看起来更快”;同时用并发 miss 把等待摊薄。

5.1 Cache

  • Offset 选 block 内字节
  • Index 选 set
  • Tag 做命中比较
Physical Address (example)
+-------------------+-----------+--------+
|        Tag        |   Index   | Offset |
+-------------------+-----------+--------+

调参数(block size / associativity / sets),本质是在做取舍:

  • block 大:更吃空间局部性,但可能增加冲突/带宽压力
  • associativity 大:冲突少,但 hit 可能更慢、更耗能

5.2 Non-blocking Cache

阻塞 cache 的问题是“一次 miss,全体停工”。
Non-blocking cache 通过 MSHR 记录未完成 miss,使得:

  • Hit-under-miss:miss 期间仍可服务 hit
  • Miss-under-miss:允许多个 outstanding miss

这就是把“等内存”的时间变成“并行的在路上”。

5.3 Prefetch

预取的本质是猜“未来可能要用”,提前拉进来,减少未来 miss 的可见延迟。
但它也会抢带宽、污染 cache。这里有多种hardware和software的方式来预测。比如miss了这一行就把这一行和下一行一起拉进去。

6. MOESI

当从单核走向多核,共享内存下每个核都有自己的 cache。
MOESI 是 MESI 的扩展:多了一个 O(Owned) 状态,用来减少回写压力、提升共享脏数据的效率。

6.1 5 Stage for MOESI

  • M (Modified):只在本 cache,已修改,内存是旧的,本 cache 负责提供最新数据
  • O (Owned):数据可能被多个 cache 共享,但本 cache 是“owner”,内存仍是旧的,owner 负责对外提供数据(脏共享)
  • E (Exclusive):只在本 cache,未修改,内存一致;若写可直接转 M(无需总线广播)
  • S (Shared):多个 cache 共享,未修改,内存一致;写需要使其他副本失效
  • I (Invalid):无效

6.2 Why we introduce O?

没有 O 的 MESI 下,如果一份数据被修改后又要被其他核读:

  • 常见路径:M → (被迫)写回内存 → 其他核再从内存拿
    这会把“共享”变成大量回写。

有 O 后:

  • 修改者可以把数据以 O 的形式分享出去:不必立刻写回内存
  • 其他核拿到 S,owner 仍负责提供最新数据
    这在共享读多、写少的场景能显著减少内存流量。

7. TLB

OS设计的虚拟内存(virtual memory),带来一个新开销:每次访存都要把 VA → PA。
TLB 是page的cache。

7.1 VIPT

在 VIPT (virtual index, physical tag) L1 cache 中:

  • VA 的 page offset 部分作为 Index+Offset 去读 cache set(因为 page offset 翻译前后不变)
  • 同时用 TLB 把 VA 翻成 PA,得到 Physical Tag
  • 最后用 Physical Tag 和读出的 tag array 比较,确认命中

这样子就可以两边并行了。

7.2 Example I

假设 page size = 4KB,则 page offset = 12 bits(低 12 位不变):

Virtual Address
+---------------------+----------------+
|        VPN          |  page offset   |
+---------------------+----------------+
                         |      |
                         |      +--> Offset (within cache block)
                         +----------> Index (select cache set)

TLB 做的是 VPN → PPN:

TLB:  VPN  -->  PPN

Physical Address 变成:

Physical Address
+---------------------+----------------+
|        PPN          |  page offset   |
+---------------------+----------------+
|        Tag          |  Index+Offset  |  (in VIPT L1)

关键约束(VIPT 能并行的条件)

L1 cache 的 Index bits 必须完全落在 page offset 里
否则不同虚拟页可能映射到同一个物理页的同一行却落到不同 set(alias/synonym)

7.3 Example II

  • page size = 4KB → offset = 12 bits
  • L1 line size = 64B → block offset = 6 bits
  • 那么可用于 index 的 page-offset bits 还有:12 – 6 = 6 bits
  • index bits = 6 → sets = 64
  • 若 8-way:L1 size = sets × ways × line = 64 × 8 × 64B = 32KB

8. SMT: Simultaneous Multithreading

本课程教授的杰作

单线程会因为分支错预测、长延迟 load、资源冲突而产生大量bubble。
SMT 的思路是:同周期从多个线程取指/发射,让一个线程的bubble被另一个线程的就绪指令填掉

8.1 Strategies

  • Coarse-grain:大 miss 才切换,切换成本高(可能要 flush)
  • Fine-grain:每周期轮换线程,单线程延迟变大但吞吐稳
  • SMT:多线程 + superscalar,追求吞吐最大化

8.2 Resources

SMT 的核心矛盾是:多个线程争抢同一套前端/后端资源
如果取指策略不好,很容易出现一种情况:某个线程因为 cache miss/长延迟指令卡住,但前端还在疯狂给它喂指令,结果这些指令堆在队列里占着 ROB/IQ/LSQ,把“能跑的线程”也挤死。

I-count 的思路非常直接:

优先从 “in-flight 指令数更少” 的线程取指。
in-flight(或称占用度)可以用该线程当前占用的 ROB 项数、IQ 项数等来近似衡量。

Why?

  • 少喂“已经塞满/可能在等内存”的线程
    如果一个线程 in-flight 很多,往往说明它已经占了大量窗口资源,但并没有及时退休(常见原因:长延迟 load、分支错预测恢复、资源瓶颈)。
  • 多喂“更可能立刻产生 useful work” 的线程
    in-flight 少的线程,意味着它没占多少资源,给它更多取指机会,能更快把执行单元填满、提高整体 IPC/吞吐。

Example

  • Thread A:刚触发一个 LLC miss,ROB 里堆了一堆依赖它的指令(in-flight 很高,但大概率短期无法退休)
  • Thread B:工作集命中 L1/L2,很多指令能快速执行并退休(in-flight 低,但“喂一点就能干活”)

I-count 会倾向多取 Thread B,避免让 Thread A 继续膨胀占资源,从而把 vertical waste(等待)horizontal waste(槽位闲置) 都压下去。

9. Energy

加宽发射、加深窗口、堆更复杂的预测器/缓存结构,几乎都会把功耗和能耗推上去,而现代 CPU 往往首先被 功耗墙(power wall)/温度/供电约束。

9.1 Math Format

功耗分为两类动态功耗静态功耗.

  • 动态功耗大致满足:
    $$
    P_{dyn} \propto C \cdot V^2 \cdot A \cdot f
    $$
    其中 (C) 是等效电容,(V) 电压,(A) 活动因子(有多少电路在翻转),(f) 频率。
    直觉:降电压收益极大(平方项),而提高频率往往需要提高电压,导致能耗暴涨。

不过随着制程的进步,静态功耗逐渐变大,小的晶体管也有漏电问题。

9.2 Save Energy in Parallelism

从并行的视角,很多省电机制其实是在问:哪些并行是“错的/没用的”?

  • DVFS(动态电压频率调节)
    负载低、或瓶颈在内存时,提高频率意义有限(前端/后端更多在等),这时降频降压常常是最划算的。
  • Clock gating / Power gating
    • clock gating:不让某些模块翻转(降低活动因子 (A))
    • power gating:直接断电(减少静态泄漏 + 动态),但唤醒有延迟/状态恢复代价
  • “推测是耗电的”(非常重要的架构直觉)
    分支预测错了,做了大量无效工作:取指、译码、rename、进队列、执行、写回……全变成了“发热”。
    所以很多设计会用类似“confidence/节流”的策略:预测不稳时降低前端攻击性,少取一点,减少错的并行。

9.3 Summary for Energy

  • 更大的 OoO window:更能隐藏延迟,但 wakeup/select、CAM/比较、bypass 网络会显著增加功耗
  • 更激进的 prefetch:可能降延迟,但也可能占带宽、污染 cache、带来大量“无效搬运”
  • SMT:吞吐更高,但竞争更激烈;对某些 workload,额外线程会把 cache/TLB 压力推大,反而能耗更差

10. Security: Leaking from Parallelism

当我们为了并行而引入:

  • 投机执行(分支预测 + OoO)
  • 多级 cache / TLB / prefetch
  • 共享资源(SMT、多核共享 LLC)

就会出现一个长期副作用:程序的“微架构痕迹”可能暴露本不该暴露的信息

10.1 Side Channel Attack

Prime+Probe(No need for shared page)

  • Prime:攻击者先把某些 cache sets 填满(建立“占位”)
  • Victim runs:受害者访问内存,可能把攻击者占位 line 挤出去
  • Probe:攻击者再访问自己那批地址测时间 → 推断哪些 set 被挤了
    优点:适用面广(不需要共享页)。缺点:噪声相对更大。

Flush+Reload(Need for shared page/line)

  • Flush:把共享 line 从 cache 清掉(例如 clflush)
  • Victim runs:受害者若访问该 line,会把它带回 cache
  • Reload:攻击者 reload 测时间 → 命中则说明 victim 访问过
    优点:信号强、分辨率高。限制:通常需要共享内存页(例如共享库页)以及一些缓存包含性/系统条件。

10.2 Spectre: Predicting World

Spectre V1

1) 训练预测器:让 CPU “习惯”某个分支方向(例如 if (x < bound) 走真)
2) 触发越界条件:真实条件为假,但 CPU 在投机阶段仍沿着“训练出来的方向”执行了越界访问
3) 用 cache 痕迹外泄:把 secret 编码到 cache(例如访问 array2[secret*4096]),之后攻击者用测时读出 secret

Spectre V2

1) 训练BTB:让 CPU “习惯”某个分支方向
2) 触发越界条件:跳转这个方向到hack代码.



📢 转载须知



本文作者:Tianci Hou


本文标题:《Advanced Architecture Parallelism is All You Need》


本文链接:https://blog.tiancihou.me/advanced-architecture-parallelism-is-all-you-need/




CC BY-NC-SA


版权声明:本文采用 CC BY-NC-SA 4.0 许可协议。转载请务必保留以上署名及原始链接。



暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
下一篇