# 求解器时间剖析与计数调查 日期: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)。