CXL.cache 协议基础
咱们今天聊聊 CXL.cache 协议。说白了,就是怎么让加速器(比如智能网卡、GPU)和 CPU 共享内存,而且谁都不拿到脏数据。
我先说说为什么会有这个协议。传统 PCIe 设备干活,用的是 DMA,读写主存都要走驱动,来回一趟延迟几百纳秒。但加速器等不了那么久。CXL.cache 就是让设备能直接拿着 CPU 的缓存一致性协议去读主存,延迟能降到几十纳秒。
缓存一致性模型:不只是一张 MESI 表
你肯定知道 MESI 协议。但 CXL.cache 不是简单把那套搬过来。
CXL.cache 用的是 简化的 MESI 模型,但加了一个很重要的状态:I(Invalid)、S(Shared)、M(Modified)、E(Exclusive) 基本都有,但有些实现里把 E 和 M 合并了。为什么这样干?
因为设备端的缓存通常比 CPU L2 小得多,而且设备不会做复杂的预取。设备需要的是“只读一份最新数据”或者“拿到所有权自己改”。
我记得有一次调一个 FPGA 加速卡,它读数据总读到旧版本。查了三天,最后发现是设备缓存状态机里没有处理 Remote Invalidate。你想想看,CPU 那边改了数据,发了个 invalidate 过来,设备缓存如果不响应,那就拿不到新数据。
CXL.cache 的一致性模型还规定了两种操作:
- Device-to-Host (D2H) 请求: 设备主动向 CPU 发请求,比如读、写、读当前
- Host-to-Device (H2D) 监听: CPU 主动问设备“你有这个数据吗?我要改/读”
这两种消息必须严格配对,不能丢,不能乱序。
Device Coherency:设备也能做缓存代理
Device Coherency 这个概念,很多人一听就晕。其实没那么复杂。
启用 Device Coherency 后,设备端的缓存(我们叫 D-Cache)就变成系统缓存一致性域的一部分。CPU 要读一个地址,如果设备端有 Modified 副本,CPU 得等设备把数据写回来才行。
我见过不少系统设计人员,把 Device Coherency 和 IO Coherency 搞混。简单区分:
| 特性 | IO Coherency | Device Coherency |
|---|---|---|
| 数据路径 | 设备 → IOMMU → 主存 | 设备 ↔ CPU 缓存直接交互 |
| 延迟 | 相对高(走 IOMMU 表 | 低(走 CXL 链路,约 40-80ns) |
| 一致性开销 | 需要软件手动 flush | 硬件自动维护 |
| 适用场景 | 大块数据搬移 | 小数据块、低延迟随机访问 |
启用 Device Coherency 后,设备可以工作在三种 Bias Mode 下:
- HOST Bias: CPU 优先访问,设备读需要先问 CPU。延迟低,适合 CPU 主导的场景。
- DEVICE Bias: 设备优先访问,CPU 读需要先问设备。适合设备密集写。
- NO Bias: 根据访问者动态决定,适合公平竞争。
我记得第一次调 Host Bias 时,设备端的读延迟从 120ns 降到了 65ns。但写延迟反而高了,因为每次写都要先 invalidate CPU 端的副本。
HA / HDM 端口:处理器端的两个关键角色
HA(Host Agent)和 HDM(Host-managed Device Memory)是 CXL.cache 在处理器端的两个端口。没有它们,设备没法参与一致性。
HA(Host Agent) 是 CPU 侧的“一致性管家”。它接收设备的 D2H 请求,查 CPU 缓存,发出去监听,收回来数据,再返回给设备。说白了,HA 就是设备在 CPU 内部的代理。
HA 有两个关键延迟指标:
- HA 入队延迟: 请求从 CXL 链路到达 HA 队列的时间,一般在 5-15ns
- HA 处理延迟: HA 发起监听并收齐数据的总时间,取决于缓存命中情况
如果 HA 过载,请求会在队列里等几十甚至上百纳秒。我调试过一个案例,设备端测到的总延迟超过 200ns,最后发现是 HA 队列深度不够,被 CPU 侧的缓存压力堵住了。
HDM(Host-managed Device Memory) 不是 CXL.cache 的直接组成部分,但它和 CXL.cache 配合使用。HDM 端口把设备本地内存映射到系统地址空间,CPU 能通过 load/store 直接访问设备内存。走 CXL.cache 的方式访问 HDM,延迟比传统 DMA 低一个数量级。
HA 和 HDM 的关系怎么理解?我个人的习惯是:HA 管“谁的缓存里有这数据”,HDM 管“数据最终落在哪块物理内存”。
这张图里可以看到三条关键数据路径:
- 设备发 D2H 请求给 HA(橙色线),HA 查 CPU 缓存并监听
- 如果数据不在 CPU 缓存里,HA 走内部总线(实线)到 HDM,拉全局最新数据
- 如果数据在设备缓存里,HA 发 H2D Snoop 给设备(青色线),设备返回数据
路径 1 和 3 是 CXL.cache 独有的。路径 2 是内存控制器的基础路径,但通过 HA 来协调。
最后说说端到端延迟怎么在这些路径上分布。我拿一个实际配置举例:
| 路径阶段 | 典型延迟(ns) | 说明 |
|---|---|---|
| 设备到 HA 链路 | 20-30 | CXL 链路传输延迟 + 端口序列化 |
| HA 入队+处理 | 10-25 | 取决于 HA 当前队列深度 |
| HA 到 CPU 缓存 (命中) | 5-15 | 如果 CPU L1/L2 命中,极快 |
| HA 到 HDM (未命中) | 30-50 | 走内存控制器到 DDR/HBM |
| HA 回设备链路 | 15-25 | 返回数据 |
| 端到端(命中 CPU) | 60-85 | 加速器最关心的总延迟 |
| 端到端(未命中) | 100-140 | 需要去 HDM 取数据 |
嗯,讲到这里,CXL.cache 协议的缓存一致性模型、Device Coherency 以及 HA/HDM 端口这几个基础就铺完了。你想想看,搞清楚这些,后面再聊优化方法,才能落在实处。