跳转至

笔记

Mooncake File vs Block Device

导言

我最开始想确认的是一个很具体的问题:Mooncake Classic TE 和 TENT 打开的究竟都是文件系统普通文件,还是也能把 /dev/nvme0n1 这样的 SSD 裸块设备交给 GDS?源码里两边都会调用 open(path, O_DIRECT),NVIDIA cuFile 又确实接受 device fd,看起来答案应该是“可以”。继续向上追到 Segment 层后,结论却发生了分叉。

这篇文章把这条调用链从头串起来:先解释普通文件、块设备、S_ISREGS_ISBLK,再看 GDS 提供了什么 API、Mooncake 实际用了什么,最后落到 cuFileBatchIOSubmit 的同一组参数为什么在两个场景里具有不同含义。核心判断是:GDS 统一了 I/O 接口,却没有替 Mooncake 补上块设备的容量、对齐、越界和独占语义。

Mooncake Classic vs TENT Engine

导言

看到“应用必须持有 Transport*”时,我真正卡住的是:持有到底是什么意思?为什么直接保存一个后端指针就能工作,统一的 TransferEngine 反而走不通?

把这个词拆开后,Classic 与 TENT 的差异就不再只是两套接口。Classic 的公共 Batch 先于后端选择而创建,后端私有状态缺少稳定的安放位置;TENT 则在选定后端后,为每个 transport 创建独立 SubBatch,再由统一运行时协调提交、状态与回收。

因此,判断一个 Engine 是否容易扩展多个后端,不能只数有多少个 Transport 子类。真正要问的是:后端的私有状态放在哪里,由谁创建,又由谁保证它完成整个异步生命周期。

Mooncake Classic NVMeoF Transport

导言

在分析 Mooncake 的 NDS 接入位置时,经典 NVMeoFTransport 很容易被误认为 TENT GdsTransport 的前一版:二者都注册 buffer 和文件 handle,都使用 cuFile Batch API,也都维护异步完成事件。

但这条旧路径真正特殊的地方不在 cuFile,而在 Batch 所有权。可运行的调用必须直接持有 Transport*,并始终走 xport->allocateBatchID → xport->submitTransfer → xport->getTransferStatus → xport->freeBatchID。一旦换成 engine->allocateBatchID → engine->submitTransfer,批次便由 MultiTransport 创建,NVMeoFTransport 无法附加私有 descriptor,最终返回 NotImplemented

本文把这条旧路径单独展开:先给出从 NVMe-oF 挂载到专用测试的 SOP,再画出可运行路径与断路分支,最后按执行顺序逐句解释 Batch 分配、请求切片、cuFile 提交、完成事件聚合与资源回收。

Mooncake Codebase Architecture

导言

上一篇文章从 FAST'25 论文出发,解释了 P/D 解耦、分布式 KV Cache 与调度机制。论文读懂以后,打开代码仓却很容易再次迷路:Connector、Mooncake Store、Transfer Engine、TENT 看起来像四个并列组件,实际却跨越 vLLM 与 Mooncake 两个仓库,并分别承担框架适配、对象管理、字节搬运和新传输内核

本文固定在 Mooncake 6a00c353 与 vLLM 5bbc58c0,从仓库结构、请求流、时序和关键类关系重新组织这些概念,最后给出一套可重复的源码走读与开发 SOP。

TurboBus PCIe Bandwidth Pooling

导言

GPU Memory Offloading 有一个很反直觉的现象:应用可能把大部分时间耗在 PCIe 搬运上,同一台服务器却仍有多数 PCIe 带宽处于空闲。问题不在于服务器缺少链路,而在于每块 GPU 只能使用自己名下的那条链路;不同 GPU、不同作业的搬运阶段又往往没有同时到来。

TurboBus 的核心思想,是把 NVLink、NVSwitch 等高速 Scale-Up Fabric 当成一条节点内的转发背板:繁忙 GPU 先把数据送到邻居 GPU,再借邻居的空闲 PCIe 链路访问 Host Memory。本文沿着论文 Figure 1、4、5、6、7 解释这条思路怎样从一张直觉图落成带隔离、流水、调度和 API 的系统,并区分实验已经证明的收益与仍受硬件拓扑、NUMA 和工作负载限制的外推。

io_uring Async I/O

导言

读到 Tutti 的 GPU io_uring 一节时,我几乎完全没看懂。SQ、CQ、IOCB、CUDA Event 和 Green Context 挤在一起,很难看出它们各自解决什么问题。

本文先用取餐叫号解释核心直觉,再用 Linux io_uring 的 SQ、CQ、SQE、CQE 和 SQPOLL 建立准确模型,最后把这些对象放回 Tutti:为什么队列能把发起请求等待结果拆成两个时间点,以及为什么还需要 CUDA Event、Green Context 和 slack-aware Scheduler 才能真正减少 GPU 停顿。

SHMEM Symmetric Memory

导言

我最初把 HCCL 理解成集合通信,把 HiXL 理解成单点通信。这个分法可以作为起点,却会让 SHMEM 无处安放:它既能一对一 Put/Get,也能做原子操作与同步,为什么还需要“对称内存”这套约束?

关键在于,HCCL、HiXL 和 SHMEM 并不只是在争夺同一种通信 API。HCCL 更接近“我要完成什么群体操作”,HiXL 更接近“我要把哪段数据传到哪里”,SHMEM 则把问题改写成“我要访问哪个 PE 上的哪个内存坐标”。它用跨 PE 的布局约束,换取设备侧可以低开销地定位和操作远端数据;但当各 PE 的容量需求严重不均时,这份约束也确实可能造成浪费。

Mooncake TENT Request Path

导言

上一篇文章把 TENT 的请求路径压缩成了一串箭头。那串箭头没有错,但它隐藏了代码走读时最容易断掉的几处连接:公共 Request 在哪里变成 TaskInfo,为什么 selector 会返回 GDS,一个逻辑 task 怎样展开成多个 cuFile slice,以及完成事件怎样重新聚合成公共状态。

本文固定在 Mooncake 提交 89da2c3a,只追踪一次成功的 GDS 读取。每个关键节点都给出实际会执行的 C++ 片段;代码语句保持原样,只增加中文走读注释和明确的省略标记。

Ascend Interconnect Evolution

导言

在昇腾通信栈的第五轴中,PCIe、片内 NoC、HCCS、RoCE 与 UnifiedBus 被放在同一行。看到这些概念,一个很自然的疑问是:它们为什么没有统一?是不是每一种新互联,都是为了解决上一代的缺陷,并最终取代前者?

这个问题抓住了技术演进,却预设了一条并不存在的替换链。这些概念分别工作在芯片内部、服务器 I/O、节点内 Scale-Up 和集群 Scale-Out 等不同范围,提供的事务与内存语义也不相同。本文从无法绕开的物理约束出发,沿历史纵轴梳理它们出现的契机,再用同一组维度比较其设计取舍,最后说明:什么时候一种新思想只需要增加后端,什么时候它会形成新的编程契约和代码仓。

Ascend Communication Runtime Evolution

导言

在昇腾通信栈的第二轴中,HCOMM、SHMEM Runtime、IPC、CANN VMM 与 Fabric Memory 被放在同一个“资源、地址与运行时”区域。它们都在帮助程序访问另一进程、另一设备甚至另一节点的内存,因此很容易被理解成五套重复方案:既然目标相同,为什么不统一成一套 API?

这个问题真正触及的是系统抽象如何演进。五个概念共享“让数据可达”的目标,却分别承诺资源组织、PGAS 编程、进程共享、虚拟内存管理和内存池化;它们不是五代同类技术,而是从五个不同矛盾长出的责任边界。本文从第一性原理拆出这些不可互换的契约,再沿时间纵轴和能力横轴梳理其提出契机、公开时机、新仓库形成条件与未来收敛方向。