# Dragon 协议 —— 写更新(write-update)的代表，与 MESI 的写失效对立

> 前面 [MSI](/concepts/cache/msi.md)/[MESI](/concepts/cache/mesi.md)/[MOESI](/concepts/cache/moesi.md)/[MESIF](/concepts/cache/mesif.md) 都是 **write-invalidate（写失效）**：某核要写，先让别人的副本失效。Dragon 走另一条路——**write-update（写更新）**：某核写了，不失效别人，而是把新值**广播更新**到其他持有者的缓存里。理解这条对立路线，才算看全一致性协议的设计空间。


> 家族对比见 [comparison.md](/concepts/cache/comparison.md)。

## 一、写失效 vs 写更新：两种根本思路

同一件事——"核 A 改了一行、核 B 也持有它"——两派做法相反：

```plantuml
@startuml
skinparam shadowing false
skinparam sequence {
  ParticipantBackgroundColor #E3F2FD
  ParticipantBorderColor     #1976D2
  ArrowColor                 #00838F
  LifeLineBorderColor        #90A4AE
}
participant "核A(写者)" as A
participant "核B(持有者)" as B
== 写失效 (MESI 等)：让 B 的副本作废 ==
A -> B : Invalidate（你那份作废）
B -> B : 标记 Invalid
note over A,B : B 下次要用得重新拉取；\n若 B 其实还想用 → 一次 miss
== 写更新 (Dragon)：把新值推给 B ==
A -> B : Update（新值 = X'）
B -> B : 就地更新，仍可用
note over A,B : B 无需 miss 就能读到新值；\n但若 B 根本不再用 → 白推一次
@enduml
```

- **写失效**：写者只发一个"失效"信号（便宜），代价转移到读者——读者若还要用就得 miss 重取。**适合"写多读少、或写后别人不再读"**的模式（避免无用更新）。
- **写更新**：写者把新数据推给所有持有者（每次写都有广播成本），但读者永远命中。**适合"写后其他核马上要读"**（生产者写、多消费者立刻读）。

现实中**写失效成为主流**（MESI 族），因为写更新在"写者反复写、读者早不看了"时会疯狂广播无用更新，浪费总线带宽。Dragon/Firefly 是写更新的经典教学协议。

## 二、Dragon 的四种状态

Dragon **没有 Invalid 态**（它不靠失效，靠更新），四态描述"是否独占 / 是否脏 / 是否与内存一致"：

| 状态 | 全称 | 独占? | 和内存一致? | 说明 |
|------|------|:---:|:---:|------|
| **E** | Exclusive-clean | 是 | 是 | 只有本核有，且干净 |
| **Sc** | Shared-clean | 否 | 是 | 多核共享，本核不负责写回 |
| **Sm** | Shared-modified | 否 | 否（脏）| 多核共享，**本核是 Owner，负责写回**（类似 MOESI 的 O）|
| **M** | Modified | 是 | 否（脏）| 只有本核有，且脏 |

> 没有 I：一行要么不在缓存里（根本没这条目），要么就是上面四态之一持有着有效数据——写更新保证共享者手里的数据一直是新的，不需要"失效"这个态。

## 三、状态机（PlantUML）

先约定术语——分**处理器侧(Pr)** 和**总线侧(Bus)** 两类信号（注意 Dragon 是写更新协议，用 `BusUpd` 而非写失效族的 `BusRdX`/`BusUpgr`）：

| 缩写 | 英文全称 | 含义 |
|------|----------|------|
| `PrRd` | **Pr**ocessor **Rd**（read）| 本核处理器发起的**读** |
| `PrWr` | **Pr**ocessor **Wr**（write）| 本核处理器发起的**写** |
| `BusRd` | **Bus** **Rd**（read）| 总线上别的核发起的读请求 |
| `BusUpd` | **Bus** **Upd**ate | 写者把**新值**广播到总线，更新其他核的副本（写更新的核心，取代写失效的"作废"）|
| `Sh` | **Sh**ared line（信号线）| 总线上一根专用线：是否**还有别的核持有**该副本（决定写完是变 M 还是 Sm）|

```plantuml
@startuml
skinparam shadowing false
hide empty description
skinparam state {
  BackgroundColor<<e>>  #BBDEFB
  BorderColor<<e>>      #1976D2
  BackgroundColor<<m>>  #FFCDD2
  BorderColor<<m>>      #C62828
  BackgroundColor<<sc>> #C8E6C9
  BorderColor<<sc>>     #388E3C
  BackgroundColor<<sm>> #FFE0B2
  BorderColor<<sm>>     #EF6C00
}
state "Exclusive(E)" as E <<e>>
state "Modified(M)" as M <<m>>
state "Shared-clean(Sc)" as Sc <<sc>>
state "Shared-modified(Sm)" as Sm <<sm>>
[*] --> E  : PrRd 首次读且无人共享(Sh=0)
[*] --> Sc : PrRd 首次读但有人共享(Sh=1)
E  --> M  : PrWr (独占，静默变脏)
E  --> Sc : 嗅探 BusRd (别人来读→变共享)
M  --> Sm : 嗅探 BusRd (别人来读→自己成 Owner，转发)
M  --> M  : PrRd/PrWr 命中
Sc --> Sm : PrWr 且有人共享(Sh=1)\n发 BusUpd 广播新值，自己成 Owner
Sc --> M  : PrWr 且无人共享(Sh=0)
Sc --> Sc : 嗅探 BusUpd (收到更新，就地刷新，仍是 Sc)
Sm --> Sm : 嗅探 BusUpd / 给新读者转发
Sm --> M  : PrWr 且无人共享
Sm --> Sm : PrWr 且有人共享(发 BusUpd)
note bottom of Sm
  Sm = Owner：脏且共享，负责写回
  （对应 MOESI 的 Owned）
end note
@enduml
```

要点：

- **写共享行时发 `BusUpd`**（更新别人）而不是 `Invalidate`（作废别人）——这是和 MESI 的本质分野。
- **`Sh` 信号线**决定写完是变 M（独占，没人共享）还是 Sm（还有共享者，我当 Owner）。
- **Sm 类似 MOESI 的 Owned**：脏 + 共享 + 负责写回。

## 四、为什么现代 CPU 基本不用纯写更新

| | 写失效 (MESI 族) | 写更新 (Dragon) |
|---|---|---|
| 每次写共享行 | 发一次失效（小）| 广播新数据（大，且可能无人再读）|
| 读者体验 | 可能 miss 重取 | 总是命中 |
| 写者反复写同一行 | 第一次失效后别人已 I，后续写无广播 | **每次写都广播**，若读者早不看 → 大量无用流量 |
| 主流采用 | ✅ 是（Intel MESIF、AMD MOESI）| ❌ 基本不用（教学/历史）|

**决定性缺点**：写更新在"生产者高频写、消费者读得慢或不读"时，把总线塞满无用的更新广播。写失效则"一次作废，之后随便写"。所以真实 CPU 选写失效；Dragon/Firefly 主要活在体系结构教材里，作为设计空间的另一极。

> 少数场景写更新有价值（写后立即被多核读、且更新数据小），因此也有**混合/自适应**协议根据访问模式在两者间切换——但通用 CPU 的默认是写失效。

## 五、对开发者的意义

伪共享在**写更新**协议下表现不同（不是 ping-pong 失效，而是 ping-pong 更新广播），但**规避手段仍然一样**：cache line 对齐/填充（见 [mesi.md](/concepts/cache/mesi.md) 第五节）。由于你手上的 x86 服务器几乎都是写失效（MESI/MOESI/MESIF），Dragon 主要用于**理解协议设计的两条路线**，实践中按 MESI 族思路优化即可。

## 六、和本仓库其他文档

> 写失效族：起点 [msi.md](/concepts/cache/msi.md)、主协议 [mesi.md](/concepts/cache/mesi.md)、[moesi.md](/concepts/cache/moesi.md)、[mesif.md](/concepts/cache/mesif.md)；家族对比 [comparison.md](/concepts/cache/comparison.md)；伪共享观测 `perf c2c` 见 [mesi.md](/concepts/cache/mesi.md) 第六节。
