【FHE 同态加密】我们如何实现同态加密推理(十五):实测账本——一跳 1.8 小时,钱花在哪(同态加密推理性能剖析)

关键词:同态加密 | FHE | 性能剖析 | 实测账本 | 自举 | 域变换 | CPU 推理 | 大模型密文推理 | 耗时分析

导读:一次同态加密推理的一跳(lay + boot)在 8 核 CPU 上约 1.8 小时,而这个时间绝大部分不在多项式乘法上——自举的两段域变换单独就占了约 80%。本篇是实测账本,讲清钱花在哪,以及为什么"继续优化卷积基本是白费力气"。所有数字都绑定线程数、且只在同轮交错 A/B 下可比。

项目仓库
Gitee 主仓:https://gitee.com/pei-xiaoguang/kestrel-llm
GitHub 镜像:https://github.com/m13253246268-ship-it/kestrel-llm

相关文档
术语与数据口径 |
性能与基准 |
构建与复现 |
快速上手 |
架构总览

0. 一句话结论

一跳(lay + boot)约 1.8 小时(8 核 CPU,纯 CPU、无 GPU/NPU)。而这个时间绝大部分不在多项式乘法上:

自举的两段"域变换"——coeff_to_slot 与 slot_to_coeff——单独就占了自举耗时的约 80%。

结论直接改变优化方向:继续优化卷积基本是白费力气。


1. 观测口径(先交代前提,再上数字)

按本项目《术语与数据口径》§2.5 的纪律:

项 值 / 说明
平台 AMD Ryzen 7 9800X3D(8 核 16 线程)
线程数 T23_NT(所有数字都绑定线程数,见本系列第 13 篇)
比对方式 同轮交错 A/B;不使用跨轮次的绝对耗时
编译 gcc -O2 -fopenmp,-DCKKS_N=2048
链长 lay 编译期 112;boot 编译期 2100

为什么强调"同轮交错":跨会话/跨轮次的机器状态(频率、缓存、后台负载)不可控,绝对值不可比。这是本文所有数字成立的前提。


2. 一跳的账单

阶段 耗时 判据 内存
lay0 1920 s RESULT=PASS 工作集 ≈1.0 GB
boot0 4285 s BOOT=PASS,8× out np=2083 链长 np=2100,建议 ≥8 GB
合计 ≈ 6205 s ≈ 1.72 h — —

链尾同数量级(供参照):boot26 4469 s、boot27 4489 s。

一眼可见的结构:自举比层内前向贵一倍以上。 一跳 1.8 小时里,约 1.2 小时花在 boot 上。


3. 自举的钱花在哪(本文核心)

boot0 的 [boot profile] 分段(字段名对应本系列第 7 篇的七段流水线):

段 耗时 占自举比例
coeff_to_slot 1688 s ~39%
slot_to_coeff 1716 s ~40%
sin_fold(实部 + 虚部) ~850 s ~20%
modraise / rotate_k / conj_extract / restore_merge 余下 剩余零头
total 4285.03 s 100%

两段域变换合计约 3400 s,占 80%。

3.1 这个结论推翻了什么

我们最初的优化直觉是"优化最热的内核"——也就是 NTT / 多项式乘法,因为它是每次乘法都要跑的东西,profiling 里出现次数最多。

但出现次数最多 ≠ 占用时间最多。在这条链上,真正的成本是:

coeff_to_slot / slot_to_coeff
   = 大量 Galois 旋转
   + 每次旋转都要做 key-switch
   + key-switch 要消耗那对 3×2 的密钥(每个都是 nprimes × n)

也就是说:瓶颈在"搬运"(域变换 + 置换 + 密钥切换),不在"计算"(卷积)。

而 key-switch 的密钥体积是 nprimes × n × 64 bit 量级——链长 2100 时,这些密钥本身就大到缓存装不下。所以这里的成本很可能不是算力,而是内存带宽。

⚠️ 这一条是推断,不是结论。我们只测了"这段花了多少时间",没有测它的微架构行为(缓存命中、带宽、指令数)。要证实"瓶颈是带宽",需要额外一轮 profiling。按本项目纪律,这里标注为未验证的假设。


4. 精度账本(一并公开)

性能之外,本项目同样需要公开的是精度,而且这一份更不能挑数字。

对象 结果 max|err|
u0(lay0,np=16) PASS 8/8 1.2712e-03
u0r112(自举刷新后,np=112) PASS 8/8 1.2706e-03
u26 / u26r112(链尾) PASS 8/8 4.4707e-03 ~ 2.2993e-02
u27(链尾) ⚠️ 6/8 t1h1=3.8216e-02、t3h1=7.9687e-02(超默认容差 3e-2)

三个值得注意的点:

  1. 自举几乎不改变数值:1.2712e-03 → 1.2706e-03。自举是"加油",不是"纠错"(本系列第 7 篇)。
  2. 误差随深度增长:从层 0 的 1.27e-03 到链尾的 2.3e-02,涨了约一个数量级。
  3. 链尾有超阈项:u27 八项中两项越过 3e-2。我们不调容差把它"变成通过",而是照实记账。

5. 另外三份账

5.1 位级复现账

比对 结果
u0 × 8 vs 归档 identical 8 / diff 0
u0r112 × 8 vs 归档 identical 8 / diff 0
lay0.out 日志 213 / 213 行,差异 4 处全是耗时行
lay0.err 日志 1 / 1 行,完全相同
boot0.out 日志 19 / 19 行,差异 1 处(profile 总耗时)
boot0.err 日志 65 / 65 行,62 处差异全是进度耗时

(前提:线程数固定。)

5.2 数据账

项 结果
自助生成权重 vs 分发物 各 9/9 逐字节相同
silu0..25.bin(26 个) 26/26 逐字节相同
embed4.bin / 参考值 / sk.bin 相同
归档 manifest 自校验 主包 104/104、链尾 45/45、工具包 6/6

这条最重要:约 7 GB 的数据包不随仓库分发,但任何人可以用同一个模型把它重新生成出来,且逐字节相同。

5.3 进度账

部分 状态
层 0–4(链头) ✅ 可复现、可独立验证
层 26–27 + 收尾 ✅ 已跑通并归档
层 5–25(21 跳) ⏳ 待认领

6. 安全边界(务请读完)

本文所述参数为机制验证级(n=2048、112 素数内层链、2100 素数自举链),
远低于 HE 参数标准的 128-bit 水平,不得用于保护真实数据。
本文主张的是:本实现在我们这台机器上的工程量账本。
本文不主张:安全强度、性能优越性、与任何其他方案的对比。

特别声明:本文所有耗时都是我们这份实现、我们这台机器的数字,不能用来推断任何其他系统(包括同态加密推理的商用实现)的性能。按项目纪律,跨轮/跨平台绝对值不可比。


7. 这一篇的未解问题

  1. 域变换为什么贵,没有微架构归因(§3.1 的标注)。这是本项目最该做、收益最大的一块分析。
  2. 没有"最小可用链长"的扫描。boot 编到 2100、实际用多少、能不能压到 1500——直接决定内存与耗时,却是拍的。
  3. 精度预算没有闭环。逐层实测 + 逐层定标(第 9 篇),但没有"从 28 层反推每层可用误差"的推导。链尾超阈项是这个问题的一次现形。
  4. 单跳耗时里"内存带宽"的占比未测。如果瓶颈真是带宽,优化方向应该是减少取数(降链长、减密钥体积),而不是加速计算——但这一点还没被证实。

下一篇是最后一篇:未完成清单与已知边界——把这一路所有"我们知道但没解决"的事一次列清。

posted @ 2026-10-01 09:03  haliu  阅读(29)  评论(1)    收藏  举报