协议变种:MOESI、MESIF 与写更新路线 Dragon
MESI 是"写失效"家族的主协议,但也不是终点。硬件继续演化出两个"第五态"变种——MOESI(AMD 的 O,允许脏且共享)与 MESIF(Intel 的 F,指定共享行的唯一应答者);另外还有一条与写失效对立的路线 Dragon(写更新)。这篇把三个方向放到一起看:各自解决哪个性能痛点、代价是什么、谁在用。
家族起点 msi,横向对比 comparison。
一、MOESI:+Owned,让"脏且共享"成立(AMD)
1.1 五种状态
| 状态 | 全称 | 和内存一致? | 可有其他副本? | 谁负责供数/写回 |
|---|---|---|---|---|
| M | Modified | 否(脏) | 否 | 本核 |
| O | Owned | 否(脏) | 是 | 本核(唯一 Owner) |
| E | Exclusive | 是 | 否 | — |
| S | Shared | 见下 | 是 | — |
| I | Invalid | — | — | — |
O 是 MOESI 的灵魂:它表示"这行是脏的(比内存新),但我允许别的核也持有共享副本,而我作为 Owner 负责在需要时把最新数据供给它们、并最终负责写回内存"。
注意 MOESI 里 S 的语义放宽了:MESI 的 S 一定和内存一致(干净);MOESI 的 S 副本可能对应一个处于 O 的脏主本——即 S 可能"脏",但持有者不负责写回,写回责任在那个 O。
1.2 O 解决什么:省掉写回内存
对比"核 A 改过某行(M),核 B 现在要读":

在多核频繁读被别人改过的数据(生产者-消费者、共享数据结构)的场景,MOESI 靠 cache-to-cache 转发 + 延迟写回,显著减少内存带宽压力——这对内存带宽紧张的多核服务器很关键。
1.3 状态机差异
只画出与 MESI 的关键差异部分(M/E/S/I 的本核迁移同 mesi):

MESI vs MOESI 一句话区别:
- MESI:一行要么"脏且独占(M)",要么"干净可共享(S)"。脏的行被别人读时,必须先写回内存,才能变共享。
- MOESI:允许"脏且共享(O + 其他核 S)"。脏的行被别人读时,Owner 直接转发、不写内存,写回推迟到 Owner 这行被驱逐时。
省下的就是那一次(乃至多次)到内存的写。AMD 系列(Opteron 以来,直到 Zen 的 Infinity Fabric)主要采用这一族,优势场景正是核多、共享脏数据频繁、内存带宽是瓶颈。
二、MESIF:+Forward,指定唯一应答者(Intel)
2.1 五种状态
| 状态 | 全称 | 和内存一致? | 可有其他副本? | 收到别人读请求时 |
|---|---|---|---|---|
| M | Modified | 否(脏) | 否 | 自己供数 |
| E | Exclusive | 是 | 否 | — |
| S | Shared | 是(干净) | 是 | 保持沉默,不应答 |
| I | Invalid | — | — | — |
| F | Forward | 是(干净) | 是 | 由我负责应答/转发 |
F 是一种特殊的 S:一行被 N 个核共享时,其中恰好一个副本处于 F,其余处于 S。F 是这组共享副本的"指定发言人"——新的读者来要数据时,只有 F 那个核负责转发,S 的核一律沉默。
2.2 F 解决什么:避免多副本"抢答"
设想一行被 3 个核共享,第 4 个核来读它:

没有 F 的困境:MESI 里所有共享副本都是对等的 S。新读者广播 BusRd 时——若约定"大家都应答",会在互联上造成冗余甚至冲突;若约定"都不应答、去读内存",则放弃了更快的 cache-to-cache 转发。F 指定唯一应答者,既避免抢答,又能用缓存间转发(比读内存快)。这在多核、共享只读数据多(如大量核读同一份配置/查找表)的场景收益明显——正是数据中心多路 Intel 服务器的典型负载。
2.3 F 的迁移规则
关键规则:F 通常"漂"给最近取得副本的那个核(most-recent holder),这样负担不会一直压在同一个核上。

细节:当 F 副本被驱逐(evict)时,如果还有其他 S 副本存在,一致性目录/协议会挑一个 S 提升为 F,或让后续读者回落到读内存——实现因微架构而异。
2.4 MESIF vs MOESI:两个"第五态"解决不同问题
它们都在 MESI 上加一个态,但方向不同,容易混:
| MESIF(Intel) | MOESI(AMD) | |
|---|---|---|
| 新增态 | F(Forward) | O(Owned) |
| F/O 的数据 | 干净(和内存一致) | 脏(比内存新) |
| 解决的问题 | 多个干净共享副本里,指定唯一应答者,避免抢答 | 让脏数据可共享,转发时不必写回内存 |
| 省掉的开销 | 冗余的总线应答 / 读内存 | 写回内存的那次写 |
| 典型互联 | QPI / UPI | Infinity Fabric |
一句话:F 管"共享干净行由谁应答",O 管"脏行能否共享且免写回"。理论上可以都要(有的设计接近 MOESIF),但商用上 Intel 走 MESIF、AMD 走 MOESI。
三、Dragon:写更新路线,与写失效对立
3.1 写失效 vs 写更新:两种根本思路
同一件事——"核 A 改了一行、核 B 也持有它"——两派做法相反:

- 写失效:写者只发一个"失效"信号(便宜),代价转移到读者——读者若还要用就得 miss 重取。**适合"写多读少、或写后别人不再读"**的模式(避免无用更新)。
- 写更新:写者把新数据推给所有持有者(每次写都有广播成本),但读者永远命中。适合"写后其他核马上要读"(生产者写、多消费者立刻读)。
现实中写失效成为主流(MESI 族),因为写更新在"写者反复写、读者早不看了"时会疯狂广播无用更新,浪费总线带宽。Dragon/Firefly 是写更新的经典教学协议。
3.2 Dragon 的四种状态
Dragon 没有 Invalid 态(它不靠失效,靠更新),四态描述"是否独占 / 是否脏 / 是否与内存一致":
| 状态 | 全称 | 独占? | 和内存一致? | 说明 |
|---|---|---|---|---|
| E | Exclusive-clean | 是 | 是 | 只有本核有,且干净 |
| Sc | Shared-clean | 否 | 是 | 多核共享,本核不负责写回 |
| Sm | Shared-modified | 否 | 否(脏) | 多核共享,本核是 Owner,负责写回(类似 MOESI 的 O) |
| M | Modified | 是 | 否(脏) | 只有本核有,且脏 |
没有 I:一行要么不在缓存里(根本没这条目),要么就是上面四态之一持有着有效数据——写更新保证共享者手里的数据一直是新的,不需要"失效"这个态。
3.3 状态机与信号
先约定术语——分处理器侧(Pr) 和总线侧(Bus) 两类信号(Dragon 是写更新协议,用 BusUpd 而非写失效族的 BusRdX/BusUpgr):
| 缩写 | 含义 |
|---|---|
PrRd | 本核处理器发起的读 |
PrWr | 本核处理器发起的写 |
BusRd | 总线上别的核发起的读请求 |
BusUpd | 写者把新值广播到总线,更新其他核的副本(写更新的核心,取代写失效的"作废") |
Sh | 总线上一根专用信号线:是否还有别的核持有该副本(决定写完是变 M 还是 Sm) |

要点:
- 写共享行时发
BusUpd(更新别人)而不是Invalidate(作废别人)——这是和 MESI 的本质分野。 Sh信号线决定写完是变 M(独占,没人共享)还是 Sm(还有共享者,我当 Owner)。- Sm 类似 MOESI 的 Owned:脏 + 共享 + 负责写回。
3.4 为什么现代 CPU 基本不用纯写更新
| 写失效 (MESI 族) | 写更新 (Dragon) | |
|---|---|---|
| 每次写共享行 | 发一次失效(小) | 广播新数据(大,且可能无人再读) |
| 读者体验 | 可能 miss 重取 | 总是命中 |
| 写者反复写同一行 | 第一次失效后别人已 I,后续写无广播 | 每次写都广播,若读者早不看 → 大量无用流量 |
| 主流采用 | ✅ 是(Intel MESIF、AMD MOESI) | ❌ 基本不用(教学/历史) |
决定性缺点:写更新在"生产者高频写、消费者读得慢或不读"时,把总线塞满无用的更新广播。写失效则"一次作废,之后随便写"。所以真实 CPU 选写失效;Dragon/Firefly 主要活在体系结构教材里,作为设计空间的另一极。
少数场景写更新有价值(写后立即被多核读、且更新数据小),因此也有混合/自适应协议根据访问模式在两者间切换——但通用 CPU 的默认是写失效。
四、一页速查与开发者视角
| 新增态 | 状态数据 | 解决什么 | 代价 | 谁在用 | |
|---|---|---|---|---|---|
| MOESI | O(Owned) | 脏 | 脏行共享读免写回内存 | 状态多、写回责任复杂 | AMD |
| MESIF | F(Forward) | 干净 | 多副本抢答/都读内存 | 状态多、F 转移逻辑 | Intel |
| Dragon | 四态(无 I) | 混合 | 写后读者永远命中 | 每次写都广播 | 教学/历史 |
对开发者:三个变种优化都是硬件内部的数据流转,不改变应用层准则——伪共享照样用 cache line 对齐/填充规避(见 mesi-false-sharing),定位照样用 perf c2c(见 mesi-false-sharing 第四节)。你几乎不会在代码里"感知"到 O/F 的存在;Dragon 则主要用于理解设计空间的两极。
一句话总结:MESI 的 E 省"首写广播";MOESI 的 O 省"写回内存";MESIF 的 F 省"应答抢答";Dragon 换了一条"写更新"的路,但广播更新太贵,留在教材里。真实 CPU 是写失效 + 目录的天下。
相关:起点 msi;总纲 mesi(机制 mesi-protocol);对比 comparison;伪共享与观测 mesi-false-sharing。