Skip to content

Engram——DeepSeek V4.1 Flash 引入的盘上 n-gram 关联记忆:把"最近 4 个 token"哈希进一张 189 GiB 的学习表,取回 48 行 256 维向量,经学习投影和余弦门控加进残差流。"engram" 是神经科学术语(记忆痕迹)。它不是 KV cache——不随上下文增长,是模型权重的一部分,只是永远留在磁盘上。

它解决什么 ​

Transformer 的知识全部压在权重里,参数量决定了"背了多少"。Engram 走了另一条路:把一部分知识外置成一张查找表,推理时按 n-gram 键取出向量直接注入隐藏状态。表可以做得极大(189 GiB,比主权重还大),因为读取是稀疏的——每个 token 只碰 48 行 × 264 B ≈ 12.7 KiB,随机读才是它的访问模式,正好匹配 SSD 的强项;而 CPU/GPU 内存完全不用为它掏一分钱。

对照两条老路:MoE 是"稀疏激活的权重"(还是常驻内存的计算图),SSD streaming 是"把冷权重流进来"(为了算它);Engram 是"外置的记忆"(读进来只为加一下)。

布局与哈希探测 ​

ds4 里 Engram 挂在 V4.1 的第 1 和第 14 层(ds41_engram_layer(il)),每层一张表(DS4_ENGRAM_LAYERS = 2)。探测管线(ds4_engram.c,277 行,值得整读):

c
// ds4_engram.h — 编译期布局
enum {
    DS4_ENGRAM_NGRAM  = 4,    // 当前 token + 3 个前驱
    DS4_ENGRAM_HEADS  = 8,    // 每个历史位置 8 个独立哈希
    DS4_ENGRAM_COLS   = 24,   // 3 个位置 × 8 头
    DS4_ENGRAM_DIM    = 256,  // 每行 256 维
    DS4_ENGRAM_ROW_BYTES = 264, // 256×E4M3 + 8×E8M0 块缩放
};
  1. 词表压缩:token 先过 token_map 从 129280 压到 99092 个"压缩 ID"(NFKC→NFD→去重音→小写→空白折叠——大小写和重音不同的拼法落进同一行)
  2. 乘法哈希:每层一组奇数乘子,hash = id₀·m₀ ⊕ id₁·m₁ ⊕ id₂·m₂ ⊕ id₃·m₃——与 开放寻址哈希表 同族的 multiply-xor 哈希
  3. 素数取模分桶:对 3 个历史位置 × 8 头共 24 列,各用一个素数 p 做桶数:row = hash % p + offset。各列 offset 依次累加,行区间互不重叠,rows = Σp 就是表的总行数(两层各 ~3.84 亿行)。8 头 × 多位置 = 同一个 4-gram 探测 24 个视角,天然抗单哈希碰撞
  4. 掩码语义:padding/图像位置把历史标成 DS4_ENGRAM_DEAD,跨界的 n-gram 全部落到 pad_id 行,图侧还要在这些位置抑制 Engram 输出(kernel_dsv41_engram_add 的 masked 分支直接 return)

哈希状态只有 3 个 int(ds4_engram_history 的 tail)——复制即快照,所以检查点/快照恢复只要重放最后 3 个 token 就能重建(ds41_load_payload)。

消费:学习投影 + 余弦门控 ​

读回的行不是直接求和加进残差。每个 Engram 层带一个 F16 权重 blk.{1,14}.engram_kv.weight,形状 [24·256, 5·5120]:

kernel_dsv41_engram_add(metal/dsv41.metal,dispatch (rows, 4)——第二维就是 4 条超连接流)的数学:

dot   = Σᵢ hᵢ · (q_wᵢ·k_wᵢ) · kᵢ                       // h=残差, k=投影 key, q_w/k_w=逐元素学习权重
dot  *= rsqrt(mean(h²)+ε) · rsqrt(mean(k²)+ε) · √width  // 缩放余弦相似度
gate  = sigmoid(copysign(√|dot|, dot))                   // 平滑符号门:相似度正负都能压小门
h    ← bf16(h + gate · bf16(v))                          // 门控残差加法,BF16 对齐参考数值

关键是门控:Engram 不是无条件注入——投影出的 key 与当前残差做余弦比对,只在"这张表对当前上下文有话要说"时才把 value 加进去。engram_q_norm/engram_k_norm(F32 [5120,4])是逐流逐元素的学习缩放。

永不常驻的 I/O 设计 ​

表在 GGUF 里是尾部一个 I8 张量(blk.{1,14}.engram_embd.weight,[264, rows],编码 e4m3_e8m0_32_row264:256 个 E4M3 权重字节 + 8 个 E8M0 块缩放字节,32 维共享一个缩放——和 MXFP4 同族的块缩放格式)。加载时 model_unmap_engram 直接 munmap 掉这 189 GiB,之后:

  • 单独的 O_RDONLY fd,F_NOCACHE + F_RDAHEAD=0(macOS)——绕过 page cache,读过的页不驻留,189 GiB 永远不挤占内存(对照 mmap 常规权重"缺页即缓存"的策略,这里是刻意反着来)
  • decode:每 token 48 行 pread,2026-09 中旬起(6c00e2d)也走并发批量入口——此前 256 行的并发阈值只有 prefill 够得着,decode 的 48 个串行读全排在 GPU 开工前(串行 107.9 µs/行 vs 并行 12.8);阈值降到 8(Apple 限定),Q2 常驻 decode +9.9%(19.16→21.06 t/s)、输出逐字节相同。行复用率仅 22.5%——赢的是并发,不是缓存
  • prefill:所有行号先一次性哈希好,pthread 预取线程按 2048 token 粒度流式读入(原子计数器发布进度);第 2 张表在第 2 层开始读,正好被前面的编码器计算掩盖。批量读 ds4_engram_read_batch:请求按行号排序 + 去重 + 16 个 GCD 并发 reader,临时缓冲封顶 384 KiB、与表大小和前缀长度无关

TP 两个 rank 都持完整表(各读各的行);批处理 decode 每 session 独立维护表 fd 与哈希历史。

相关概念 ​

  • hash-table — Engram 的探测是同一族 multiply-xor 哈希,只是桶数换成素数、无冲突处理(冲突=读错行,8 头×3 位置冗余兜底)
  • Qwen3.8 PLE — 同构的表亲:第 1 层挂 95.37 GiB 盘上 BF16 3-gram 表(16 行/token、逐行 pread 直读、拒绝量化过的表),同样的 multiply-xor 哈希 + 缩放余弦门控(见 Part 3 §9)——"哈希 n-gram 外部记忆"看来正在成为一族架构模式
  • moe — 另一种"稀疏访问大参数"的思路:MoE 稀疏激活权重,Engram 稀疏查外部表
  • ssd-streaming — 同样靠 SSD 容量,但 streaming 流的是"要参与计算的权重",Engram 读的是"直接相加的记忆"
  • quantization — 行内 E4M3+E8M0 块缩放编码
  • Part 3 §8 — V4.1 Flash 完整架构与 Engram 在图里的位置
  • Part 6 §4q — Metal V4.1 后端:宽批 prefill、预取与原生会话解码