# MESIF 协议 —— 增加 Forward 状态，指定唯一应答者（Intel）

> MESIF 在 [MESI](/concepts/cache/mesi.md) 基础上加了 **F(Forward)** 状态。它要解决的不是"写回内存"（那是 [MOESI](/concepts/cache/moesi.md) 的 O 干的事），而是**多个核共享同一行时，谁来响应新的读请求**——避免多个持有者同时应答造成总线/互联上的冗余流量。Intel 的 QPI/UPI 互联采用这一族。


> 家族起点见 [msi.md](/concepts/cache/msi.md)，横向对比见 [comparison.md](/concepts/cache/comparison.md)。

## 一、五种状态

| 状态 | 全称 | 和内存一致? | 可有其他副本? | 收到别人读请求时 |
|------|------|:---:|:---:|------|
| **M** | Modified | 否（脏）| 否 | 自己供数 |
| **E** | Exclusive | 是 | 否 | — |
| **S** | Shared | 是（干净）| 是 | **保持沉默，不应答** |
| **I** | Invalid | — | — | — |
| **F** | **Forward** | 是（干净）| 是 | **由我负责应答/转发** |

**F 是一种特殊的 S**：一行被 N 个核共享时，其中**恰好一个**副本处于 F，其余处于 S。F 是这组共享副本的"指定发言人"——新的读者来要数据时，**只有 F 那个核负责转发**，S 的核一律沉默。

## 二、F 状态解决了什么：避免多副本"抢答"

设想一行被 3 个核共享，第 4 个核来读它：

```plantuml
@startuml
skinparam shadowing false
skinparam sequence {
  ParticipantBackgroundColor #E3F2FD
  ParticipantBorderColor     #1976D2
  ArrowColor                 #6A1B9A
  LifeLineBorderColor        #90A4AE
}
participant "核4(新读者)" as N
participant "核1" as C1
participant "核2" as C2
participant "核3" as C3
== 没有 F：谁来应答？两种坏结果 ==
N -> C1 : BusRd 广播
N -> C2 : BusRd 广播
N -> C3 : BusRd 广播
note over C1,C3
  要么三个都应答 → 冗余流量/冲突
  要么都不敢应答 → 只能去读内存(慢)
end note
== 有 F：唯一指定应答者 ==
N -> C2 : BusRd
C2 -> N : 我是 Forward，由我 cache-to-cache 转发
C1 -> C1 : (Shared，沉默)
C3 -> C3 : (Shared，沉默)
note over N, C3 : 一次干净的转发，无抢答、不必读内存
@enduml
```

**没有 F 的困境**：MESI 里所有共享副本都是对等的 S。新读者广播 BusRd 时——若约定"大家都应答"，会在互联上造成冗余甚至冲突；若约定"都不应答、去读内存"，则放弃了更快的 cache-to-cache 转发。**F 指定唯一应答者**，既避免抢答，又能用缓存间转发（比读内存快）。

这在**多核、共享只读数据多**（如大量核读同一份配置/查找表）的场景收益明显——正是数据中心多路 Intel 服务器的典型负载。

## 三、F 的迁移规则（PlantUML）

关键规则：**F 通常"漂"给最近取得副本的那个核**（most-recent holder），这样负担不会一直压在同一个核上。

```plantuml
@startuml
skinparam shadowing false
hide empty description
skinparam state {
  BackgroundColor<<m>> #FFCDD2
  BorderColor<<m>>     #C62828
  BackgroundColor<<e>> #BBDEFB
  BorderColor<<e>>     #1976D2
  BackgroundColor<<f>> #E1BEE7
  BorderColor<<f>>     #6A1B9A
  BackgroundColor<<s>> #C8E6C9
  BorderColor<<s>>     #388E3C
  BackgroundColor<<i>> #EEEEEE
  BorderColor<<i>>     #757575
}
state "Exclusive" as E <<e>>
state "Forward"   as F <<f>>
state "Shared"    as S <<s>>
state "Invalid"   as I <<i>>
state "Modified"  as M <<m>>
E --> F : 嗅探到 BusRd\n(独占变共享时，自己成为那个 Forward)
F --> S : 有新读者时把 F 交给新副本\n自己退为普通 Shared
I --> F : PrRd 收到某个 F 的转发\n(新副本成为新的 Forward)
F --> M : PrWr (先让其他共享失效)
F --> I : 嗅探到 BusRdX (有人要写)
S --> I : 嗅探到 BusRdX
note right of F
  N 个共享副本中恰有 1 个是 F，
  其余是 S；F 随“最近取得者”转移
end note
@enduml
```

> 细节：当 F 副本被驱逐（evict）时，如果还有其他 S 副本存在，一致性目录/协议会挑一个 S 提升为 F，或让后续读者回落到读内存——实现因微架构而异。

## 四、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。

## 五、对开发者的意义

和所有写失效协议一样：**MESIF 优化的是硬件内部数据流转，不改变应用层的规避准则**。伪共享照样用 cache line 对齐解决（见 [mesi.md](/concepts/cache/mesi.md) 第五节），`perf c2c` 照样能定位跨核争抢。你几乎不会在代码里"感知"到 F 状态的存在——它只是让 Intel 多核在共享只读数据时更省互联带宽。

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

> 起点 [msi.md](/concepts/cache/msi.md)；主协议 [mesi.md](/concepts/cache/mesi.md)；AMD 变种 [moesi.md](/concepts/cache/moesi.md)；写更新族 [dragon.md](/concepts/cache/dragon.md)；对比 [comparison.md](/concepts/cache/comparison.md)；观测 `perf c2c` 见 [mesi.md](/concepts/cache/mesi.md) 第六节。
