优化 Jacobian 确定性复用并补充性能剖析与平台依赖文档

在单次 Jacobian 构建内按完整输入精确复用储气物性、PH 反算、密度和管路求根结果,保持原有求值副作用、差分政策与失败回退。八路模型求解 CPU 中位数减少 19.27%,循环和不循环的完整原始采样均与恢复基线一致。

增加独立的跨平台时间剖析工具,记录互斥阶段耗时、Newton/LU 统计、矩阵复用与内核复用,保存 UD00 两种工况的调查报告和机器可读汇总。

补充 Windows/Linux 运行、测试、原生编译和剖析所需依赖文档及索引,不修改依赖清单、版本锁或安装环境。

验证:8 项新增专项回归通过;2270 次完整 Jacobian 核对零差异;16 次剖析配对及预热运行保持完整数值一致。既有固定样本哈希失败和 Linux 实机验收限制见报告。
This commit is contained in:
ljz committed 2026-09-16 13:39:53 +08:00
1 parent 2b07d996cf
commit 1aac220084
26 files changed
+2293 -13

No files matched your search

@@ -0,0 +1,105 @@
# Jacobian 确定性复用与 UD00 验收
日期:2026-09-15。
## 结论与版本
已将方向一的工作区改动(含未跟踪源文件)保存到 Git stash,再从干净基线独立实施方向二。方向二在当前八路模型上将求解 CPU 时间减少 **19.27%**,完整原生工作进程耗时减少 **17.56%**。循环和不循环的完整原始采样均与基线逐字节一致。
- 方向一备份:`stash@{0}`,固定对象 `06b161c309644bb01aa402ff79f34749ff8e889a`。
- 备份名称:`codex-direction-one-purpose-evaluation-before-jacobian-2026-09-15`。
- 恢复后的基线:`2b07d996cff295d9f401dd32edd10472238bdf2d`,分支 `system-optimization`。
- 本轮修改保留在工作区,尚未提交;未重新应用方向一。原有忽略目录中的验收数据仍保留。
- 所有“优化前”均指上述恢复基线。交付核对确认两种优化后构建的 36 个源文件哈希、输入哈希及重新生成的 `model.c` / `model.h` 与实测冻结版本一致。
## 实施范围与通用性
在一次 Jacobian 构建中记录未扰动基准的确定性结果。分组扰动后,完整输入的浮点位全部一致才复用;输入变化时调用原始计算。每次 Jacobian 请求重新清空工作区。
覆盖储气物性、压力—焓温度反算、密度,以及管路标量求根。保留原有物性缓存的查询/填充顺序、温度观察、数值有效性检查和失败行为。普通 RHS、接受步检查、输出计算继续使用原路径,原有着色、差分步长和 Dense LU 不变。
复用依据实际物理输入与完整介质参数,不依赖模型实例名称、支路数或固定拓扑。使用已有内核的新模型自动接入,无需增加注册参数或维护另一套方程;新增底层内核需要审查完整输入和副作用后才能加入复用。
工作区使用堆内存。标量缓存为 1024 槽、每次至多探测 32 槽;内存不足、表满或未命中时执行原计算。分组计算失败时沿用原有未缓存逐列差分恢复。独立矩阵核对发现差异时采用参考矩阵并关闭后续着色/复用。
本轮未引入解析 Jacobian 或自动微分。现有切换、限位及隐式求根需要先明确导数政策,再评估这些方法。实现细节见 [技术说明](../standard/native-jacobian-reuse.md)。
## 正式性能对比
环境为同一台 Windows 11 机器、CVODE 7.4 BDF。输入 `tests/data/test-mql-8-corrected.json`,132 个状态、1784 个输出,两个 UD00 均不循环。仿真 0–10 s,采样间隔 0.01 s,rtol 为 1e-8,保持原有 atol 和求解设置。
编译及重型测试结束后,每个版本预热一次,排除预热,交替串行测量三次,报告中位数。完整工作进程计时包含原始数据写盘、输出回放和 JSON 写出,排除编译、API 及浏览器。带矩阵核对的运行不参与计时统计。
| 指标 | 优化前 | 优化后 | 耗时减少 |
|---|---:|---:|---:|
| 求解 CPU | 7.296875 s | 5.890625 s | 19.27% |
| 求解实际耗时 | 7.242260 s | 5.876553 s | 18.86% |
| 完整原生工作进程 | 7.852573 s | 6.473363 s | 17.56% |
六次正式运行的完整状态文件、输出文件、最终值、物性告警及原有求解计数全部相同:1002 个采样,nfev 44467,接受步 10840,拒绝步 918,Jacobian 896,LU 3106,状态转换 1,求解器启动 4。nfev 保持不变;收益来自每次求值内部减少了昂贵计算。
该工况的 Jacobian 优化路径计数如下,范围仅为基准/分组探针,不代表整个求解器的计算量:
| 内核 | 实际计算 | 复用 |
|---|---:|---:|
| 储气物性 | 150568 | 1254360 |
| 压力—焓温度反算 | 85283 | 601820 |
| 密度 | 440295 | 2479392 |
| 管路求根 | 196345 | 884176 |
原生可执行文件由 400314 字节增至 411613 字节。构建缓存状态未配平,不据此报告冷编译性能变化。以上性能收益限于当前机器及工况,不推断所有模型的加速比例。
## UD00 循环与不循环
按实际项目 JSON 修改两个 UD00 的 `iscyclic`,通过正常生成器分别构建。每个版本、每种工况运行一次 0–21.7 s,覆盖 10.8 s 和 21.6 s 的周期边界。
| 两个 UD00 的配置 | 采样数 | 优化前 CPU | 优化后 CPU | 全部原始数据及原有计数 |
|---|---:|---:|---:|---|
| 均不循环 | 2172 | 8.093750 s | 6.453125 s | 完全一致 |
| 均循环 | 2175 | 10.796875 s | 8.750000 s | 完全一致 |
这些时间是单次工况观察,正式性能结论使用上一节的三次中位数。
- 不循环:nfev 50092,接受步 11006,拒绝步 884,Jacobian 1073,LU 3287,状态转换 1,求解器启动 5。
- 循环:nfev 66495,接受步 15731,拒绝步 1334,Jacobian 1374,LU 4700,状态转换 4,求解器启动 10。
- 完整比较包含所有状态和输出采样、最终状态/输出、完整物性告警和上述计数;并非只比较曲线外观或最终值。本轮没有改变 UD00 信号语义。
## 独立 Jacobian 验证
`--verify-jacobian` 使用未缓存的 canonical 逐列差分,核对矩阵每个元素。
| 工况 | 核对矩阵数 | 不匹配数 | 与对应普通运行的数值结果 |
|---|---:|---:|---|
| 不循环,0–10 s | 896 | 0 | 完全一致 |
| 循环,0–21.7 s | 1374 | 0 | 完全一致 |
共 **2270 次矩阵核对,零差异**。核对运行的 nfev 增量分别严格等于 `896 × 132` 与 `1374 × 132`;原步数、事件、完整采样及复用计数不变。这避免把重复调用缓存路径误当作独立验证。
## 回归与验收限制
新增内核/生成器测试覆盖精确输入键、各介质参数的 1 ULP 变化、正负零、失败、冲突、容量耗尽、记录关闭、观察与缓存副作用、反向管流和活动容积。测试理想气体和氦气、热状态和多变储气;全模型 RHS/输出在所有状态逐项扰动及非有限输入下与原路径逐位对照。
新增运行时测试使用 SUNDIALS 公共类型编译生产 `cvode_solver.c`,覆盖基准重置、原始差分步长、无可选工作区、分组失败恢复、取消、故意破坏复用结果后由独立核对捕获并停用优化。
相关回归包括物性、管路、生成器、依赖排序、守恒、告警、分块存储、摩擦及 Jacobian。unittest 汇总为 **Ran 52 tests,FAILED (errors=1, skipped=1)**,已实际执行的用例通过,但不能称全量验收通过:
1. `NativeJacobianProbeTests.setUpClass`:旧固定状态样本的输入 SHA 与当前物理输入不符。当前输入为 `60ed16c71b72be4ae71ca7980f4f8cdcdf6b4c4c4c8950863c2390960b71ced4`,样本要求 `670977bef67e62d9c66e8af497bada208bd72a7301be45128d185d47282cf288`。这是基线已有问题,输入及样本均未在本轮修改,也未更新样本以绕过保护;已排除换行差异。
2. `NativeJacobianRuntimeTests`:旧测试使用 Linux SUNDIALS 7.4 私有 ABI/静态库,在 Windows 跳过。新增公共类型运行时测试在 Windows 通过,但不替代真实 Linux 验收。
当前没有可用的 Linux 容器或 WSL 运行环境,未完成 Linux 编译/运行验收;本轮也未重新执行完整 Amesim 对照或浏览器端全流程。数值一致结论限定为同平台恢复基线与方向二的前后比较。
## 复现与证据
- 比较工具:[benchmark_native_jacobian_reuse.py](../../tests/manual/benchmark_native_jacobian_reuse.py)。`prepare` 冻结对应源码构建,`run` 保存单次测量及原始数据,`report` 断言全部对比项。
- 本地证据目录:`test/jacobian-reuse-20260915/`(项目忽略的验收数据目录,不随源码提交)。
- `comparison.json`:六次正式测量、两种 UD00 工况、矩阵验证和数值一致性断言。
- `before/`、`after/`、`before-cyclic/`、`after-cyclic/`:冻结构建、输入/源文件哈希与构建清单。
- 各运行子目录:`states.bin`、`outputs.bin`、`result.json`、`measurement.json`、日志。
- `regression.log`:相关回归完整输出;`delivery-verification.json`:交付源文件与实测生成文件的一致性核对。
| 构建 | buildKey |
|---|---|
| 基线,不循环 | `80cf19d21e41cb5662f934232c7a2c5302eb7fc5b91f82da3cb23f14e3f9b885` |
| 方向二,不循环 | `7fe268c97557c3e1c5a511ea5e28cf0f0479a087031abe1a38d7007dde3c4ca0` |
| 基线,循环 | `a74d52ffe2daef7ccba5030dec75209f96d39f1bc7b47436ba5351fab08a3a92` |
| 方向二,循环 | `a50f302b77a49d1aa28a4ee305239a36e8c9281ecdc93756b729183b8f1e0a0a` |
@@ -0,0 +1,464 @@
{
"scope": "Native worker: model initialization through result encoding and cleanup. Excludes build, process launch, API and browser. Error estimation remains inside CVODE controller. Dense LU has no symbolic factorization. Explicit Newton uses the same library implementation.",
"cases": [
{
"name": "noncyclic",
"meanTotalSeconds": 7.6562506666667405,
"phases": {
"residual_evaluation": {
"seconds": 2.0878067666756883,
"percent": 27.269310496395295
},
"jacobian_assembly": {
"seconds": 2.739998700024747,
"percent": 35.78773500656092
},
"numerical_factorization": {
"seconds": 0.23652539999102373,
"percent": 3.089311077820268
},
"linear_solve": {
"seconds": 0.17955199999232718,
"percent": 2.3451687752864254
},
"linear_system_setup_other": {
"seconds": 0.10677826666005785,
"percent": 1.3946547900389645
},
"newton_overhead": {
"seconds": 0.012538333336427362,
"percent": 0.16376597217507388
},
"cvode_controller_including_error_estimation": {
"seconds": 0.021977399989888607,
"percent": 0.28705172997499034
},
"event_detection": {
"seconds": 0.001956633334733245,
"percent": 0.025556025003882137
},
"accepted_property_checks": {
"seconds": 1.1713370666645158,
"percent": 15.29909504875804
},
"sampling_and_storage": {
"seconds": 0.0096981666655059,
"percent": 0.12666992092786503
},
"output_replay": {
"seconds": 0.2987757666667979,
"percent": 3.9023770207470787
},
"result_encoding": {
"seconds": 0.777298566666559,
"percent": 10.152470190803813
},
"poll_and_progress": {
"seconds": 0.006268433334904937,
"percent": 0.0818734078573996
},
"framework_other": {
"seconds": 0.005739166663564295,
"percent": 0.0749605376499895
}
},
"meanScopes": {
"total": {
"inclusiveSeconds": 7.6562506666667405,
"exclusiveSeconds": 0.00013766666703910838
},
"integration": {
"inclusiveSeconds": 6.578948899999887,
"exclusiveSeconds": 0.005601499996525187
},
"cvode_controller": {
"inclusiveSeconds": 5.389218199991167,
"exclusiveSeconds": 0.021977399989888607
},
"rhs": {
"inclusiveSeconds": 2.0841626000111924,
"exclusiveSeconds": 2.082771766674341
},
"nonlinear_residual": {
"inclusiveSeconds": 2.0881416333457614,
"exclusiveSeconds": 0.005035000001347119
},
"jacobian": {
"inclusiveSeconds": 2.7426492000089033,
"exclusiveSeconds": 0.02242556667806639
},
"jacobian_probe": {
"inclusiveSeconds": 2.720223633330837,
"exclusiveSeconds": 2.7175731333466806
},
"linear_setup": {
"inclusiveSeconds": 3.085952866659985,
"exclusiveSeconds": 0.10677826666005785
},
"numerical_factorization": {
"inclusiveSeconds": 0.23652539999102373,
"exclusiveSeconds": 0.23652539999102373
},
"linear_solve": {
"inclusiveSeconds": 0.17955199999232718,
"exclusiveSeconds": 0.17955199999232718
},
"newton": {
"inclusiveSeconds": 5.366184833334501,
"exclusiveSeconds": 0.012538333336427362
},
"event_detection": {
"inclusiveSeconds": 1.181901733331415,
"exclusiveSeconds": 0.001956633334733245
},
"accepted_property_checks": {
"inclusiveSeconds": 1.1713370666645158,
"exclusiveSeconds": 1.1713370666645158
},
"sampling": {
"inclusiveSeconds": 0.00887103333207051,
"exclusiveSeconds": 0.0008239000012508768
},
"append": {
"inclusiveSeconds": 0.008658133330906518,
"exclusiveSeconds": 0.000971733330970892
},
"flush": {
"inclusiveSeconds": 0.007902533333284131,
"exclusiveSeconds": 0.007902533333284131
},
"output_replay": {
"inclusiveSeconds": 0.2987757666667979,
"exclusiveSeconds": 0.2987757666667979
},
"result_encoding": {
"inclusiveSeconds": 1.076074333333357,
"exclusiveSeconds": 0.777298566666559
},
"poll": {
"inclusiveSeconds": 0.006268433334904937,
"exclusiveSeconds": 0.006268433334904937
}
},
"symbolic_factorization": {
"seconds": 0,
"status": "not applicable: dense LU"
},
"error_estimation": {
"seconds": null,
"status": "included in cvode_controller_including_error_estimation"
},
"counters": {
"accepted_steps": 11006,
"residual_evaluations": 20048,
"linear_rhs_evaluations": 0,
"jacobian_evaluations": 1073,
"linear_setups": 3287,
"error_test_failures": 884,
"newton_iterations": 20037,
"newton_failures": 978,
"nonlinear_step_failures": 179,
"counter_segments": 5,
"jacobian_reuses": 2214,
"jacobian_refresh_setups": 1073,
"linear_setup_failures": 0,
"LU_factorization_failures": 0,
"LU_solve_failures": 0,
"event_model_evaluations": 0,
"scheduled_boundary_count": 3,
"rejected_steps": 1063,
"application_accepted_steps": 11006,
"same_time_returns": 0,
"max_same_time_streak": 0,
"nonlinear_residual_calls": 20037,
"jacobian_probe_evaluations": 30044,
"LU_factorizations": 3287,
"LU_solves": 20037,
"newton_solve_calls": 12069,
"event_count": 1,
"event_detection_calls": 11006,
"solver_starts": 5,
"all_counted_model_evaluations": 50092,
"accepted_property_checks": 11007,
"sample_count": 2172,
"jacobian_kernel_reuse": {
"gasEvaluations": 180304,
"gasReuses": 1502160,
"phEvaluations": 105403,
"phReuses": 732566,
"densityEvaluations": 531697,
"densityReuses": 2979837,
"pipeEvaluations": 235272,
"pipeReuses": 1063254
}
},
"medians": {
"control": {
"solveSeconds": 6.583620599999904,
"solveCpuSeconds": 6.59375,
"processWallSeconds": 7.7777962999998635
},
"profiled": {
"solveSeconds": 6.638836199999787,
"solveCpuSeconds": 6.6875,
"processWallSeconds": 7.830034699999942
}
},
"observedOverheadPercent": {
"solveSeconds": 0.8386813784482694,
"solveCpuSeconds": 1.4218009478673022,
"processWallSeconds": 0.671634971978885
},
"allRunsFullParity": true,
"samples": 3
},
{
"name": "cyclic",
"meanTotalSeconds": 9.711948900000152,
"phases": {
"residual_evaluation": {
"seconds": 2.8323357333368526,
"percent": 29.163412642511005
},
"jacobian_assembly": {
"seconds": 3.431086966648157,
"percent": 35.32851132122517
},
"numerical_factorization": {
"seconds": 0.3293585666582051,
"percent": 3.391271618595521
},
"linear_solve": {
"seconds": 0.24083636668046893,
"percent": 2.479794417786374
},
"linear_system_setup_other": {
"seconds": 0.14310710000260465,
"percent": 1.4735157842789146
},
"newton_overhead": {
"seconds": 0.01622606665841886,
"percent": 0.1670732293331836
},
"cvode_controller_including_error_estimation": {
"seconds": 0.028731599965188554,
"percent": 0.2958376352782097
},
"event_detection": {
"seconds": 0.002641499996722511,
"percent": 0.02719845443917512
},
"accepted_property_checks": {
"seconds": 1.6209957666577186,
"percent": 16.69073615757691
},
"sampling_and_storage": {
"seconds": 0.009674700005386208,
"percent": 0.09961646323516032
},
"output_replay": {
"seconds": 0.29708673333334445,
"percent": 3.058981635841801
},
"result_encoding": {
"seconds": 0.7434664000000643,
"percent": 7.655172073650971
},
"poll_and_progress": {
"seconds": 0.008574800045759426,
"percent": 0.0882912393181897
},
"framework_other": {
"seconds": 0.007826600011261084,
"percent": 0.08058732692941743
}
},
"meanScopes": {
"total": {
"inclusiveSeconds": 9.711948900000152,
"exclusiveSeconds": 0.00014803333336506816
},
"integration": {
"inclusiveSeconds": 8.670142566666678,
"exclusiveSeconds": 0.007678566677896015
},
"cvode_controller": {
"inclusiveSeconds": 7.026683966654825,
"exclusiveSeconds": 0.028731599965188554
},
"rhs": {
"inclusiveSeconds": 2.8279145666924705,
"exclusiveSeconds": 2.825953500006411
},
"nonlinear_residual": {
"inclusiveSeconds": 2.832391033356089,
"exclusiveSeconds": 0.006382233330441522
},
"jacobian": {
"inclusiveSeconds": 3.4341274666670265,
"exclusiveSeconds": 0.027501033304664208
},
"jacobian_probe": {
"inclusiveSeconds": 3.406626433362362,
"exclusiveSeconds": 3.403585933343493
},
"linear_setup": {
"inclusiveSeconds": 3.9065931333278363,
"exclusiveSeconds": 0.14310710000260465
},
"numerical_factorization": {
"inclusiveSeconds": 0.3293585666582051,
"exclusiveSeconds": 0.3293585666582051
},
"linear_solve": {
"inclusiveSeconds": 0.24083636668046893,
"exclusiveSeconds": 0.24083636668046893
},
"newton": {
"inclusiveSeconds": 6.9960466000228125,
"exclusiveSeconds": 0.01622606665841886
},
"event_detection": {
"inclusiveSeconds": 1.632206199992955,
"exclusiveSeconds": 0.002641499996722511
},
"accepted_property_checks": {
"inclusiveSeconds": 1.6209957666577186,
"exclusiveSeconds": 1.6209957666577186
},
"sampling": {
"inclusiveSeconds": 0.008811533338606145,
"exclusiveSeconds": 0.0008969333368137692
},
"append": {
"inclusiveSeconds": 0.008516533335447699,
"exclusiveSeconds": 0.0009090666676456749
},
"flush": {
"inclusiveSeconds": 0.007868700000926765,
"exclusiveSeconds": 0.007868700000926765
},
"output_replay": {
"inclusiveSeconds": 0.29708673333334445,
"exclusiveSeconds": 0.29708673333334445
},
"result_encoding": {
"inclusiveSeconds": 1.0405531333334086,
"exclusiveSeconds": 0.7434664000000643
},
"poll": {
"inclusiveSeconds": 0.008574800045759426,
"exclusiveSeconds": 0.008574800045759426
}
},
"symbolic_factorization": {
"seconds": 0,
"status": "not applicable: dense LU"
},
"error_estimation": {
"seconds": null,
"status": "included in cvode_controller_including_error_estimation"
},
"counters": {
"accepted_steps": 15733,
"residual_evaluations": 28023,
"linear_rhs_evaluations": 0,
"jacobian_evaluations": 1374,
"linear_setups": 4700,
"error_test_failures": 1334,
"newton_iterations": 28003,
"newton_failures": 1218,
"nonlinear_step_failures": 229,
"counter_segments": 10,
"jacobian_reuses": 3326,
"jacobian_refresh_setups": 1374,
"linear_setup_failures": 0,
"LU_factorization_failures": 0,
"LU_solve_failures": 0,
"event_model_evaluations": 0,
"scheduled_boundary_count": 5,
"rejected_steps": 1563,
"application_accepted_steps": 15731,
"same_time_returns": 2,
"max_same_time_streak": 1,
"nonlinear_residual_calls": 28003,
"jacobian_probe_evaluations": 38472,
"LU_factorizations": 4700,
"LU_solves": 28003,
"newton_solve_calls": 17296,
"event_count": 4,
"event_detection_calls": 15731,
"solver_starts": 10,
"all_counted_model_evaluations": 66495,
"accepted_property_checks": 15732,
"sample_count": 2175,
"jacobian_kernel_reuse": {
"gasEvaluations": 230872,
"gasReuses": 1923560,
"phEvaluations": 136587,
"phReuses": 940359,
"densityEvaluations": 685389,
"densityReuses": 3819723,
"pipeEvaluations": 301626,
"pipeReuses": 1368464
}
},
"medians": {
"control": {
"solveSeconds": 8.631554299999607,
"solveCpuSeconds": 8.65625,
"processWallSeconds": 9.813595800000257
},
"profiled": {
"solveSeconds": 8.620837400000255,
"solveCpuSeconds": 8.640625,
"processWallSeconds": 9.760674299999664
}
},
"observedOverheadPercent": {
"solveSeconds": -0.12415956184568255,
"solveCpuSeconds": -0.18050541516245744,
"processWallSeconds": -0.5392671664813387
},
"allRunsFullParity": true,
"samples": 3
}
],
"evidence": {
"revision": "2b07d996cff295d9f401dd32edd10472238bdf2d",
"compiler": "gcc.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0",
"platform": "win32",
"stop": 21.7,
"settings": {
"method": "BDF",
"start": 0,
"sample_step": 0.01,
"rtol": 1e-08,
"max_step": 1e+30
},
"directionOneStash": "06b161c309644bb01aa402ff79f34749ff8e889a",
"normalSourcesUnchanged": true,
"generatedModelIdentical": true,
"runsWithFullParity": 16,
"repeats": 3,
"warmups": 1,
"builds": [
{
"name": "noncyclic",
"ud00Count": 2,
"buildKey": "7fe268c97557c3e1c5a511ea5e28cf0f0479a087031abe1a38d7007dde3c4ca0",
"inputSha256": "f16fc251c10073a4155b56060625e622d81cbc141779dc52fa44746ae8654b5b",
"controlSha256": "ee51ac31f625391ede4457d909968dae856471e66f19f1ae6d614a0735dc20d6",
"profiledSha256": "06c56f6963e7e3ea6930b1fc1f832231b97eb9feb231d60a3b0bed5b8d28fd01"
},
{
"name": "cyclic",
"ud00Count": 2,
"buildKey": "a50f302b77a49d1aa28a4ee305239a36e8c9281ecdc93756b729183b8f1e0a0a",
"inputSha256": "38189b2d3608ca8dfc2691dd8e60b634de499b8d7c35f94ea1a3e72e9b58eae3",
"controlSha256": "02041b6b16a2aca2d26f3ef727d995e67595ff094387615f66e8b7f759626f3d",
"profiledSha256": "705948a62df23999084223e8e96269b65741de3f913c731041f42eecaaa8de27"
}
]
}
}
@@ -0,0 +1,163 @@
# 求解器时间剖析与计数调查
日期:2026-09-16。
## 结论
方向二优化后的当前求解器,主要成本仍是 **Jacobian 基准/扰动求值、普通 residual 求值,以及接受步物性检查**。两种工况下它们合计占完整原生运行约 78%–81%。Dense LU 分解与线性求解合计约 5%–6%;Newton 控制、事件检测及步长/阶数控制所占比例很小。
本轮交付独立诊断工具和调查结果。计时加入 `test/` 下的源码副本,当前生产求解器未额外修改。方向一仍保存在 `stash@{0}`(`06b161c309644bb01aa402ff79f34749ff8e889a`)。
## 工况、测量与数值保持
- 当前八路模型:132 个状态、1784 个输出;Windows 11,CVODE 7.4 BDF。
- 两个 UD00 均不循环、两个 UD00 均循环,分别运行 0–21.7 s,覆盖 10.8 / 21.6 s 边界。
- 采样间隔 0.01 s,rtol 1e-8,保持既有 atol,max_step 1e30。
- 所有构建结束后,每种工况各进行一组预热和三组正常/插桩串行配对测量;预热排除。
- 16 次运行的完整结果 JSON(仅排除计时字段)、全部状态/输出二进制、警告及原有计数均通过一致性检查。
- 两种工况每次重复的新计数也完全一致。原始源码哈希及生成 C/头文件已核对。
- 计时器嵌套、提前返回、goto 清理及源码锚点保护测试:2 项通过。
以下 Total simulation 指原生进程内从初始化到结果写出和清理的完整工作,不含构建、进程启动、API 和浏览器。各阶段采用单调墙钟,使用扣除子调用后的互斥时间;三次取平均,分项可加总。求解/进程总时长中位数另列。
## 时间树
### UD00 不循环
```text
Total simulation 7.656251 s (100%)
├── symbolic factorization N/A(Dense LU)
├── error estimation 独立计时 未分离,包含在下方 CVODE controller 组
├── residual evaluation 2.087807 s 27.27%
├── Jacobian assembly (含探针) 2.739999 s 35.79%
├── numerical factorization 0.236525 s 3.09%
├── linear solve 0.179552 s 2.35%
├── linear setup / 矩阵缩放等 0.106778 s 1.39%
├── Newton overhead 0.012538 s 0.16%
├── CVODE controller / error estimation 等 0.021977 s 0.29%
├── event detection 0.001957 s 0.03%
├── accepted-step property checks 1.171337 s 15.30%
├── sampling / storage 0.009698 s 0.13%
├── output replay 0.298776 s 3.90%
├── result encoding / 写出 0.777299 s 10.15%
├── poll / progress 0.006268 s 0.08%
└── framework / other 0.005739 s 0.07%
```
其中积分循环平均耗时为 **6.578949 s**。
### UD00 循环
```text
Total simulation 9.711949 s (100%)
├── symbolic factorization N/A(Dense LU)
├── error estimation 独立计时 未分离,包含在下方 CVODE controller 组
├── residual evaluation 2.832336 s 29.16%
├── Jacobian assembly (含探针) 3.431087 s 35.33%
├── numerical factorization 0.329359 s 3.39%
├── linear solve 0.240836 s 2.48%
├── linear setup / 矩阵缩放等 0.143107 s 1.47%
├── Newton overhead 0.016226 s 0.17%
├── CVODE controller / error estimation 等 0.028732 s 0.30%
├── event detection 0.002641 s 0.03%
├── accepted-step property checks 1.620996 s 16.69%
├── sampling / storage 0.009675 s 0.10%
├── output replay 0.297087 s 3.06%
├── result encoding / 写出 0.743466 s 7.66%
├── poll / progress 0.008575 s 0.09%
└── framework / other 0.007827 s 0.08%
```
其中积分循环平均耗时为 **8.670143 s**。
### 关键边界
- residual 行是普通 RHS 加非线性 residual 的代数组装,不包含 Jacobian 探针。
- Jacobian 行包含基准/扰动模型求值和矩阵装配;普通 residual 与它不重复相加。
- symbolic factorization 不适用于当前稠密 LU,并非已经测出一个稀疏符号分解耗时为零。
- 误差估计在预编译 SUNDIALS 内部无独立计时入口,记录为 null;CVODE controller 还包含预测、历史更新、步长/阶数控制等,不能把整组冒称为误差估计。
- Newton overhead 已扣除 residual、Jacobian、线性 setup、分解和回代;包括剩余收敛判断、控制和接口开销。
- 事件检测已扣除物性检查及普通采样。result encoding 包含 JSON 序列化和文件写出,不能解释为纯编码算法的 CPU 耗时。
## 求解计数
| 参数 | 不循环 | 循环 |
|---|---:|---:|
| `accepted_steps` | 11006 | 15733 |
| `application_accepted_steps` | 11006 | 15731 |
| `same_time_returns` | 0 | 2 |
| `rejected_steps` | 1063 | 1563 |
| `error_test_failures` | 884 | 1334 |
| `nonlinear_step_failures` | 179 | 229 |
| `residual_evaluations` | 20048 | 28023 |
| `nonlinear_residual_calls` | 20037 | 28003 |
| `jacobian_probe_evaluations` | 30044 | 38472 |
| `jacobian_evaluations` | 1073 | 1374 |
| `jacobian_reuses` | 2214 | 3326 |
| `LU_factorizations` | 3287 | 4700 |
| `LU_solves` | 20037 | 28003 |
| `newton_solve_calls` | 12069 | 17296 |
| `newton_iterations` | 20037 | 28003 |
| `newton_failures` | 978 | 1218 |
| `event_count` | 1 | 4 |
| `scheduled_boundary_count` | 3 | 5 |
| `event_detection_calls` | 11006 | 15731 |
| `accepted_property_checks` | 11007 | 15732 |
| `solver_starts` | 5 | 10 |
| `all_counted_model_evaluations` | 50092 | 66495 |
| `sample_count` | 2172 | 2175 |
两种工况的 linear_setup_failures、LU_factorization_failures、LU_solve_failures、linear_rhs_evaluations、计数接口错误均为 0。
### 三处容易误读的计数
1. **rejected_steps 不等于旧 rejectedSteps。** 旧字段仅含误差检验失败:884 / 1334;加上非线性求解导致的步失败 179 / 229,本次完整拒绝尝试计数为 1063 / 1563。Newton 内部失败 978 / 1218 可能通过刷新 J 重试恢复,不全部导致拒绝步。
2. **循环的内部成功步 15733 与应用接受步 15731 都保留。** 差额对应两次 same_time_returns;原应用对时间未推进的返回不执行接受步事件/采样流程。这不是插桩造成的轨迹变化。
3. **jacobian_reuses 是矩阵复用。** 统计成功线性 setup 前后 J 刷新计数未增加的次数,分别为 2214 / 3326;它与计算新 J 时的物性内核复用不同。LU_solves 直接统计 Dense solve 调用,不能以 Krylov 迭代次数代替。
额外一致性关系在本次两种工况均成立:
- Newton solve 调用数 = CVODE 内部成功步 + 两类拒绝步。
- LU 分解次数 = Jacobian 刷新 setup + 沿用 J 的 setup。
- 原 nfev = 普通 RHS + Jacobian 探针(本模型无额外摩擦求值)。
- 累加计数段数 = solver_starts;覆盖所有事件及预定时间边界的 ReInit。
### 方向二内核复用
| 内核 | 不循环:计算 / 复用 | 循环:计算 / 复用 |
|---|---:|---:|
| gas | 180304 / 1502160 | 230872 / 1923560 |
| ph | 105403 / 732566 | 136587 / 940359 |
| density | 531697 / 2979837 | 685389 / 3819723 |
| pipe | 235272 / 1063254 | 301626 / 1368464 |
这些内核计数仅覆盖新 Jacobian 的基准/分组探针,不代表整个运行的全部物性调用。
## 插桩扰动
| 工况 | 原求解中位数 | 插桩求解中位数 | 观测差值 | 原进程中位数 | 插桩进程中位数 |
|---|---:|---:|---:|---:|---:|
| noncyclic | 6.583621 s | 6.638836 s | +0.84% | 7.777796 s | 7.830035 s |
| cyclic | 8.631554 s | 8.620837 s | -0.12% | 9.813596 s | 9.760674 s |
观测扰动约为百分之一或以内;负差值属于运行波动,不代表插桩带来优化。没有从各阶段强行扣除估算的计时器成本,小于毫秒级的项目不宜过度解释。时间树采用三次平均,此表采用中位数,且进程时长含启动,三者不应直接相减当作某个新阶段。
## 下一步优化判断
1. **Jacobian 仍是第一热点。** 约 35%–36% 的完整运行时间位于该阶段;扣除子调用的装配/分组管理仅约 0.022 / 0.028 s,绝大多数成本仍在模型探针。下一步应继续研究受扰动依赖范围及昂贵内核的导数计算;单纯优化矩阵填充循环收益有限。
2. **普通 residual 与接受步物性检查值得分别优化。** 普通 residual 占约 27%–29%,物性检查占约 15%–17%。这支持减少诊断用途完整重算的方向,但不等于已暂存的方向一方案具有正收益;必须对具体裁剪/计算调度的新增成本再次实测。
3. **完整运行还存在结果处理成本。** 输出回放与 JSON 写出合计约 11%–14%。若目标是用户等待时间,应把这部分一起衡量;若目标只限积分内核,则排除这部分。
4. **本规模下暂不优先更换线性求解器。** 分解+回代约 5%–6%,加上线性 setup 其他工作约 7%。这限定了只优化线性代数的当前收益空间;不能推广到状态数更大的模型。
5. **事件与控制框架当前不是主要瓶颈。** Newton 控制和 CVODE controller 各不足 0.3%,事件检测约 0.03%。应首先减少昂贵物理求值成本,而不是从这些小项着手。
## 工具、证据与限制
- [使用与参数口径](../standard/native-solver-profiling.md)
- [可复用调查工具](../../tests/manual/profile_native_solver.py)
- [随文保存的机器可读汇总](assets/2026-09-16/solver-profile-summary.json)
- 完整原始证据:项目 `test/solver-profile-20260916/`,含冻结程序、每次原始文件、profile.json、measurement.json、summary.json 和构建日志。该目录按项目约定被 Git 忽略。
- 本轮没有重新执行全仓库、Amesim 或浏览器全流程验收;这里的正确性结论是插桩与当前生产版本的完整数值保持。
- Windows 实测通过;计时与构建代码提供 Linux 路径,真实 Linux 运行未验收。
- 使用同一 SUNDIALS 库的 Newton 实现和公共操作表;诊断版显式挂接 Newton 便于观测,并用完整数值/计数对照检验等价性,不访问 CVODE 私有结构。
计数定义参考 [SUNDIALS 7.4 CVODE 可选输出接口](https://sundials.readthedocs.io/en/v7.4.0/cvode/Usage/index.html#optional-output-functions)。