Files
SystemSimulationApp/tests/manual/state_valve_tail_report.md
ljz 7611f13208 修复循环信号与事件采样并接入 LSTP 接触定位,补充八路验证及复用实验
相较上一版 Jacobian 确定性复用更新,本次补齐事件边界一致性、结果两侧采样及接触事件定位;保留已有物性复用和组件力学公式。

- 统一 UD00 信号求值与下一事件查询的绝对时间边界,修复循环边界浮点舍入导致的阶段错位、重复或漏报,并覆盖零时长、多阶段及长周期场景。
- 引入原生输出语义 v2:保留规则网格真实时间,补充内部时间事件和状态事件的左邻及事件后采样,按保存时间、状态和离散模式重放结果。
- 两条代码生成路径均发出 LSTP 接触描述,默认定位间隙过零及非负力模式的力截断;仅在接受事件时更新防重复记录,增加 contactEvents 诊断计数。
- 补充 MASS/LSTP 独立事件实验、八路全曲线与驱动阶段配对评估,以及 Amesim 不连续点输出对照和力差定位报告;MASS 新增释放机制仍保留为独立实验。
- 保存局部 probe、context 访问与回退、shadow replay、R288 real skip/typed replay 及阀门数值尾部诊断工具和报告;未证明净收益的实验不启用为生产默认优化。
- 更新原生运行说明和元件建模规范,补充信号边界、输出语义、接触事件和实验依赖回归测试。

验证:五组专项回归共 34 项全部通过;37 个待提交 Python 文件语法检查通过;git diff --cached --check 通过。
2026-09-17 23:50:13 +08:00

238 lines
16 KiB
Markdown
Raw Permalink 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.
# position379:state_valve 数值尾部诊断
日期:2026-09-17。仅针对既有八路模型 0–10 s 轨迹中,Jacobian baseline/probe 实际执行的 **position379 / PNVO001_1.port_2**。没有改生产路径,没有研究其他 position,没有实现真实 memo skip。
## 四个问题的答案
1. **尾部典型单次约0.618 µs。** 在不做前置lookup/FP采样的三轮计时对照中,逐次中位数为0.611–0.628 µs;每轮均值为0.692–0.918 µs,均值的跨轮中位数0.803 µs。均值含调度长尾,不能当作纯CPU指令时间。该数值来自本次尾部计时,不是此前0.783 µs的state_valve exclusive。
2. **完整数值key重复率84.615%。** 每个Jacobian有5种key,22个probe与baseline逐位相同;全轨迹理论命中19,712/23,296。包含完整x87状态字保护后,本次“无FP状态变化才可直接返回”的候选只允许8,734次,即37.491%。
3. **当前严格候选未达到完整机制break-even。** 五轮hit lookup均值0.152–0.475 µs,baseline新增2.088–6.619 µs/J;全部miss成本也必须计入。两轮相对平稳的配对数据要求hit成本低于0.109/0.074 µs,实测分别为0.152/0.153 µs。五轮诊断净预算全部为负,−0.367至−8.269 ms/轨迹,中位数−2.341 ms。
4. **不进入真实实现阶段,停止该方向。** 数值key确实重复,但当前候选没有证明在保留完整FP语义后能获得净收益。这不证明任何kernel memo都不可能盈利;它说明本轮没有满足启动真实实现的门槛。没有通过忽略x87状态、过滤慢样本或扩大position范围来改变结论。
## 1. 源码边界与准确输入
依据归档 [properties.c](../../test/local-probe-20260917/worker/properties.c) 第266–277行和 [orifice.c](../../test/local-probe-20260917/worker/orifice.c) 的 `native_medium_orifice_context`。使用与既有fallback诊断相同的归档worker,保证比较对象一致;这不是对工作区后来其他改动重新建模。
### A. 必须保留的context/property行为
调用链:`native_medium_orifice_context → native_temperature_ph_context → medium_valve → property_pt → state_valve`。在state_valve内,以下语句及其子调用均原样执行:
```c
double p=up->p,T=up->T;
pd=fmax(fmin(pd,p),0);
const NativeMedium *m=&up->medium;
double cp=m->cp+m->slope*(T-m->Tref);
double factor=m->real_helium?isentropic(cache,up,pd):(cp-m->R)/cp;
double g=fmax(1e-9,fmin(1-1e-9,factor));
double rho=fmax(property_density(up),1e-12);
```
这包括PH/PT查询、首次匹配、entry创建、count/valid变化、上游和下游等熵准备、density获取及温度observer行为。orifice后续的q计算、方向/opening处理、有限性检查和返回值仍执行。目标调用是orifice,没有额外跳过任何pipe逻辑;整个求解中的其他pipe/context路径保持原执行。
### B. 已有memo/valid覆盖的工作
- `local_isentropic` 先观察温度,valid命中才直接返回;不能把观察和valid语义一起memo掉。
- `property_density`、PH context获取中的已有缓存/Jacobian scalar memo继续工作。
- 先前fallback函数统计中,昂贵PH反算、密度求解、pipe resistance求根实际调用为0;本轮不把它们当作新可省工作。
### C. 本次计时的数值尾部
**起点:原源码第272行 `double r=...`;终点:第276行平滑修正完成。** `subsonic_cm` 的调用也计入该尾部。
```c
double r=fmax(pd/p,0),critical=pow(2*g/(g+1),1/(1-g)),eff;
if(r<=critical) {
eff=critical;
*cm=sqrt(2/(1+g)*rho*T/p)*pow(2*g/(g+1),g/(1-g));
*vel=sqrt(2/(1+g)*p/rho);
} else {
eff=r;
*cm=subsonic_cm(r,g,rho,T,p);
*vel=sqrt(fmax(2/(1-g)*p/rho*(1-pow(r,1-g)),0));
}
double ref=subsonic_cm(.9999,g,rho,T,p);
if(*cm>0 && ref>0) {
double smooth=tanh(fmax(12*fabs(*cm/ref)*log(eff)/log(.9999),0));
*cm*=smooth; *vel*=smooth;
}
```
- 完整数值输入:**`p, T, pd, g, rho`**,五个double的原始位,共40 B。pd已经限幅,g和rho已经完成上述准备/限幅。不能用原operation的p/h/opening作为替代key。
- 数值输出:**`cm, vel`**,两个double原始位。
- 不再读取medium、entry、valid、count或pipe,不写property context;原输出指针的既有值不是尾部输入,两条分支均先写cm/vel。
- 这只表示“数值计算不依赖context”,**不表示没有机器浮点状态副作用**。数学库和x87运算会改变部分状态字位,因此不能把整个state_valve或这个尾部无条件当作可直接缓存返回的纯函数。
## 2. 诊断方法
独立worker复制既有local probe实现,只在目标position设置诊断scope;普通residual及其他position不进入记录。原whole-context guard及其成功恢复路径保留。
- 每次目标尾部仍执行一次原数学代码。诊断lookup只把可能的缓存结果写到临时记录,不用于operation输出,**没有真实skip,也没有双路Reference执行**。
- 使用invariant TSC,`lfence/rdtsc/lfence`,全轨迹对QPC校准;原编译参数仍为O3、禁止fast-math与FP contraction。两端标记包围原尾部,没有循环重复同一个热key来替代实际轨迹。
- 所有详细记录先写预分配内存,积分结束后写盘;单记录128 B。候选baseline记录仅72 B:40 B key、16 B输出、12 B环境、4 B ready。
- 五轮完整诊断,同时直接测K、候选hit/miss lookup、baseline key/环境准备、结果捕获及每Jacobian重置。G包含外层调用、key构造、环境读取、完整比较及写回临时结果,不只测memcmp。
- 另三轮 **K-only** 对照:尾部之前只放计时标记,key/环境采样全部移到尾部之后。这三轮的环境不是入口环境,**其hits/G/H字段不参与任何命中率或盈利判断**。
- 原始单次均值、中位数和min/max全部保留,没有剔除抢占/缺页长尾。空标记中位约12–14 ns;预算只从可省K中扣除空标记,G/H保留原始计时,避免把微小收益做大。
归档初版构建与交付诊断版本的 `model.o`、`properties.o`、`valve_tail_diag.o` 字节一致;清理仅移除了未启用的预留接口,没有引入真实skip版本。
已检查两版 `state_valve` 目标码控制流:density/g准备先于起始标记,`pd/p`、critical及尾部数学调用位于计时范围内,输出写入完成后才取结束标记。汇编分别保存在构建目录的 `state-valve.asm`。
## 3. 全轨迹进入次数与尾部时间
所有轮次都完成896个Jacobian。目标物理operation进入和尾部执行次数均为 **24,192**:
| 类别 | 次数 |
|---|---:|
| baseline | 896 |
| probe group0–25 | 23,296 |
| group26 | 0:已有whole-context复用使目标operation未执行 |
| 真实skip | 0 |
分母是Jacobian内实际执行的目标probe,不含普通residual,也不把group26已有的复用计成新memo收益。
### K-only低扰动对照(每轮24,192次)
| 轮次 | 均值µs | 单次中位µs | 单次min–max µs | 原始累计ms | 扣空标记均值µs |
|---|---:|---:|---:|---:|---:|
| tail-only-0 | 0.803447 | 0.610863 | 0.377976–3595.053 | 19.436981 | 0.791542 |
| tail-only-1 | 0.691729 | 0.617559 | 0.369792–459.269 | 16.734302 | 0.679824 |
| tail-only-2 | 0.917657 | 0.627976 | 0.369047–3031.652 | 22.199961 | 0.904264 |
三轮逐次中位数的中位数 **0.617559µs**,每轮均值的中位数 **0.803447µs**,累计时间中位数 **19.436981ms**。数千µs的max明显混有系统调度长尾,不代表一次数学计算通常要这么久;这些样本没有被删除。
### 与G/H同时采集的五轮K(盈亏使用同轮配对数据)
| 轮次 | 均值µs | 单次中位µs | 单次min–max µs | 原始累计ms |
|---|---:|---:|---:|---:|
| diagnostic-0 | 0.600212 | 0.575893 | 0.360863–33.676 | 14.520341 |
| diagnostic-1 | 0.759084 | 0.680059 | 0.396577–201.298 | 18.363768 |
| diagnostic-2 | 0.843411 | 0.677083 | 0.367559–1362.520 | 20.403801 |
| diagnostic-3 | 1.176682 | 0.691964 | 0.368303–8174.892 | 28.466296 |
| diagnostic-4 | 0.642986 | 0.577381 | 0.416666–243.518 | 15.555124 |
这些结果支持尾部是亚微秒级计算,不能支持“所有测得墙钟长尾都可通过memo省掉”。K-only与完整诊断的批次/代码布局不同,不跨轮拿最贵K减最便宜G来拼净收益。
## 4. 完整数值bit-key的真实重复率
每个Jacobian的27次物理进入(1 baseline + 26 probes)均有 **5种不同key**:
- baseline与 **group0–5、10–25** 共用同一key;每Jacobian22次重复。
- group6、7、8、9各有一个不同key,彼此及baseline不同。
- 896个Jacobian全部如此;baseline-only单槽候选已经覆盖所有数值重复,增加多条probe memo不会再增加理论hit。
| 统计 | 结果 |
|---|---:|
| 理论memo hit | 19,712 |
| 理论probe miss | 3,584 |
| 数值key hit rate | 19,712 / 23,296 = **84.6153846%** |
| 相同key的cm/vel逐位不同 | **0** |
| 各组与baseline相同次数 | 上述22组分别896次 |
[每个Jacobian的baseline完整十六进制key、各组key和分组关系](../../test/position379-tail-20260917/diagnostic-0/key-groups.json)。其他四轮也各自保存清单和原始记录,并得到相同分组计数。
### 数值重复不等于可以直接返回
本次读取:errno、MXCSR全寄存器、x87 control word、x87 status word。尾部前后:
- errno、MXCSR、x87 control word变化次数均为0。
- **x87 status word变化13,374次**,其中baseline变化499/896次。
- 变化位是x87条件状态位,例如 `0x120 → 0x320` 和 `0x320 → 0x120`;本轨迹没有观察到标准浮点异常标志或舍入控制变化。不能把“标准异常标志没变”表述为“完整状态字没变”。
首版诊断候选保持保守边界:baseline key/output必须有限,所有异常被mask;baseline尾部前后上述环境完全相同,当前probe环境又与baseline一致,且完整40 B key相同,才计算为可直接返回的hit。其余情况仍原计算。没有尝试重放/修补x87状态字,也没有把条件位从比较中移除。
| 严格候选结果(每轮一致) | 次数 |
|---|---:|
| 可直接返回候选hit | **8,734 = 397 × 22** |
| 候选hit rate | **37.4914148%** |
| baseline改变x87状态,故记录不可直接复用 | 12,974次probe |
| 在可复用baseline下,probe环境不同 | 901 |
| 环境相同但key不同 | 687 |
| 总候选miss / 原执行 | 14,562 |
拒绝原因按实际检查优先级归类,环境不同和key不同可能重叠,不能将此表与3,584次纯数值miss相加。上述hit均进一步检查了真实原执行的出口环境和cm/vel:与baseline一致;但本轮没有把它接入真实skip。
这里的完整x87状态保护比仅核对C标准fenv的异常/舍入控制更严格。它明确对应本轮不放宽FP状态语义的候选;不能用其37.49%结果断言所有可能的FP兼容memo设计都只有这个命中率。
## 5. Break-even:必须收费的miss与baseline
本轮的H包括每Jacobian重置、baseline key/入口环境构造、出口环境检查、完整key/output捕获和ready判定。原baseline尾部计算本身未计为H,因为无优化时也必须执行。
```text
net = sum(K_i for accepted hits)
− sum(G_hit_i)
− sum(G_miss_i)
− sum(H_j)
```
仅用 `K > G_hit + H/22` 会出错:严格候选平均每Jacobian只有 **9.747768次hit**,同时每Jacobian有 **16.252232次miss lookup**。miss不省任何尾部工作,却仍要支付查找开销。
### 同轮测量的开销及诊断预算
G为逐次成本的均值,H为总baseline新增除以896。下表净值使用该轮可接受hit对应的原尾部时间,扣一个空标记后计算;不是根据全体K均值估算命中部分。
| 轮次 | G_hit µs | G_miss µs | H µs/J | 可省尾部ms | 全部probe lookup ms | baseline新增ms | 诊断净预算ms |
|---|---:|---:|---:|---:|---:|---:|---:|
| 0 | 0.151522 | 0.157994 | 2.161951 | 5.193716 | 3.624103 | 1.937108 | **−0.367495** |
| 1 | 0.385920 | 0.575816 | 3.331597 | 6.472028 | 11.755655 | 2.985111 | **−8.268738** |
| 2 | 0.415912 | 0.480126 | 3.563161 | 8.496013 | 10.624167 | 3.192592 | **−5.320746** |
| 3 | 0.475128 | 0.507744 | 6.618877 | 15.133467 | 11.543533 | 5.930514 | **−2.340579** |
| 4 | 0.152557 | 0.197271 | 2.087834 | 5.390453 | 4.205092 | 1.870699 | **−0.685338** |
五轮G_hit均值中位数 **0.385920µs**,H中位数 **3.331597µs/J**。H不仅是几个孤立长尾:不含重置的逐baseline成本中位数也为 **1.794–2.944µs**。没有测出H≤1µs/J。
G_hit逐次中位数较低(约0.071–0.133µs),但不能只收hit的中位数费用,再把miss和baseline账单省略。要计算轨迹净收益,必须使用累计成本。
### 具体break-even门槛
固定同轮实测H和全部miss成本后:
```text
G_hit_max = (saved_tail_work − baseline_cost − miss_lookup_cost) / hits
```
五轮上限分别为 **0.109446、−0.560810、−0.193287、0.207143、0.074089µs**。负数表示即使hit完全免费,该轮H+miss也已用完预算;其余轮的实际G_hit同样超过上限。
以较平稳的第0轮举例,命中尾部扣标记均值0.594655µs:
- 若H=1µs/J,hit与miss统一收费G,需要 **G<0.184483µs/每次probe尝试** 才盈利。因此“G≤0.25µs且H≤1µs/J”在37.49%可接受率下也不自动足够。
- 实测H=2.161951µs/J时,所有probe的平均查找预算约 **0.13979µs**;实测约0.15557µs,仍超过。
- 这是具体候选的盈亏边界,不是kernel memo的理论最低实现成本。没有测量未知的新FP状态重放设计,也不把这种未实现方案当作已有收益。
所有净预算是诊断阶段的候选费用估计,**不是实测real-skip加速**。系统负载和计时长尾较大,无法证明纯硬件成本存在绝对负收益下界;但是没有一轮形成完整账单上的正收益,故不满足“诊断显示可盈利才实现”的条件。
## 6. 数值验收与停止决定
8轮(5轮完整诊断、3轮K-only)全部与归档未修改local probe基线一致:states、outputs、events二进制、最终状态、warning和solver counters。diagnostic-0与tail-only-0另分别核对全部896个132×132 Jacobian及对应t/y,逐位一致。
| 计数 | 各轮结果 |
|---|---:|
| accepted / rejected | 10840 / 918 |
| Newton iterations / convergence failures | 19371 / 798 |
| nfev / njev / nlu | 44467 / 896 / 3106 |
| solverStarts / stateTransitions | 4 / 1 |
| 目标原尾部实际执行 | 24,192 / 24,192 |
本轮没有真实skip版本,因此没有声称完成memo版的逐evaluator property/pipe context与memo生命周期双路验收。当前验证证明诊断插桩保持了上述完整轨迹数值;将来若提出另一种盈利方案,仍须重新完成用户要求的全context/FP等真实skip验收,不能借用本轮数值一致性代替。
**停止决定:保留position379原尾部计算;不接入生产、不扩展其他position、不继续调小这套候选以追求过线。**
## 产物与复现
- [诊断构建/执行脚本](diagnose_valve_tail.py)、[只读汇总脚本](analyze_valve_tail.py)。
- [诊断接口](valve_tail_diag.h)、[诊断记录与计时](valve_tail_diag.c)。代码没有真实skip入口。
- [全部轮次统计、预算及FP变化](../../test/position379-tail-20260917/diagnostic-analysis.json)。
- [最终核验记录](../../test/position379-tail-20260917/verification.json):五轮key/output/FP记录投影逐字节相同,源文件/构建哈希及数值检查通过。
- `test/position379-tail-20260917/diagnostic-0..4/`:原始128 B记录、每Jacobian key分组、空时钟标记、逐轮校验结果。
- `tail-only-0..2/`:独立K对照;其后置环境采样不作语义证据。
- `diagnostic-build-v1/`:原五轮使用的构建及哈希;`diag-trace/`为交付诊断构建,关键目标码一致;`tail-only-trace/`为K对照构建。
```powershell
.venv-win/Scripts/python.exe -B tests/manual/diagnose_valve_tail.py prepare
.venv-win/Scripts/python.exe -B tests/manual/diagnose_valve_tail.py run --label diagnostic-new --matrices
.venv-win/Scripts/python.exe -B tests/manual/diagnose_valve_tail.py prepare --tail-only
.venv-win/Scripts/python.exe -B tests/manual/diagnose_valve_tail.py run --worker tail-only-trace --label tail-only-new --matrices
.venv-win/Scripts/python.exe -B tests/manual/analyze_valve_tail.py
```
路径均从脚本所在仓库解析;此实验使用Windows归档工具链和TSC/FP寄存器读数,没有进行Linux性能测试。