三种协议:CXL.io、CXL.cache 与 CXL.mem
前面我们聊了 CXL 的整体架构,现在该深入看看它的三驾马车了。
CXL.io、CXL.cache 和 CXL.mem,这三个家伙各管一摊,又必须协同工作。
我第一次接触 CXL 时,还以为它们只是三种不同的连接方式,结果发现完全不是这么回事。
它们到底负责什么?
打个比方,CXL.io 就像门卫,负责收发包裹、确认身份;CXL.cache 是内部的临时货架,让主人随时取用高频物品;CXL.mem 则是远程的大仓库,主人可以借调仓库里的东西,甚至暂时搬到自己家里用。
具体来看:
- CXL.io:基于 PCIe 的事务层协议,主要用于枚举、配置、中断、DMA 等非一致性的通信。说白了,就是设备发现、寄存器读写、以及那些不需要缓存一致性保障的数据搬运。
- CXL.cache:允许主机 CPU 缓存加速器(或内存)中的部分数据,并保持一致性。加速器可以像普通内存一样被主机访问,但主机可以主动将共享数据缓存到自己的 Cache 中。
- CXL.mem:允许主机直接访问加速器附带的物理内存,而且这些内存可以被映射到主机的物理地址空间,享受完整的缓存一致性。最典型的场景就是内存扩展——把内存池化,给 CPU 当本地内存用。
核心区别一句话:CXL.io 处理非一致性通信,CXL.cache 处理主机侧缓存一致性,CXL.mem 处理设备侧内存一致性。
我用一张图把关系理清楚
下面这个 SVG 图是我自己画的,把三种协议在系统里的位置和交互方式标出来了。你认真看看,能少走很多弯路。
CXL.io:基础的“快递员”
CXL.io 本质上是 PCIe 6.0 的事务层封装,但它只取了 PCIe 的非一致性部分。什么意思?就是数据直接搬走,不关心缓存里有没有副本。我见过不少新人以为 CXL.io 也能做一致性,结果调试时数据错乱,追了好几天才发现是用了错误的操作码。
避坑提示:如果你需要配置寄存器、发中断、做大型 DMA,走 CXL.io 没错。但千万别用它来共享 CPU 与加速器之间的热数据,否则一致性靠你自己维护,那审计成本会让你崩溃。
CXL.cache:让主机缓存设备的数据
CXL.cache 协议允许主机 CPU 把加速器内部的某个数据块“拿”到自己的 Cache 里。加速器可以主动推送(Snoop)或主机主动拉取(Read)。
我曾在项目中遇到一个问题:加速器频繁修改某段数据,主机侧 CXL.cache 却不知道,读到了脏数据。后来才发现,加速器没有发送正确的 Invalidate 请求。那段时间我天天盯着逻辑分析仪看,最后在 CXL.cache 的 Flushed 状态机里找到了漏洞。
- Read & Write 操作:主机可以像访问本地内存一样访问加速器内存,但走的是 CXL.cache 请求。
- Snoop 机制:加速器可以通知主机“我改了这块数据,你赶紧丢掉你的缓存副本”。
- 状态模型:MESI 的变种,但加了 Shared-NoData 等状态,要仔细看 spec。
CXL.mem:内存扩展的杀手锏
CXL.mem 是 CXL 最吸引人的部分。它允许主机把加速器的物理内存映射到自己的地址空间,并且完全参与缓存一致性。说白了,你可以在服务器里插一块 CXL 内存扩展卡,操作系统看它就跟本地内存一样。
我记得第一次测试 CXL.mem 时,发现延迟比本地内存高了不少,但带宽很接近。实际应用里,比如内存池化、数据库热数据扩展,CXL.mem 都能大幅降低 TCO。
| 协议 | 核心用途 | 一致性保障 | 典型延迟(参考) |
|---|---|---|---|
| CXL.io | 控制面、DMA、中断 | 无(非一致性) | ~1 μs |
| CXL.cache | 主机缓存设备内存 | 主机侧一致性 | ~200 ns(若命中) |
| CXL.mem | 主机直接访问设备内存 | 端到端一致性 | ~100 ns(本地访问) |
小心陷阱:CXL.mem 的一致性虽然强,但不是零成本。每次写操作都需要设备端响应,如果设备内存很慢,会拖慢整个写流程。我曾经调过一个加速器,它的本地内存用了 DDR4-2400,结果 CXL.mem 带宽只跑到理论值的 60%。一查,原来是设备端没有开启 Write Cohort 优化。开了之后直接翻倍。
实际选型建议
- 如果只是控制加速器,用 CXL.io 就够了。
- 如果主机需要频繁读取加速器的一小部分热数据,用 CXL.cache。
- 如果想把加速器的内存当作扩展内存池,必须用 CXL.mem。
- 大多数商用设备会同时支持 io + cache 或 io + mem,甚至三者全开。
嗯,我觉得最实用的方法是:先把 CXL.io 调通,保证链路稳定,再逐步叠加 cache 和 mem。别一口气全开,不然出了问题根本不知道是哪个协议的锅。我有一次就是先调通了 CXL.mem,然后才加的 CXL.cache,结果两个协议在地址冲突上打架,花了两周才定位到是地址区间重叠了。
一句话总结:CXL.io 是骨架,CXL.cache 是肌肉,CXL.mem 是血液——三者缺一不可,但各管各的活。