4. CXL.cache 事务分类:读、写、窥探,一个都不能少
聊到 CXL.cache 协议,我第一个想到的,就是它那套复杂又精妙的事务分类。说白了,你得先搞清楚设备到底在和主机“聊什么”,才能去跟踪它、优化它。我自己在刚接触 CXL 时,就吃过“混淆事务类型”的亏,调了两个星期的 bug,最后发现是写请求配了读属性。嗯,这种坑,咱们尽量别踩。
CXL.cache 的事务,按功能可以分为三大类:读事务、写事务和窥探事务。每一类下面还有子类型,我习惯把它们叫做“事务家族”。下面这张图,是我自己梳理的,你们先看一眼,有个整体印象。
4.1 读事务:设备想看看数据
读事务,是设备请求从主机内存中读取数据。你想想看,一个加速卡想要处理数据,总得先把数据从内存里拿过来吧?读事务就是干这个的。
读事务有两个变体:RdCurr 和 RdOwn。它们的区别,我总结在下表里了:
| 特性 | RdCurr(读当前) | RdOwn(读拥有) |
|---|---|---|
| 语义 | 设备只读,不修改数据 | 设备后续可能会修改数据 |
| 返回缓存状态 | S(共享)或 I(无效) | E(独占)或 M(修改) |
| 是否需要写回 | 否,设备不能修改 | 是,可能触发写回 |
| 典型用例 | 查表、配置空间读取 | 计算加载、原子操作准备 |
我记得有次调试一个 AI 推理卡,它的权重加载老是比预期慢。用协议分析仪抓了一圈,发现设备每次读取权重都用的是 RdOwn,导致主机要把缓存行独占给它。其实那些权重根本不会被修改,用 RdCurr 就够了。换成 RdCurr 之后,延迟降了 30%。
RdCurr。别浪费缓存一致性协议的“独占”开销。
4.2 写事务:设备要往内存里写
写事务是设备发起写操作。但它不是简单地“把数据发过去就完事”,CXL.cache 的写事务还要和主机的缓存状态打交道。这里面有两个子类型:WrCurr 和 WrInit。
- WrCurr(写当前):设备写数据,但不要求写回主存。主机可能只是把数据更新到自己的缓存里,并标记为脏。这适合写频繁、但不需要持久化的场景。
- WrInit(写初始化):设备写数据,并且要求写操作“直达”主存,或者至少让主机感知到这是一个完整的写操作。常用于初始化一段内存。
你可能会问:“那我到底用哪种?”
我讲一个实际案例。有个做网络加速卡的团队,他们设备在处理数据包时,需要频繁更新统计计数器。他们用了 WrInit,结果每次写都要等主机确认,性能上不去。我建议他们改成 WrCurr,配合主机缓存策略,吞吐量直接翻倍。不过要注意,如果你的数据对持久性有要求,比如日志落盘,那别偷懒,该用 WrInit 还得用。
WrCurr 后立即读同一地址,结果读到旧数据。为什么?因为主机缓存还没处理完写请求。你想想看,这本质上是个“写-读冒险”。一定要在写完成后,再发读请求,或者用 Fence 机制保证顺序。
4.3 窥探事务:主机在想什么?
窥探事务(Snoop Transaction)是 CXL.cache 里比较“特殊”的一类。它不是设备发起的,而是主机主动发给设备的。目的是什么?为了保持缓存一致性。
想象一下:设备缓存了一行数据,结果主机那边另一个核心把这个数据改了。主机需要通知设备:“嘿,你那个数据脏了,别用了!”——这就是窥探。
常见的窥探事务有两种:
- SnpCurr:主机询问设备当前有没有该数据。设备需要返回缓存状态,比如 S、E、M 或 I。
- SnpOwn:主机要求设备交出数据的“所有权”。如果设备持有脏数据(状态 M),设备必须写回,然后转为 I 状态。
你想想看,如果设备不及时响应窥探,主机这边可能就拿到了错误的数据。所以,窥探响应的延迟,直接影响整个系统的性能。
SnpOwn 的频率,精准找到了一个被多设备争抢的锁变量。
4.4 怎么区分和记忆?
我习惯用一句话来记:
- 读:设备要数据(付钱还是不付钱?—— RdCurr 不付,RdOwn 付)。
- 写:设备给数据(直接放缓存还是非写到内存?—— WrCurr 放缓存,WrInit 写到底)。
- 窥探:主机来问话(就问一问,还是逼你交东西?—— SnpCurr 问问,SnpOwn 交出来)。
当然,实际协议里还有一些组合事务和特殊标记位,比如数据是否有效、是否包含预取提示等。这些我们在后续章节会展开。但今天先把这三个大类记牢,后面的就好办多了。
4.5 实战建议:从分类开始定位热点
在《CXL.cache 事务跟踪》这门课里,你拿到一份 trace 日志,第一步不是看地址,而是先看事务类型分布。怎么分析?
- 统计各类事务的数量占比。如果写事务占比异常高,可能设备在频繁更新数据,或者缓存策略有问题。
- 检查窥探事务的分布。如果某一段地址空间被频繁
SnpOwn,那极有可能是热锁或伪共享。 - 对比
RdCurr和RdOwn的比例。如果RdOwn占比过高,但设备实际很少写,那就是“过度申请所有权”,浪费带宽。