Appearance
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 块缩放
};- 词表压缩:token 先过
token_map从 129280 压到 99092 个"压缩 ID"(NFKC→NFD→去重音→小写→空白折叠——大小写和重音不同的拼法落进同一行) - 乘法哈希:每层一组奇数乘子,
hash = id₀·m₀ ⊕ id₁·m₁ ⊕ id₂·m₂ ⊕ id₃·m₃——与 开放寻址哈希表 同族的 multiply-xor 哈希 - 素数取模分桶:对 3 个历史位置 × 8 头共 24 列,各用一个素数
p做桶数:row = hash % p + offset。各列 offset 依次累加,行区间互不重叠,rows = Σp就是表的总行数(两层各 ~3.84 亿行)。8 头 × 多位置 = 同一个 4-gram 探测 24 个视角,天然抗单哈希碰撞 - 掩码语义: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_RDONLYfd,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、预取与原生会话解码