跳转到主要内容

Engram:TP 分片与同机 DP 共享的源码研究

沿 vLLM 0.31 NVIDIA 源码区分 Engram 大表、wkv 投影和共享主存,推导容量与查询成本,并设计验证矩阵

· 约 9 分钟阅读

本文目录

Engram 把容量放进巨大的静态查找表,输入 token 的 n-gram 决定要读哪些 rows。部署时的关键问题是:这些 rows 在哪里、谁持有一份、谁发起查询,以及取回的结果怎样进入 hidden state。

本页固定 vLLM 0.31.0 db9527a46873454610df6dbedf79a36d6bf1a7f6 的 NVIDIA 路径。模型机制与全局缓存先读 DeepSeek V4.1 Flash;这里只深读 Engram 的存储、并行与预取。研究结论来自源码,没有独立 GPU 实测。

30 秒复习
  • 一句话:Engram 的 head 分片管理大表,wkv 分片管理投影,同机 DP 共享管理物理主存;三者改变的资源不同。
  • 三个判断:共享的是静态权重表;DP 共享省主存而不自动省查表流量;sequence parallel 会改变 wkv 的 TP 合同。
  • 核心模型:一份表的字节为 W=∑lRl(d+d/32)W=\sum_l R_l(d+d/32);同节点 DD 个 replicas 的独立表基线约为 DWDW,共享表约为 WW,都需另加 staging、投影和其他模型状态。
  • 边界:公式是逻辑 payload,物理内存与延迟须实测;NVIDIA 构造路径和配置文档存在值得复核的差异,不凭参数名认定已运行某种拓扑。

1. 把四类对象分开

对象来源与生命周期主要成本
Engram embedding rows + scalescheckpoint 静态权重,按 head 的 bucket ranges 分片host/HBM 驻留、随机查询带宽与 TLB
wkv、gate weights把查找结果变成 key/value 并融合 hidden state投影 GEMM、TP gather、局部计算
staged_rows每 rank 的 GPU BF16 查询结果缓冲HBM 容量与写读;DP 共享不会共享这块缓冲
n-gram token history请求/chunk 前沿所需的 token IDshash 正确性、P/D 与 async 交接

静态表可以让多个 replicas 使用同一份物理权重,查询结果仍依赖每个 replica 的 tokens。它不是把多个请求的动态 KV Cache 混成一份。哈希碰撞由模型训练与 gating 处理,也不代表 cache key 可以忽略模型版本。

2. TP 分片有两个层次

2.1 大表按 hash head 拆分

common/engram.py::ParallelEngramEmbedding 先计算 part_n_hash_cols = ceil(n_hash_cols / num_shards),再按本 rank 的 head 范围累加 bucket sizes,得到 vocab_start_idx、vocab_end_idx 和 part_num_embeddings。加载器从 checkpoint 切对应 rows,FP8 权重与 ue8m0 scales 使用同样的 row 范围。

这是 head-wise sharding:每个 head 的整段 buckets 属于一个 shard,维度 d 不在这里切开。不同 head 的 prime-sized bucket ranges 略有差别,最后一组还可能 padding,所以 W/TW/T 只是近似;精确值应从每 rank 的 part_num_embeddings 计算。

若 shard 数让某些 rank 没有任何 hash head,构造会拒绝该拓扑。源码 TODO 提及 row-wise sharding,不能把它写成这个版本已实现的能力。

2.2 wkv 按投影输出切分,但有 SP 条件

表查询完成后,拼接得到宽度 n_hash_cols × head_dim 的 memory vector,wkv 输出 (hc_mult + 1) × hidden_size:各 hyper-connection 分支的 key 加一个共享 value。

# 固定源码的关键构造参数;省略其他参数。
self.wkv = ColumnParallelLinear(
    n_hash_cols * layout.head_dim,
    self.dim * (self.hc_mult + 1),
    gather_output=True,
    disable_tp=use_sequence_parallel,
)
执行模式tokens 在 TP ranks 上的关系wkv 合同
SP 关闭每个 TP rank 处理所有 tokens输出列按 TP 分片,再 gather 输出
SP 开启每个 rank 保留一段 tokensdisable_tp=True,完整投影作用于本 rank 的 token 分片

Engram.embed() 也有相应分支:普通 TP 沿 head 宽度 gather;SP 路径先 gather rank-major rows,再由 _engram_select_rows 重排,保留本 rank 的 token window。只看到 all_gather(dim=0) 不能把通信对象解释成模型权重。

3. 同机 DP 共享怎样建立

DPSharedEngramStorage 管理一个同节点 Engram DP group 对应的物理分配。相同 TP shard 的 DP peers 执行以下启动流程:

  1. group leader 检查空闲空间,在 /dev/shm 创建 backing file,并广播路径。
  2. 各进程 mmap(..., MAP_SHARED) 同一文件,从同一 storage 切出 FP8 weights 与 uint8 scales。
  3. 每个进程对自己的虚拟地址调用 cudaHostRegister,取得 pinned host memory 的 UVA view。
  4. 确认 peers 均完成映射,再释放文件路径;映射继续保持数据存活。
  5. leader 写入对应 checkpoint shard,其他 ranks 等待权重加载 barrier。
flowchart TB
    subgraph HOST[同一节点的共享物理主存]
        H0[TP shard 0:rows 与 scales]
        H1[TP shard 1:rows 与 scales]
    end
    H0 --> A0[DP0 / TP0:本请求 rows → GPU staging]
    H1 --> A1[DP0 / TP1:本请求 rows → GPU staging]
    H0 --> B0[DP1 / TP0:另一个请求 rows → GPU staging]
    H1 --> B1[DP1 / TP1:另一个请求 rows → GPU staging]
    A0 <--> A1
    B0 <--> B1
图 1 · TP=2、同节点 DP=2 的共享关系 性质:按固定源码整理的机制示意;线表示访问与 TP 交换,不表示时间。host 表共享,GPU staging 和请求 token history 各自持有。

共享发生在同一并行运行组织出的 EDP group 中;同节点、同 IPC namespace 是必要条件,不意味着两个独立启动且未协调的 Engine 自动复用同一 backing file。

4. DP 共享与跨 DP 分片的代价不同

4.1 共享 host 表:各 replica 查自己的 tokens

共享模式将 embedding 的有效 dp_size 设为 1。每个 replica 的 TP ranks 共同覆盖全部 heads,各 TP rank 直接查询自己映射的 host shard;对应的 DP peers 映射同一份物理 shard。gather_engram_hashes(..., dp_shared_memory=True) 返回自己的 hash IDs,不执行 DP hash gather;_ready_rows() 也跳过跨 DP rows exchange。

它消除的是 Engram 查询链的逐步 DP collectives。TP gather、wkv 的 TP 输出 gather、整个模型的 EP/DP 同步以及启动时的 CPU group collectives仍然需要分别计量。

4.2 跨 DP 分片:用通信换更小的每 rank 表

分片模式下,NVIDIA embedding 使用 TP × node-local EDP size 个 shards。一次查询先把同组各 replica 的 hash IDs padding 到公共 token slot,再做 DP all-gather;各 rank 读自己拥有的 heads,随后交换 rows,并选择回本 replica 的 tokens,最后完成 TP 侧的 heads gather。

公共 slot 来自节点内 group 的最大 token count。一个 replica 有长 Prefill、其他 replicas 只有短 Decode 时,padding 与 collective 频率可能成为额外成本。DEAD_ID padding 保障无效位置不被当成合法 row 0。

4.3 配置字段与实际构造路径需要对照

EngramConfig 文档把 embedding_across_dp=False 描述为每 DP replica 独立 TP-sharded 表。但本次 NVIDIA 源码中,ParallelEngramEmbedding.__init__ 直接读取 get_engram_dp_size(),_get_shard_info() 再按有效 dp_size 决定分片;该构造没有直接读取 embedding_across_dp。关闭共享或预检查回退后,不能只凭这个配置字段就认定取得了独立副本基线。

这是一个源码与配置语义需要运行复核的点,不是已经在 GPU 上复现的缺陷。部署记录应增加实际 embed_tokens.dp_size、head/row ranges、EDP group membership 与日志,再用它们标注实验模式。

5. 容量账:省主存与省带宽分别验收

设第 ll 个表有 RlR_l rows,每 row 有 dd 个 FP8 字节和 d/32d/32 个 scale 字节,则:

W=∑lRl(d+d32).W=\sum_l R_l\left(d+\frac{d}{32}\right).

只考虑一节点的完整同机 DP replica group,忽略 head 不均衡:

理论存储方式每 TP shard 的 host payload节点 aggregate host payload新增 Engram DP 稳态交换
独立副本基线每 replica 约 W/TW/TDWDW无
同机 DP 共享peers 共同映射约 W/TW/TWW无
node-local DP 分片每 rank 约 W/(TD)W/(TD)WWhash IDs 与 rows
表 1 · 静态表的理想容量对照 T 为 TP size,D 为完整同机 group 的 replica 数;不含 staging、wkv、allocator 与其他模型状态。独立副本行是比较基线,不是对本版本某个 flag 的运行保证。

例如假设 W=200W=200 GiB、T=2T=2、D=4D=4,独立表基线节点约占 800 GiB,共享后约 200 GiB。这个数字只用于展示数量关系,不是 DeepSeek checkpoint 的精确容量或实测结果;多节点仍按各节点的实际 group 数落账。

共享映射可能被各进程 RSS 重复统计,应使用节点物理内存、/dev/shm allocation 和 /proc/<pid>/smaps_rollup 的 PSS/共享页信息交叉检查。只把 RSS 相加会误判共享收益。

若各 replica 都要查询 NiN_i 个 tokens,每 token 读 HH 个 heads,逻辑查询 payload 仍约为:

Blookup≈∑iNiH(d+d32).B_{\mathrm{lookup}}\approx\sum_i N_i H\left(d+\frac{d}{32}\right).

重复 row、CPU cache、page 粒度与事务放大会改变实际总线流量。共享不会自动把多个 GPU 的访问合并成一次传输;同机 replicas 反而可能争用同一 NUMA memory controller 或 PCIe root。

6. 预取的关键路径与适用边界

model.py 在进入 decoder layers 前生成 hash IDs,并为本地 Engram layers 提交 lookup。各层共享一条 prefetch stream,串行安排 offloaded 查询;消费层只等待自己的 completion event。lookup 的 persistent grid 在 background 模式限制 SM 数,给主计算留出资源。

token IDs → hash → side-stream UVA lookup → staged BF16 rows
                    │ 与前置层计算重叠           │
main-stream 前置层 ───────────────────────→ wait_event
                                             → TP gather → wkv → gate

暴露到关键路径的等待可用 max⁡(0,Tlookup−Tlead)\max(0,T_{\mathrm{lookup}}-T_{\mathrm{lead}}) 作为初始假设,但不要把它当作完整延迟公式:查表自身消耗 SM/HBM/host 带宽,会拖慢本来用于掩盖它的前置计算。优化应同时观察 lookup、前置层与端到端 ITL。

配置边界来自源码:共享要求 CPU offload、有效节点内 group、共享 IPC 和足够 /dev/shm;不支持 elastic EP。THP 与共享模式不兼容。DBO/microbatching 会在 V4.1 Engram 配置校验中被拒绝。拓扑/容量预检查可以回退,实际共享分配或注册失败则可能报错,不能统称为静默 fallback。

7. 下一轮研究的最小验证矩阵

先固定 checkpoint、vLLM SHA、GPU/NUMA topology、TP/SP、精度与请求回放;选择能容纳模型的最小同节点配置,再做以下对照。

要分辨的问题单次改变应观察的结果
主存是否共享实际 shared/private storage,确认构造结果节点物理 bytes/PSS、shm、启动加载时间
DP 查询交换是否消除同 payload 下 shared 与 DP-shardedhashes/rows collectives、padding slot、每步暴露等待
host locality 是否主导固定模型与负载,调整 CPU/NUMA placementhost bandwidth、PCIe bytes、lookup 与 ITL P95/P99
wkv TP 是否划算可支持组合中的 SP 开/关projection shard、TP gather、HBM 峰值与层时间
overlap 是否真实冷/热查询分别记录,保持请求身份prefetch event 的等待、前置层回归、TTFT/ITL/goodput
状态是否正确prefix hit、P/D、chunk boundary、spec rollback与相同精度基线的输出/质量对照、lookback IDs

使用无 profiler 的测量给出性能结果,Trace 负责归因。不要把 event wait 的完整时长都算作可回收收益;先确认同期主 GPU stream 在做什么。

官方 test_engram_dp_shard.py 提供多 rank 表共享、权重加载、graph replay、uneven token padding 与节点边界测试,是未来 GPU 实验的重要入口。本轮阅读了这些测试合同,未执行其 CUDA 多 GPU 用例。

相关页面与源码入口

修改历史2 次提交