Files
SystemSimulationApp/docs/other/按需编译与分层缓存优化-2026-09-12.md
T

116 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 按需编译与分层缓存优化
日期:2026-09-12。基线提交 `aa4951b14ea6453138e3e877f1167a28100f486f`。本轮已接入正常构建入口,采用“可复用目标文件 + 完整模型缓存 + 容量预算/LRU”。同机八路首次构建中位 3.7742 → 2.4875 s,减少 34.09%;完整缓存命中的检查增加约 0.13 s。数值方程、牛顿求根、着色雅可比、`rtol=1e-8`、采样和结果编码保持当前已接受版本。
## 编译与复用方式
原来 `kernels.c` 中的实现拆为物性、孔口、管路、机械、信号五个功能模块。59 个原函数体逐字一致。构建器从生成代码中查找实际引用的导出函数,补齐依赖后选择模块;例如管路、孔口依赖物性。固定运行库仍需编译。粒度是功能模块,不是每个元件实例;同一模块内的相关函数会一起编译。
首次只编译所需单元,最多 4 个并行任务。每个单元预处理成确切的 C 内容,以预处理字节、逻辑文件名、编译器和编译选项计算对象键,再编译这份内容。缓存有效性覆盖实际使用的宏和间接头文件。完整模型另核对生成源码/头文件、组件合同、对象键、链接库和依赖声明。
系统改参数通常只影响生成的 `model.c`;拓扑或状态布局变化也可能使包含 `model.h` 的运行单元失效。完整模型仍在缓存时直接复用可执行文件,否则复用未变化的 `.o` 再链接。时间范围、步长、rtol 等运行选项不编入模型。缓存跨服务重启保留;服务重启本身不要求重新编译。
A/B/C/D 对应的模块对象可共同留在对象池,各系统的可执行文件也可分别留在模型池。容量回收后可能只保留部分对象或模型;再用到被清理内容时自动重建。这样能跨项目共享相同内容,也能限制长期增长。
## 文件位置与容量管理
所有新编译缓存都在 `app/data/native-builds/`,已被 Git 忽略:
| 位置 | 内容 | 默认容量预算 |
| --- | --- | ---: |
| `models/<SHA>/` | 完整模型可执行文件、生成源码、合同清单及必要 DLL | 256 MiB |
| `objects/<SHA>/` | 可复用目标文件及校验清单 | 128 MiB |
| `.locks/` | 固定分片的进程使用锁和清理锁 | 最多 257 个空锁文件 |
环境变量 `SIMULATION_NATIVE_MODEL_CACHE_MB`、`SIMULATION_NATIVE_OBJECT_CACHE_MB` 可设为非负整数 MiB。按最近使用时间淘汰较久未用内容;两池分别计量。模型淘汰不强制删除其共享对象,对象淘汰也不影响已链接模型。再次运行被淘汰的模型只是需要重新构建,不会丢失工程或仿真结果。
预算是**安全软上限**:正在构建/运行的条目跳过,锁分片碰撞也可能暂缓清理;单个条目已超过预算时保留最后一份并报告超额。计量为受管理文件的逻辑字节,不含文件系统元数据、锁、正在写入的临时目录以及旧格式缓存。因此不能将 384 MiB 理解为磁盘占用的绝对硬上限。
旧格式的根级 `<SHA>/` 未自动迁移或删除,本机检查时有 229 份、89,754,121 字节(85.60 MiB),不计入新预算,新代码也不会继续向旧格式追加。保留它们是为了不影响仍可能使用旧构建器的进程;它们的位置与新 `models/objects/` 分开,后续可在确认旧进程停止后单独整理。
普通失败清理临时目录;进程被强制终止遗留的新格式 `building-<SHA>-<随机后缀>/`,在后续清理时先取得对应排他锁,再回收。并发清理由全局锁串行化,避免重复淘汰。缓存缺文件、校验失败、符号链接或合同不一致时明确报错,不执行未经核验的缓存。
## 构建成本对照
使用冻结的基线源码和当前候选,各 1 轮预热、3 轮正式测量,每轮从独立空应用缓存开始;交替版本顺序,整个序列共 48 个案例。没有清空操作系统文件缓存。编译器、编译参数、链接库、各案例的生成 C/头文件/XML 均核对一致。统计是两组各自的中位数。
本表计时包含构建函数及紧邻的使用锁释放/容量清理,不含 Python 导入、XML/C 生成和求解。旧版没有对象阶段细分,缺失值保留为空,不用差值伪造。
| 场景 | 原版 | 新版 | 变化 |
| --- | ---: | ---: | ---: |
| 八路首次构建(空应用缓存) | 3.7742 s | 2.4875 s | 减少 34.09% |
| 相同八路再次运行 | 0.0347 s | 0.1632 s | 增加 128.6 ms |
| 八路改一个管长参数 | 3.9009 s | 2.4838 s | 减少 36.33% |
| 改参数后切回原八路 | 0.0335 s | 0.1868 s | 增加 153.4 ms |
| 八路切换到修正四路 | 2.4908 s | 1.1109 s | 减少 55.40% |
| 四路切回已缓存八路 | 0.0332 s | 0.1580 s | 增加 124.9 ms |
参数案例仅在隔离副本中将 `amesim_pnl0001_1.le` 从 1 改为 1.001 m;`tests/data` 正式模型未改。切四路使用现有修正四路工程,最后重新用原八路。
新版首次编译 12 个单元;仅改管长时复用 11 个、重编 1 个;切四路复用 7 个、重编 5 个。八路和四路实际上都使用五类功能模块,所以这组速度改善来自并行构建和对象复用,不能归结为它们省掉了某个未用元件模块。未用模块不会触发编译/失效的行为另有专项测试。
| 新版场景 | 预处理墙钟 | 并行编译墙钟 | 链接墙钟 |
| --- | ---: | ---: | ---: |
| 八路首次 | 0.0965 s | 2.2163 s | 0.0490 s |
| 八路改参数 | 0.0925 s | 2.2261 s | 0.0494 s |
| 切换四路 | 0.0956 s | 0.8880 s | 0.0490 s |
| 相同八路完整命中 | 0.0966 s | 0 | 0 |
阶段是嵌套观测值,各阶段的中位数不能相加后当作一次实测总时长。`compileSeconds` 是并行任务墙钟之和,不是 CPU 时间,也不能与 `compileWallSeconds` 累加。
仍有两点代价:
- 完整命中仍做预处理和内容校验,检查从 34.7 ms 增到 163.2 ms。保留这层检查可准确识别头文件/宏变化;本轮不把它描述为命中速度优化。
- 八路生成的 `model.c` 单独编译仍约 2.23 s,占主要成本。改一个参数虽然少编 11 个单元,墙钟仍接近新版首次构建;本轮没有把模型参数改成运行时载入。
运行序列结束时,旧缓存逻辑文件 4,230,318 字节,新缓存 5,064,224 字节(其中共享对象 788,363 字节),增加约 19.7%。首次单模型分别为 1,638,937 / 2,034,665 字节。新增对象池用一些空间换取重用,容量预算负责控制后续增长。性能案例没有触及预算;实际淘汰在独立并发/LRU 测试中验证。
## 数值与网页验证
修正八路 0~10 s、BDF、`rtol=1e-8` 完整运行。原生 `series/final/finalState` 的 **1,790,486 个 binary64 值逐位一致**,包括正负零。参考来自上一轮已接受的着色雅可比结果。`nfev=22853`、`njev=433`、雅可比 RHS 12124、普通 CVODE RHS 10729、接受步 6660、拒绝步 359、事件 1、启动 4、雅可比回退 0,均未变化。
用生产前端静态资源和真实浏览器在临时 8024 服务执行 1 次预热 + 3 次测量。首次完整模型未命中,但已有 7 个对象可用,因此它不是上表的全空缓存案例。首次构建 2.5649 s、点击到可查看 6.1815 s。随后三次中位数:
| 网页过程 | 耗时 |
| --- | ---: |
| 后台构建缓存检查 | 0.1976 s |
| C 求解 | 2.6489 s |
| 点击运行 → 结果可查看 | 3.7586 s |
| 点击运行 → 浏览器 IndexedDB 保存完成 | 3.8605 s |
| CSV 点击 → 下载保存完成 | 1.0348 s |
可查看是完成状态和可用按钮的 DOM 观测;保存是事务完成后的索引发布观测;下载包含浏览器通知及自动化 `saveAs`。过程存在重叠,不能累加。此次是本机回环地址验证,未复测远程端口转发链路,也不拿历史网页样本计算本轮配对加速比。
4 次完整网页结果、CSV、刷新恢复均与参考逐数值一致,共核对 7,154,280 个 CSV 单元;所有 CSV 文件字节相同。前端只调整了准备阶段文案/进度识别,显示生成、检查缓存、编译及链接的实际阶段。
本轮未重新运行 Amesim 软件或给出新增 Amesim 速度结论。四路已有外部曲线基准仍纳入回归,门槛保持不变;八路数值结论是对已接受的 native 版本等价。
## 回归与适用范围
- 93 项后端回归通过(54.123 s):元件/物性/管路、50 个网络冻结基准、四路曲线门槛、BDF/RK45、生成器、事件、取消、雅可比、结果接口等。
- 收尾构建/存储专项 37 项通过(7.397 s,与前述部分重叠):真实 GCC 构建、跨模型复用、参数/头文件/宏/编译选项/链接库失效、缓存完整性、编译链接失败、并发发布、活跃进程期间清理、LRU/软预算、符号链接、清理失败、被杀进程遗留目录回收。
- 前端构建通过;仿真超时与活动看门狗 12 项通过;实际浏览器四轮下载与恢复核对通过。
- 诊断工具已适配新的模块结构,避免聚合入口和模块重复编译;历史管路插桩工具做了语法/插桩准备检查,本轮未重跑其整套历史性能实验。
实际平台为 Linux x86_64、Python 3.12.3、GCC 13.3.0、SUNDIALS 7.4.0。Windows 使用 `LockFileEx` 的共享/排他锁实现,但没有 Windows 实机测试。最终补充生成 C/H 显式 LF 写出,避免 Windows 默认 CRLF 导致身份校验不一致;以模拟平台换行的真实构建回归验证。该补充在正式计时之后,对本次 Linux 写出字节没有影响,未为此重复 48 次计时。来源摘要分别保存计时版本与最终版本 SHA。
## 记录与复现
轻量 [机器可读摘要](assets/2026-09-12/native-build-cache-summary.json) 保存场景统计、来源 SHA、容量规则和网页核对。原始记录位于 Git 忽略的 `test/incremental-build-20260912/`:
- `baseline-source/`、`source-manifest.json`:原版本冻结源码与清单。
- `benchmark/summary.json`、`benchmark/timings.csv`、各轮案例目录:48 次构建记录。
- `browser/summary.json`、`browser-equality.json`、`native-bitwise-equality.json`:网页计时与一致性。
- `regression.log`、`frontend-build.log`、`frontend-regression.log`:验证日志;最终换行专项结果另见摘要。
```sh
.venv/bin/python tests/manual/benchmark_native_build_cache.py \
--baseline-root test/incremental-build-20260912/baseline-source \
--output-dir test/incremental-build-20260912/reproduction \
--warmups 1 --repeats 3 --run
.venv/bin/python -m unittest tests.test_native_build_cache tests.test_native_cache_storage -v
```
本轮没有安装新环境,环境/缓存/大结果均留在忽略目录。5173、8000 保持停止,临时 8024 已停止。此前结果传输压缩仍只在备份目录,未启用到生产。改动保留在工作区,未提交或推送 Git。