修复循环信号与事件采样并接入 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 通过。
This commit is contained in:
1 parent
1aac220084
commit
7611f13208
85 files changed
+11358
-33
No files matched your search
@@ -0,0 +1,191 @@
|
||||
"""Join exact fallback census, provenance, and separately sampled timings."""
|
||||
from pathlib import Path
|
||||
from collections import Counter,defaultdict
|
||||
import hashlib,json,statistics,struct,subprocess
|
||||
from diagnose_context_fallback import ROOT,OUT,SOURCE,ex
|
||||
|
||||
def read(label,name='context.json'):return json.loads((OUT/label/name).read_text(encoding='utf-8'))
|
||||
def lines(label,name):
|
||||
with (OUT/label/name).open(encoding='utf-8') as f:
|
||||
for line in f:yield json.loads(line)
|
||||
def table(head,rows):return '\n| '+' | '.join(head)+' |\n| '+' | '.join(['---']*len(head))+' |\n'+'\n'.join('| '+' | '.join(map(str,r))+' |' for r in rows)+'\n'
|
||||
def decode(h,floating):return struct.unpack('<d',int(h,16).to_bytes(8,'little'))[0] if floating else int(h,16)
|
||||
|
||||
def main():
|
||||
plan=read('','plan.json');census=read('census');trace=read('provenance');pairs={(x['group'],x['region']):dict(x) for x in census['regions']}
|
||||
original=json.loads((SOURCE.parent/'all-audit/probe.json').read_text(encoding='utf-8'))
|
||||
assert sum(x['failures'] for x in pairs.values())==sum(x['contextMisses'] for x in original['groups'])==139776
|
||||
assert sum(x['ops'] for x in pairs.values())==4620672
|
||||
assert all(x['inputDiffOps']==x['outputDiffOps']==x['exitOutputDiff']==0 for x in pairs.values())
|
||||
assert all(x['exitContextDiff']==x['failures'] for x in pairs.values())
|
||||
assert all(x['failures'] in (0,896) for x in pairs.values())
|
||||
regions={i:dict(id=i,start=a,end=b,contextual=plan['versions'][a]!=plan['versions'][b],groups=[],compares=0,failures=0,ops=0,seconds=0,operationSeconds=0,fields=Counter()) for i,(a,b) in enumerate(plan['regions'])}
|
||||
opregion={}
|
||||
for (g,r),x in pairs.items():
|
||||
regions[r]['groups'].append(g)
|
||||
for key in ('compares','failures','ops'):regions[r][key]+=x[key]
|
||||
x['seconds']=0;x['operationSeconds']=0;x['fields']=Counter()
|
||||
if x['failures']:
|
||||
for pos in range(regions[r]['start'],regions[r]['end']):
|
||||
assert (g,pos) not in opregion;opregion[g,pos]=r
|
||||
ops={}
|
||||
for g,pos,n,t in census['operations']:
|
||||
r=opregion[g,pos];ops[g,pos]=dict(group=g,position=pos,operationId=plan['operations'][pos]['id'],region=r,count=n,seconds=0)
|
||||
runs={};times={}
|
||||
for kind,mode in [('regions',2),('ops',3),('functions',4)]:
|
||||
runs[kind]=[read(f'{kind}-{i}') for i in range(3)];times[kind]=[read(f'{kind}-{i}','measurement.json') for i in range(3)]
|
||||
assert all(x['mode']==mode and x['sampled']==112 for x in runs[kind])
|
||||
for x in runs['regions']:
|
||||
scale=896/x['sampled']/3/x['frequency']
|
||||
for v in x['regions']:
|
||||
value=v['regionTicks']*scale;pairs[v['group'],v['region']]['seconds']+=value;regions[v['region']]['seconds']+=value
|
||||
for x in runs['ops']:
|
||||
scale=896/x['sampled']/3/x['frequency']
|
||||
for g,pos,n,t in x['operations']:
|
||||
value=t*scale;r=opregion[g,pos];ops[g,pos]['seconds']+=value;pairs[g,r]['operationSeconds']+=value;regions[r]['operationSeconds']+=value
|
||||
kernel_counts=Counter();kernel_reg=Counter()
|
||||
for r,k,n,t,u in trace['kernels']:kernel_counts[k]+=n;kernel_reg[r,k]+=n
|
||||
kernels={k:dict(name=name,module=module,count=kernel_counts[k],inclusiveSeconds=0,exclusiveSeconds=0) for k,(module,name) in enumerate(plan['kernels'])}
|
||||
region_kernels=defaultdict(lambda:dict(count=0,inclusiveSeconds=0,exclusiveSeconds=0))
|
||||
for (r,k),n in kernel_reg.items():region_kernels[r,k]['count']=n
|
||||
for x in runs['functions']:
|
||||
scale=896/x['sampled']/3/x['frequency']
|
||||
for r,k,n,t,u in x['kernels']:
|
||||
for key,val in [('inclusiveSeconds',t),('exclusiveSeconds',u)]:
|
||||
kernels[k][key]+=val*scale;region_kernels[r,k][key]+=val*scale
|
||||
origins=Counter();first_context_origins=Counter();field_totals=Counter();samples={};trace_count=0;causal=Counter();missing=[]
|
||||
for x in lines('provenance','failures.jsonl'):
|
||||
trace_count+=1;g=x['group'];r=x['region'];kind=x['kind'];fld=x['field']
|
||||
field=['count','capacity','temperatures','jacobian'][fld] if kind==1 else trace['fields'][str(kind)][fld]['name'] if fld>=0 else 'padding'
|
||||
full_field=field if kind==1 else f'{"states" if kind==2 else "pipes"}[{x["index"]}].{field}'
|
||||
key=(g,r,kind,x['index'],fld,x['persistentOrigin'],x['firstContextOrigin']);origins[key]+=1
|
||||
first_context_origins[g,x['firstContextOrigin']]+=1
|
||||
field_totals[field]+=1;regions[r]['fields'][field]+=1;pairs[g,r]['fields'][field]+=1
|
||||
pos=x['persistentOrigin'];first=x['firstContextOrigin']
|
||||
if pos<0 or first<0:missing.append(x)
|
||||
deps=set(plan['operations'][pos]['stateIndices'])&set(plan['groups'][g]['stateIndices']) if pos>=0 else set()
|
||||
causal['directFieldOrigin' if deps else 'cacheSideEffectOrigin']+=1
|
||||
rootdeps=set(plan['operations'][first]['stateIndices'])&set(plan['groups'][g]['stateIndices']) if first>=0 else set()
|
||||
causal['firstContextHasStateDependency' if rootdeps else 'firstContextNoStateDependency']+=1
|
||||
if (g,r) not in samples or x['jac']==200:
|
||||
floating=kind>1 and fld>=0 and trace['fields'][str(kind)][fld]['floating']
|
||||
samples[g,r]={**x,'fieldName':full_field,'baselineValue':decode(x['baseline'],floating),'trialValue':decode(x['trial'],floating),
|
||||
'originName':plan['operations'][pos]['key'] if pos>=0 else str(pos),'originOperationId':plan['operations'][pos]['id'] if pos>=0 else pos,
|
||||
'originPerturbedStates':[plan['stateKeys'][i] for i in sorted(deps)],'rootPerturbedStates':[plan['stateKeys'][i] for i in sorted(rootdeps)]}
|
||||
assert trace_count==139776 and not missing,(trace_count,missing[:2])
|
||||
assert field_totals==Counter(p=105741,count=34035)
|
||||
assert sum(origins.values())==139776
|
||||
# The census and provenance modes must have identical region-level counts.
|
||||
for x in trace['regions']:
|
||||
for k in ('attempts','compares','failures','ops','inputDiffOps','outputDiffOps','exitOutputDiff','exitContextDiff'):assert x[k]==pairs[x['group'],x['region']][k]
|
||||
operation_totals={}
|
||||
for (g,pos),x in ops.items():
|
||||
if pos not in operation_totals:operation_totals[pos]=dict(position=pos,operationId=x['operationId'],name=plan['operations'][pos]['key'],count=0,seconds=0,groups=[],regions=[])
|
||||
a=operation_totals[pos];a['count']+=x['count'];a['seconds']+=x['seconds'];a['groups'].append(g)
|
||||
if x['region'] not in a['regions']:a['regions'].append(x['region'])
|
||||
group_totals=[]
|
||||
for g in range(27):
|
||||
rr=[x for (gg,r),x in pairs.items() if gg==g];group_totals.append(dict(group=g,states=plan['groups'][g]['states'],
|
||||
compares=sum(x['compares'] for x in rr),failures=sum(x['failures'] for x in rr),seconds=sum(x['seconds'] for x in rr),
|
||||
failedRegions=[x['region'] for x in rr if x['failures']],ops=sum(x['ops'] for x in rr)))
|
||||
hot=sorted((r for r in regions.values() if r['failures']),key=lambda r:-r['seconds']);hotops=sorted(operation_totals.values(),key=lambda o:-o['seconds'])
|
||||
total=sum(r['seconds'] for r in hot);op_total=sum(o['seconds'] for o in hotops)
|
||||
native_ops=[o for o in hotops if plan['versions'][o['position']]!=plan['versions'][o['position']+1]]
|
||||
native_share=sum(o['seconds'] for o in native_ops)/op_total
|
||||
for r in regions.values():
|
||||
r['failedGroups']=[g for g in r['groups'] if pairs[g,r['id']]['failures']]
|
||||
r['failureRate']=r['failures']/r['compares'] if r['compares'] else None
|
||||
r['meanUs']=r['seconds']/r['failures']*1e6 if r['failures'] else 0
|
||||
r['nativeOperationPositions']=[pos for pos in range(r['start'],r['end']) if plan['versions'][pos]!=plan['versions'][pos+1]]
|
||||
# Production and pre-existing experimental source bytes are unchanged.
|
||||
for directory in ('worker','kernels','trace-worker'):
|
||||
b=read(directory,'build.json')
|
||||
for name,h in b['sourceHashes'].items():assert hashlib.sha256((SOURCE/name).read_text(encoding='utf-8').encode()).hexdigest()==h
|
||||
production=json.loads((SOURCE/'build-metadata.json').read_text(encoding='utf-8'))
|
||||
for name,h in production['sourceHashes'].items():
|
||||
assert hashlib.sha256((ex.builder.NATIVE/name).read_text(encoding='utf-8').encode()).hexdigest()==h
|
||||
assert hashlib.sha256((ROOT/'tests/data/test-mql-8-corrected.json').read_bytes()).hexdigest()==production['inputSha256']
|
||||
assert not subprocess.check_output(['git','diff','HEAD','--name-only'],cwd=ROOT,text=True).strip()
|
||||
checked=[]
|
||||
for path in sorted(OUT.glob('*/measurement.json')):
|
||||
m=json.loads(path.read_text());checked.append(m)
|
||||
result=dict(regions=list(regions.values()),groupRegions=list(pairs.values()),groups=group_totals,operations=list(operation_totals.values()),
|
||||
groupOperations=list(ops.values()),kernels=list(kernels.values()),regionKernels=[dict(region=r,kernel=k,**v) for (r,k),v in region_kernels.items()],
|
||||
provenance=[dict(group=k[0],region=k[1],kind=k[2],index=k[3],field=k[4],origin=k[5],firstContextOrigin=k[6],count=v) for k,v in origins.items()],
|
||||
fieldCounts=field_totals,causalCounts=causal,samples=list(samples.values()),measurements=checked,
|
||||
totals=dict(compares=sum(r['compares'] for r in regions.values()),failures=139776,regionSeconds=total,operationSeconds=op_total,nativeOperationShare=native_share))
|
||||
ex.write(OUT/'analysis.json',result)
|
||||
report=['**context fallback 定位报告**\n',
|
||||
'本轮只新增独立诊断工具及worker副本,原lp_reuse比较/恢复实现、dependency graph、ordinary residual、物性算法、accepted-step check及线性求解器未改动。八路模型0–10s,BDF、rtol=1e-8,其他设置沿用前轮。以下区间ID与schedule位置均从0开始,范围使用[start,end),position与operation原始ID不同,映射保存在plan.json。\n']
|
||||
report.append('**主要发现**\n')
|
||||
report.append('139,776次回退全部具有相同特征:区间各operation的显式输入、输出均与baseline逐位一致,区间出口输出也一致;但出口context/cache全部仍不一致。首次失败原因仅为states[i].p(105,741次)或count(34,035次)。这支持“全context比较/整体snapshot恢复使局部无关区间回退”的判断,不支持“回退区间方程本身受到这些状态扰动”的判断。\n')
|
||||
report.append('差异也不是假数据:前置受扰动operation确实会产生不同压力/温度的缓存条目;还有“前一operation改了缓存键→后一个物理输入不变的operation由命中变为新增条目→count变化”的间接链路。不能直接忽略这些差异并恢复整个baseline出口context,因为这会覆盖已有的真实扰动记录;本轮并未测试或声称该覆盖一定会改变最终解,也未证明放宽保护在任意模型上安全。\n')
|
||||
report.append('**计数、覆盖范围与耗时口径**\n')
|
||||
report.append(f'全量记录896个Jacobian、24,192次probe;495个可复用区间中127个包含context操作,发生160,384次context比较,139,776次失败({139776/160384:.2%}),涉及115个不同区间、156个group→区间组合。另有521,472次纯代数复用尝试不执行context比较,未混入比较分母。所有失败组合在896个Jacobian中均失败896次;其他组合全部成功。\n')
|
||||
report.append('计数与字段来自全量census/provenance。所有139,776次失败均回溯到具体operation,无未定位记录。完整属性条目新建调用链、实际变化输入及数值样例在第0、200、450、700、895个Jacobian详细记录,覆盖约0、0.373、2.458、4.721、9.958s。字段原值同时保存十六进制浮点位,不以相对误差判定相同。\n')
|
||||
report.append('耗时分三种独立模式,各3轮,每8个Jacobian分层抽1个,每轮112个,均完成完整仿真。区间计时从guard失败后的原计算开始,到区间执行结束为止,不包含context比较或字段日志;包含该区间原调度及诊断hook成本。operation计时包围原code语句;函数计时同时记录inclusive与扣除已插桩子函数后的exclusive。三种时间不能互相叠加。时间为原始插桩值,未扣空标记或强制缩放,极短操作和函数均值会高估;沿用前轮结论,不把细分时间当作精确优化收益。\n')
|
||||
controls=[read(f'control-{i}','measurement.json') for i in range(3)]
|
||||
report.append(table(['中位数','未插桩','区间抽样插桩','变化'],[(k,f'{statistics.median(x[k] for x in controls):.6f}',f'{statistics.median(x[k] for x in times["regions"]):.6f}',f'{statistics.median(x[k] for x in times["regions"])/statistics.median(x[k] for x in controls)-1:+.2%}') for k in ('jacobianSeconds','solveSeconds','solveCpuSeconds','processSeconds')]))
|
||||
report.append(table(['模式,每种3轮','Jacobian中位数s','Jacobian最小–最大s','相对未插桩中位数','积分中位数s'],[(name,f'{statistics.median(x["jacobianSeconds"] for x in tt):.6f}',f'{min(x["jacobianSeconds"] for x in tt):.6f}–{max(x["jacobianSeconds"] for x in tt):.6f}',f'{statistics.median(x["jacobianSeconds"] for x in tt)/statistics.median(x["jacobianSeconds"] for x in controls)-1:+.2%}',f'{statistics.median(x["solveSeconds"] for x in tt):.6f}') for name,tt in [('未插桩',controls),('区间',times['regions']),('operation',times['ops']),('函数',times['functions'])]]))
|
||||
report.append('这些小幅下降属于运行波动/编译布局差异,不能解释为插桩加速。函数模式中位Jacobian约增加4.84%,对抽中的短函数影响更大;其exclusive累计约0.879s,高于独立operation计时约0.660s。因此函数时间只用于热点排序和数量级判断,不作为无插桩下可节省时间的精确值。全量provenance为了追踪字段有意增加复制和日志,其Jacobian为4.801s、积分8.894s,完全不用于性能估计。\n')
|
||||
report.append(f'区间回退累计时间折算约**{total:.6f}s**;operation模式独立测得约**{op_total:.6f}s**。两者来自不同插桩和样本,差值不能直接当作调度开销。\n')
|
||||
report.append('**耗时最高的回退区间**\n')
|
||||
report.append(table(['区间','schedule范围','group','比较/失败','失败率','累计ms','每次µs','占全部回退'],[(r['id'],f'[{r["start"]},{r["end"]})',','.join(map(str,r['failedGroups'])),f'{r["compares"]}/{r["failures"]}',f'{r["failureRate"]:.1%}',f'{r["seconds"]*1e3:.3f}',f'{r["meanUs"]:.3f}',f'{r["seconds"]/total:.2%}') for r in hot[:20]]))
|
||||
report.append('全部127个context区间(包含成功区间)的比较次数、失败率、时间与组映射见intervals.md;纯代数区间也保存在analysis.json,但比较次数为0。\n')
|
||||
report.append('**group分布与扰动变量**\n')
|
||||
report.append(table(['group','比较/失败','累计回退ms','失败区间','扰动状态'],[(g['group'],f'{g["compares"]}/{g["failures"]}',f'{g["seconds"]*1e3:.3f}',','.join(map(str,g['failedRegions'])),', '.join(g['states'])) for g in group_totals]))
|
||||
report.append(f'group0–9占失败次数{sum(g["failures"] for g in group_totals[:10])/139776:.2%},占回退时间{sum(g["seconds"] for g in group_totals[:10])/total:.2%};group10–25只有两个失败区间/组,但其区间较长,合计占时间{sum(g["seconds"] for g in group_totals[10:26])/total:.2%}。group26没有fallback。group10–25的对应缓存分叉由PNCH012的m/U扰动引起;同组若还含机械速度状态,不能仅凭共组就把cache差异归给该速度。详见originPerturbedStates。\n')
|
||||
report.append('**完整证据链:直接压力变化**\n')
|
||||
report.append('Jacobian 200,t≈0.372854446s,group18仅扰动amesim_pnch012_12.m。它改变p[43]和h[184];schedule position51(operation原始ID59,flow:amesim_pnl0001_16.port_1)的实际输入日志确认这两个值变化。调用链为native_pipe_flow_cached_context → native_pipe_flow_context → state_valve → isentropic → property_pt → property_new。详细trace覆盖上述被包装函数;isentropic/property_new的位置由源码补全,property_new日志直接记录新建条目。\n')
|
||||
report.append('该operation新建的下游等熵条目states[74].p,baseline=15019640.749374540、probe=15019641.148960622 Pa;T也由460.10768147887495变为460.1076863719882 K。随后R475=[52,175)、R477=[313,452)入口首先在这个p字段不一致而失败。两区间实际执行的所有显式输入和输出均与baseline逐位一致。前置管路及其cache记录存在真实扰动依赖,但这两个候选复用区间的显式输入没有继续分叉;当前全context保护把前置变化传播成了这些区间的回退。\n')
|
||||
report.append('对应R475累计/均值及R477累计/均值见上表和intervals.md;两者均每组896次,合并group18/19后各1792次。R490的对应链为group24/25 → amesim_pnch012_15.m/U → position48(id56,amesim_pnl0001_13.port_1)→ states[68].p → R490/R492;group24在同一时刻的p为15019642.069348963→15019642.468935065 Pa。\n')
|
||||
report.append('**完整证据链:缓存新增导致count变化**\n')
|
||||
report.append('同一Jacobian 200,group6包含amesim_pnl0001_9.m。position8(flow:amesim_pnor001_5.port_1)受其影响,baseline缓存slot6的键(p,T)=(15019652.421499353,290.99609647580155),probe变为(15019652.526950026,291.859408827332)。\n')
|
||||
report.append('接着position10(flow:amesim_pnor001_6.port_1)的显式输入没有变化。它仍需要旧键(15019652.421499353,290.99609647580155):baseline在slot6命中,而probe该键已不存在,于native_temperature_ph_context → property_pt → property_new中新建slot8;其后另一个等熵条目也顺延。position10结束时count为9/10,后续R288入口为12/13,首次失败谓词是count。这里“首次写出count差异的operation10”不直接依赖扰动变量,真正上游原因是operation8改变了缓存内容。trace-created.jsonl保留两个调用链与键值,trace-inputs.jsonl证实operation10没有显式输入差异。\n')
|
||||
report.append('group6一次同时扰动多个状态:该probe最早的任意context差异发生在position2,而上述特定旧键消失发生在position8,count开始持续不等发生在position10。这三个“首次”不能混同;firstContextOrigin只记录最早的任意context分叉,不自动证明它就是每一个后续字段的原因。特定键的因果链由上述新建条目日志另行确认。\n')
|
||||
report.append(f'全量归因中,字段持续分叉来源直接含本group状态依赖的记录为{causal["directFieldOrigin"]:,},来源operation不含该group显式状态依赖的记录为{causal["cacheSideEffectOrigin"]:,};后者属于context/cache隐式影响,具体旧键→命中/新增→count链路以上述详细trace为例。所有139,776条记录所在probe的最早context分叉均出自图上受扰动影响的operation。这里只对五个时刻记录全部新建键调用链,未声称对每一条间接记录都完成了逐键因果回放。\n')
|
||||
report.append('**fallback到底重算了什么**\n')
|
||||
report.append(table(['position / 原始ID','operation','全量次数','累计ms','每次µs'],[(f'{o["position"]}/{o["operationId"]}',o['name'],o['count'],f'{o["seconds"]*1e3:.3f}',f'{o["seconds"]/o["count"]*1e6:.3f}') for o in hotops[:25]]))
|
||||
report.append(f'包含native调用的{len(native_ops)}个不同operation贡献operation计时的{native_share:.2%},执行次数占{sum(o["count"] for o in native_ops)/4620672:.2%};其余大量线性、alias、stream等操作虽然次数多,但单次较便宜。最耗时的前20个operation占{sum(o["seconds"] for o in hotops[:20])/op_total:.2%},前40个占{sum(o["seconds"] for o in hotops[:40])/op_total:.2%}。完整operation逐组、逐区间归属保存在analysis.json。\n')
|
||||
report.append('R490每次重跑135个operation,R457每次重跑147个operation;不是每个operation都需要context。最大连续“图上无关”区间只要跨过context操作,就采用一次入口整体比较,失败后连同纯代数部分一起执行。这是区间粒度带来的额外工作;是否值得进一步拆分仍需考虑比较、调度、恢复开销,本轮不据此修改。\n')
|
||||
report.append('区间长也不必然更贵:R3仅18个operation,每次约23.876µs,高于135个operation的R490(18.865µs);R3集中执行管路流量。相反,很多区间的operation只是别名传播或代数赋值。因此需要同时看回退次数、操作构成和单次成本,不能只按区间长度判断。主要热点的逐区间字段、来源operation、回退operation及函数时间在hotspots.md中串联展示。\n')
|
||||
report.append('**函数层调用次数与时间**\n')
|
||||
report.append('exclusive已减去本表中被插桩的子调用;仍包含未插桩子函数、计时和包装开销。inclusive存在嵌套,禁止求和当作总时间。函数与operation/区间来自独立运行,不能叠加。\n')
|
||||
report.append(table(['函数','全量fallback调用次数','inclusive ms','exclusive ms','exclusive µs/次'],[(k['name'],k['count'],f'{k["inclusiveSeconds"]*1e3:.3f}',f'{k["exclusiveSeconds"]*1e3:.3f}',f'{k["exclusiveSeconds"]/k["count"]*1e6:.3f}' if k['count'] else '—') for k in sorted(kernels.values(),key=lambda v:-v['exclusiveSeconds'])]))
|
||||
report.append('state_valve的单次成本明显高于property_pt等查找函数;后者主要靠次数累积。native_temperature_ph、native_density和native_pipe_resistance在fallback内的实际调用均为0:已有Jacobian memo覆盖了这些求解,不能把本次fallback热点归因于重复PH反算、密度求解或管阻求根。仍发生的主要计算包括阀流量/等熵计算、黏度计算、物性上下文查找和缓存键查询。\n')
|
||||
report.append('**正确性判断与边界**\n')
|
||||
report.append('1. 对本模型和完整本次轨迹,未发现fallback区间存在未被dependency graph捕获的数值输出依赖;4,620,672次回退operation的显式输入及输出全部逐位一致。不能据此证明所有模型和输入都可忽略context。\n')
|
||||
report.append('2. 已证实完整context比较覆盖了来自区间外的压力/缓存分配变化;已证实大区间失败会带动纯代数operation重算。这属于比较/区间粒度较粗以及cache副作用的保守传播。没有证据把它归为物性算法错误,或把fallback本身归为状态dependency graph过度保守。\n')
|
||||
report.append('3. 原lp_reuse成功后恢复整个出口property states和全部pipe cache;本轮139,776次回退出口context均不等于baseline。直接放宽比较并调用原restore会覆盖这些差异,不能由“区间输出相同”推出“完整上下文恢复安全”。保持现有context逐位语义时,当前保护的回退符合实现约定;它不等价于这些方程数学上必须全部重算。\n')
|
||||
report.append('4. 首次失败谓词只有count和states[i].p;由于原代码短路比较,这不代表其他property字段或pipe cache没有差异。本报告没有把“未成为首次失败原因”当作“始终相同”。\n')
|
||||
report.append('**数值与原实现保护**\n')
|
||||
report.append(f'本轮保存{len(checked)}份成功运行记录,均验证完整states/outputs/events二进制、warning、最终状态、步数和求解器计数,以及原context保护命中/回退计数不变。census、函数插桩验证轮与全量provenance轮另比较896个132×132矩阵及其(t,y),共15,611,904元素逐位一致。accepted/rejected=10840/918,Newton iterations=19371,nfev/njev/nlu=44467/896/3106。原实验和生产实现未修改。\n')
|
||||
report.append('工具:tests/manual/diagnose_context_fallback.py、context_fallback_diag.h/.c、analyze_context_fallback.py。plan.json含每个operation原代码及inputs/outputs/stateIndices;analysis.json含完整区间/group/operation/函数计数与时间;provenance/failures.jsonl含每次失败字段、十六进制原值和来源operation;provenance/trace-created.jsonl及trace-inputs.jsonl给出五个时刻的新建条目调用链和变化输入。\n')
|
||||
report.append('**代码证据与复现**\n')
|
||||
report.append('[lp_reuse入口比较和整体restore](F:/Master/SystemSimulationApp/tests/manual/local_probe_support.c:69);[property_new/count与property_pt精确键查询](F:/Master/SystemSimulationApp/native/components/modules/properties.c:40);[PH context与Jacobian memo](F:/Master/SystemSimulationApp/native/components/modules/properties.c:93);[等熵及state_valve](F:/Master/SystemSimulationApp/native/components/modules/properties.c:244);[pipe flow/cache链路](F:/Master/SystemSimulationApp/native/components/modules/pipe.c:114)。\n')
|
||||
report.append(f'已核验当前生产源文件与前轮编译元数据中的SHA-256一致,输入工程SHA-256一致;当前git diff HEAD为空。前轮编译提交为{production["commit"]}。新增文件仅为本轮独立诊断工具和产物。\n')
|
||||
report.append('从仓库根目录使用.venv-win/Scripts/python.exe -B运行tests/manual/diagnose_context_fallback.py。prepare分别不带参数、带--kernels、带--kernels --trace,生成三个独立worker;run --label census --mode 1 --matrices进行全量计数;run --label provenance --mode 5 --kernels --trace --matrices进行全量字段追踪。计时分别为mode 2(区间)、mode 3(operation)、mode 4 --kernels(函数),均--stride 8;三轮seed分别为17–19、43–45、67–69。control使用--control --mode 0;三类计时独立串行运行,避免与编译重叠。最后运行tests/manual/analyze_context_fallback.py重建报告。\n')
|
||||
(OUT/'report.md').write_text('\n'.join(report),encoding='utf-8')
|
||||
detail=['**全部context区间统计**\n','编号为0基,schedule范围为[start,end)。时间来自三轮1/8抽样的原始区间计时。\n',table(['ID','范围','比较','失败','失败率','累计ms','µs/失败','失败group','首次字段计数'],[(r['id'],f'[{r["start"]},{r["end"]})',r['compares'],r['failures'],f'{r["failureRate"]:.2%}' if r['failureRate'] is not None else '—',f'{r["seconds"]*1e3:.4f}',f'{r["meanUs"]:.3f}',','.join(map(str,r['failedGroups'])),dict(r['fields'])) for r in regions.values() if r['contextual']])]
|
||||
(OUT/'intervals.md').write_text('\n'.join(detail),encoding='utf-8')
|
||||
# Readable joins for hot regions; complete machine-readable joins are in analysis.json.
|
||||
selected=list(dict.fromkeys([r['id'] for r in hot[:10]]+[477,492,288]))
|
||||
details=['**热点区间的完整对应关系**\n','以下计数全量,时间来自独立三轮抽样折算;字段值为Jacobian 200的样例,槽号和首次字段随时刻可能变化。各区间包含的所有operation连续位于[start,end),完整原代码在plan.json。字段来源与函数时间来自不同诊断模式,不能相加。\n']
|
||||
for rid in selected:
|
||||
r=regions[rid]
|
||||
details.append(f'**R{rid},[{r["start"]},{r["end"]}),group {r["failedGroups"]}**\n')
|
||||
details.append(f'比较{r["compares"]:,}次,失败{r["failures"]:,}次({r["failureRate"]:.1%});累计{r["seconds"]*1e3:.3f}ms,每次{r["meanUs"]:.3f}µs。全量首个失败字段分布:{dict(r["fields"])}。每次重算{r["end"]-r["start"]}个operation,其中{len(r["nativeOperationPositions"])}个包含native调用。\n')
|
||||
ss=[samples[g,rid] for g in r['failedGroups']]
|
||||
details.append(table(['group','本例首字段','baseline → probe','字段持续分叉position/ID','来源operation','与group交集的依赖状态'],[(s['group'],s['fieldName'],f'{s["baselineValue"]!r} → {s["trialValue"]!r}',f'{s["persistentOrigin"]}/{s["originOperationId"]}',s['originName'],', '.join(s['originPerturbedStates']) or '无;需追context/cache副作用') for s in ss]))
|
||||
oo={}
|
||||
for (g,pos),o in ops.items():
|
||||
if o['region']!=rid:continue
|
||||
a=oo.setdefault(pos,dict(count=0,seconds=0));a['count']+=o['count'];a['seconds']+=o['seconds']
|
||||
ordered=sorted(oo.items(),key=lambda x:-x[1]['seconds'])
|
||||
details.append(table(['前10个耗时operation的position/ID','operation','次数','累计ms','平均µs'],[(f'{pos}/{plan["operations"][pos]["id"]}',plan['operations'][pos]['key'],o['count'],f'{o["seconds"]*1e3:.4f}',f'{o["seconds"]/o["count"]*1e6:.4f}') for pos,o in ordered[:10]]))
|
||||
kk=sorted([(k,v) for (rr,k),v in region_kernels.items() if rr==rid],key=lambda x:-x[1]['exclusiveSeconds'])
|
||||
details.append(table(['该区间内函数','次数','inclusive ms','exclusive ms','exclusive µs/次'],[(kernels[k]['name'],v['count'],f'{v["inclusiveSeconds"]*1e3:.4f}',f'{v["exclusiveSeconds"]*1e3:.4f}',f'{v["exclusiveSeconds"]/v["count"]*1e6:.4f}' if v['count'] else '—') for k,v in kk]))
|
||||
(OUT/'hotspots.md').write_text('\n'.join(details),encoding='utf-8')
|
||||
print(json.dumps(dict(total=total,operationTotal=op_total,fields=field_totals,causes=causal,topRegions=[{k:r[k] for k in ['id','start','end','seconds','meanUs','failures']} for r in hot[:10]],nativeShare=native_share,top20Share=sum(o['seconds'] for o in hotops[:20])/op_total),ensure_ascii=False,indent=2))
|
||||
|
||||
if __name__=='__main__':main()
|
||||
Reference in new issue
Block a user