1. CXL.cache 概述

说起 CXL(Compute Express Link),得先聊聊它为什么会出现。我在做数据中心性能调优的这些年里,经常遇到一个尴尬局面:CPU 算力越来越强,但内存带宽和延迟成了瓶颈。你想想看,几十个核心等着从内存取数据,传统内存总线的带宽根本不够用。CXL 就是为解决这类问题而生的互联协议。

CXL 本质上是个高速缓存一致性互联标准。它跑在 PCIe 5.0/6.0 的物理层上,但多了个杀手锏——缓存一致性。说白了,就是让 CPU 和外接设备(比如加速卡、内存池)共享同一个内存视图,不用你手动搬数据。今天这一章,我们先聊聊 CXL 的背景,以及 CXL.cache 在整个协议族里的定位。

1.1 CXL 技术背景

CXL 由 Intel 主导,2019 年推出 1.0 标准,现在已经有 3.0/3.1 了。它的目标很明确:在数据中心把 CPU、GPU、FPGA、智能网卡、内存池这些设备高效地粘在一起。背后推动力有两个:一是 AI/大数据对内存带宽的贪婪需求,二是分解式架构(Disaggregated Infrastructure)的流行。

我个人印象最深的是,2020 年有个客户想在单节点里塞 4TB 内存跑内存数据库。传统 DDR4 通道数有限,插满也就 2TB,而且 NUMA 访问延迟差异很大。CXL 的内存池化方案可以把远端内存做成类似本地内存的带宽和延迟——当然,不是完全一样,但已经足够诱人了。

CXL 定义了三种子协议,或者说三种设备接口:

子协议 全称 核心能力 典型场景
CXL.io I/O 协议 基于 PCIe 的发现、配置、DMA 设备枚举、寄存器访问、控制面通信
CXL.cache 缓存协议 设备访问主机缓存,保持一致性 加速器共享 CPU 缓存行,低延迟协作
CXL.mem 内存协议 设备访问主机内存,或主机访问设备内存 内存扩展、内存池化、NUMA 透明

CXL.cache 是其中最容易混淆的一个。很多人以为它跟 CPU 的 L1/L2 cache 是一回事——其实差别很大。下面我们深入聊聊。

1.2 CXL.cache 应用场景

CXL.cache 允许一个加速器(比如 AI 推理芯片)直接访问并缓存主机 CPU 的缓存行。这意味着加速器可以跟 CPU 在同一缓存一致性域里工作,不需要绕过操作系统手动同步。为什么需要这个?

  • 低延迟协作:加速器处理完一批数据,直接回写 CPU 缓存,CPU 下次读就是命中 L1/L2,而不是从内存重新取。
  • 避免数据拷贝:传统做法是把数据从 CPU 内存拷贝到设备内存,再拷回来。CXL.cache 让设备直接读 CPU 的缓存行,省掉两趟 PCIe 拷贝。
  • 支持异构计算:比如 CPU 做逻辑决策,GPU 做矩阵运算,FPGA 做信号处理——它们通过 CXL.cache 共享工作集,互相换入换出缓存行。

一个实用场景:我之前参与过一个智能网卡项目,网卡上的硬件数据过滤引擎需要读取 CPU 网络协议栈的热数据。传统做法是 DMA 到网卡本地 buffer,再轮询。改用 CXL.cache 后,过滤引擎直接 snoop CPU 的 LLC,延迟从微秒级降到纳秒级。当然,不是所有硬件都能用,得支持 CXL.cache 的客户端协议。

注意,CXL.cache 的“cache”是动词——设备可以当作 CPU 的一个缓存代理。但设备本身也有自己的本地缓存,称为设备缓存(Device Cache)。CXL.cache 协议定义了设备如何向 CPU 发送读/写请求,以及 CPU 如何回应一致性入侵。

1.3 CXL.cache 与 CXL.mem / CXL.io 的关系

我现在习惯把这三种协议想象成三层分工:

  • CXL.io 是基础层——负责设备发现、配置、中断、控制面通信。它很像 PCIe 的传输层,所有 CXL 设备都需要至少实现 CXL.io。
  • CXL.mem 是内存扩展层——设备可以暴露自己的内存给主机(称为“主机管理的设备内存” HDM),或者主机把自己的内存映射到设备空间(称为“设备管理的设备内存” DDM)。
  • CXL.cache 是缓存协作层——设备可以主动操纵 CPU 的缓存行,形成一个多级缓存层次。

这三者可以独立实现,也可以组合。比如一个智能内存池设备只需要 CXL.io + CXL.mem,不需要 CXL.cache。而一个加速器如果希望紧密耦合 CPU 的流水线,就要实现 CXL.io + CXL.cache(可能还有 CXL.mem 来访问设备本地内存)。

关键区别:CXL.mem 操作的是内存地址空间(按缓存行粒度访问 DRAM),而 CXL.cache 操作的是 CPU 的缓存一致性域(直接读/写/失效缓存行)。前者影响内存容量和带宽,后者影响延迟和一致性协议复杂度。

CXL.cache 协议定义了几种请求类型:读(SnpData)、写(Prefetch)、无效(SnpInv)、回写(WrCur)等。设备发起请求,CPU 的缓存一致性引擎(Caching Agent)负责处理并回复。我曾经踩过一个坑:设备实现 CXL.cache 时如果时序没对齐,CPU 可能返回一个无效数据——因为一致性域里还有别的主机在改同一个地址。所以调试时一定要用逻辑分析仪看 QOS(Quality of Service)信号。

警告:CXL.cache 要求设备必须实现缓存一致性协议的全部状态(MESI 或 MOESI 的变种)。如果你只是简单地把设备内存当 CPU 的 cache 用,而没有正确处理 snoop 回读,系统会随机崩溃。我之前一个同事没读协议规范,直接写死缓存状态,结果 CPU 挂了之后设备还在往失效行写数据——数据损坏,查了两周。

下面这张图展示了 CXL.cache 在整个 CXL 协议栈中的位置,以及它与 CPU、设备内存的交互关系。

CXL 协议栈核心关系图 CPU 核心(Host) 缓存一致性引擎 加速器设备 CXL.cache 协议 设备本地内存 主机 DDR 内存 CXL.mem(内存访问) CXL.io(枚举、配置、DMA) 设备通过 CXL.cache 直接操纵 CPU 缓存行 CXL.mem 访问内存地址,CXL.io 做控制面,三者配合实现高效异构计算

从这张图你可以看到:CPU 通过缓存一致性引擎管理自己的缓存行;加速器设备通过 CXL.cache 协议(绿色线)向该引擎发送请求;CXL.mem 负责实际的内存数据传输(虚线);而 CXL.io 像一条底层通道,用于初始化、中断和低级控制(点线)。

嗯,这里要注意:实际硬件实现中,这三种协议通常共用同一组 PCIe 物理链路,分时复用不同的事务层。CXL 的物理层可以承载混合流量,比如一个请求可能是 CXL.cache 的缓存读,另一个是 CXL.io 的配置写,它们在链路上无损切换。

最后我再提一点个人经验:如果你是第一次搭建 CXL.cache 的测试环境,建议先用 CXL.io 做基础通信,确认链路带宽和延迟基线,再启用 CXL.cache 功能。不然你很难分清楚是 CXL.cache 协议没实现好,还是 PCIe 链路抖动导致的问题。我当时就犯过这个错,一上来就开 CXL.cache,结果挂了,查了三天才发现是 PCIe 重训练参数没配好。

好了,这一章我们理清了 CXL 是什么、CXL.cache 在其中的角色以及它和 CXL.mem/CXL.io 的分工。接下来我们会深入 CXL.cache 的协议细节和性能测试实战。