116 lines
11 KiB
Markdown
116 lines
11 KiB
Markdown
# 按需编译与分层缓存优化
|
||
|
||
日期: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。
|