同态加密推理:能跑、能复现,但别用于生产——16篇系列的诚实边界清单

Smart AI Code2026-10-03阅读约 10 分钟

读完这篇,你会得到一份关于同态加密(FHE)推理引擎的完整「体检报告」:哪些指标合格、哪些已经亮红灯、哪些是我们主动放弃的、哪些是还没堵上的漏洞。适合正在评估 FHE 推理可行性,或打算在现有开源实现上继续开发的工程团队。

0. 先说三句话

  1. 这套同态加密推理引擎能跑——28 层 Transformer 的完整推理链路已跑通。
  2. 它能复现——在两套独立工作区(一份含既有数据、一份全新克隆)里,逐字节比对通过。
  3. 它不安全——参数是机制验证级(n=2048),远低于 128-bit 安全标准,不得用于保护真实数据。

这三句是本系列所有结论的前提。下面展开。

1. 安全边界:三条非安全事实

本项目所有参数(n=2048、112 素数内层链、2100 素数自举链)都是机制验证级,不是生产级。在此之外,还有三个具体的非安全事实:

#事实后果
1随机数源是原型级 xorshift,非密码学 RNG安全性分析不成立
2私钥 sk.bin 与密文一起在接力包内流转无密钥托管、无分段解密
3为换取自举的数值可行性,私钥稀疏度 hw 被降到 8私钥更稀疏、更不安全

这三条不是「待改进项」,是「当前状态」。任何引用本项目的输出,都请连带引用这三条。

2. 阻塞级:三个最关键的未解决问题

2.1 位级可复现性缺陷尚未修复

现象:线程数 T23_NT=4 与 T23_NT=8 的输出位级不同。

根因:_Thread_local RNG 以固定常量播种,随机数消耗与线程调度绑定。

方案:按自同构指数 k 播种(代码里已有按 k 的入口),但尚未落地。源码仍是:

c static _Thread_local uint64_t ckks_rng_state = 0x243F6A8885A308D3ull;

影响:所有「位级可复现」声明必须绑定线程数。如果你复现时用了不同的线程数,结果不会逐字节相同。

2.2 精度余量不宽,链尾已有超阈项

对象结果最大误差
u0(层 0)PASS 8/81.27e-03
u0r112(自举刷新后)PASS 8/81.27e-03
u26 / u26r112(链尾)PASS 8/84.47e-03 ~ 2.30e-02
u27(链尾)⚠️ 6/8t1h1=3.82e-02、t3h1=7.97e-02(超默认容差 3e-2)

两个要点:

  • 自举几乎不改变数值:1.2712e-03 → 1.2706e-03。自举是「加油」,不是「纠错」。
  • 误差随深度增长约一个数量级:从层 0 的 1.27e-03 到链尾的 2.3e-02。

我们不调容差把它「变成通过」,但「这两项算不算失败」还没有定论。

2.3 密文 attention 的定标越界无兜底

softmax 用「预定标」替代了 max-sub。一旦真实输入让 score 跑出统计区间,拟合多项式在区间外的行为没有任何保证,而且不会报错。

这是整个系列里最让人担心的一处:它是静默的,且随输入而定。你可能拿到一个「跑通了」的结果,但它在数学上是错的。

3. 重要的技术缺口(按严重程度排序)

#缺口说明篇目
1自举域变换未归因coeff_to_slot + slot_to_coeff 占自举 80%,但「为什么贵」只有推测(疑为内存带宽),没有微架构 profiling第 15 篇
2精度预算无闭环逐层独立定标 + 事后整链验收,没有「从 28 层反推每层可用误差」的推导第 9 篇
3无最小可用链长扫描boot 编到 2100,实际用多少、能压到多少,是拍的第 5、7 篇
4「缺失即默认」模式未清理全树同类文件读取需逐一审查第 8 篇
5DIRECT_LAYERS 是固化调参不是推导规则,换模型不能自动适配第 8 篇
6协议无版本号、无输入指纹参数一变,旧包新包长得一样,下一棒无法验证输入第 11、14 篇
7线程池无非重入断言、无 A/B 数据非可重入是静默失败;「省了多少」没有同轮实测第 12 篇
8NTT 只做 radix-2未 SIMD 化;两条模乘路径未做交叉验证第 3 篇
9预处理脚本未做完整性对拍只验了权重(9/9)与 silu(26/26),拟合脚本产出未逐字节对拍第 2 篇
10换模型需整体重跑 + 人工判断定标、拟合、SiLU 路径选择都依赖对明文参考的统计第 9 篇

4. 性能账本:已知的和未知的

4.1 已知数字(本项目,AMD Ryzen 7 9800X3D,8 核,纯 CPU)

  • 一跳(lay + boot)约 1.8 小时(≈ 6205 s)
  • 其中自举(boot)约 1.2 小时,层内前向约 0.6 小时
  • 自举的两段域变换(coeff_to_slot 1688 s + slot_to_coeff 1716 s)合计约 3400 s,占自举耗时的 80%

4.2 未知的

  • 域变换为什么贵,没有微架构归因。我们只测了「这段花了多少时间」,没有测缓存命中、带宽、指令数。要证实「瓶颈是带宽」,需要额外一轮 profiling——按项目纪律,这里标注为未验证的假设。
  • 单跳耗时里「内存带宽」的占比未测。如果瓶颈真是带宽,优化方向应该是减少取数(降链长、减密钥体积),而不是加速计算。

4.3 与前沿的横向对比(仅供参考,非本项目数据)

系统硬件结果
Cerium(arxiv 2512.11269)NVIDIA GPU(多 GPU)自举 7.5 ms;BERT-Base 8 s;Llama-3-8B 134 s
Odin(arxiv 2609.12378)单张 H100 80 GBLlama-3-8B 366.4 s(端到端)
Cachemir(arxiv 2602.11470)GPULlama-3-8B 每 token < 100 s
PQC-LLaMA-3(arxiv 2604.12168)i9 CPU237 ms/步,最高 80 token/s,精度 98%

本项目不主张与任何其他方案的优劣对比——跨平台、跨实现的绝对值不可比。

5. 已知取舍:我们主动做了哪些选择

这些不是 bug,是主动权衡。列出来是为了避免被当成疏漏:

取舍我们换到了什么
hw = 8(私钥更稀疏、更不安全)自举折叠的混叠变小 → sin 逼近只需 9 次多项式
保留 BFV 那代实现不删它是最简正确性自检(T1–T6),也是「为什么走不通」的原始证据
自研线程池不支持嵌套并行更低的调度开销 + 确定的分块
数据包不随仓库分发(约 7 GB)仓库体积可控;代价是「必须能自助生成」(已验证逐字节一致)
不追求链尾全项通过照实记账,而不是调容差
接力包内含 sk.bin免去密钥分发协议;代价是没有隐私性质

6. 文档与一致性欠账

#欠账
1vllm_ckks.h 顶部「架构」段仍写「4 个 60-bit 素数,支持深 3」,而 CKKS_NPRIMES 默认已是 32——描述停留在最初原型
2boot 日志中 out np=2083 与编译期 2100、落盘 112 的精确关系未核实,我们在文档里显式标注而不做断言
3phase 2 (l1) 与 phase 6 (lay) 语义重叠,属开发阶段遗留
4fin 阶段的判据与前面几层不同,描述不够显眼——我们自己复现时撞过
5各篇标注的「未核实项」会随代码演进过期,需要一次统一的审计

7. 如果你想接着做:从哪开始

按「收益 ÷ 难度」排序,前三件是:

  1. 落地 RNG 按 k 播种(第 2.1 条)。结构现成、方案明确、影响面清晰。做完之后,「位级可复现」才能不带星号地讲。顺手在 CI 里加一条「不同线程数比字节」的回归检查。
  2. 给自举的域变换做一次真正的 profiling(第 3.1 条)。80% 的时间在那里。哪怕只是确认「瓶颈是带宽还是算力」,就能把优化方向从「优化卷积」改成正确的那个。
  3. 让 DIRECT_LAYERS 变成推导出来的规则(第 3.5 条)。判据(窄值域→直接拟合)原理上可以从明文参考统计自动生成。这一步做完,「换模型」才有自动适配的可能。

8. 一件我没能做到的事

本系列写到这里,有件事必须说清楚:

我们还没有让这套东西「陌生人可用」。 仓库、文档、Release 资产都准备好了,但还没走到能被外部独立复现那一步——因为公开访问的入口(仓库推送 + Release 上传)还没完成。

也就是说,本系列所有「可复现」「可独立验证」的结论,目前都是我们自己在两套独立工作区里验证的,还没有第三方跑过。

这正是最初那 21 跳待认领的原因,也是这套设计存在的意义。

如果你读到这里,并且手上有一台愿意跑 1.8 小时的机器——欢迎接手一跳。

附:全系列 16 篇索引

篇目主题
第 1–2 篇预处理与数据对拍
第 3 篇NTT 与多项式乘法
第 5 篇链长与参数选择
第 7 篇自举流水线
第 8 篇DIRECT_LAYERS 与 SiLU 路径
第 9 篇逐层定标与精度预算
第 10 篇密文 attention 定标
第 11 篇协议与接力包
第 12 篇线程池
第 13 篇位级复现
第 14 篇接力验证
第 15 篇性能剖析(1.8 小时账本)
第 16 篇未完成清单与已知边界(本篇)

系列完。全系列 16 篇。若发现任何与源码不符之处,以源码为准——本文档的每一处断言都标注了它的依据与不确定性。