修复循环信号与事件采样并接入 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:
ljz committed 2026-09-17 23:50:13 +08:00
1 parent 1aac220084
commit 7611f13208
85 files changed
+11358 -33

No files matched your search

+256
View File
@@ -0,0 +1,256 @@
"""Verify each actual read and ordered write, then summarize the two cases."""
from collections import Counter
from pathlib import Path
import ctypes as c
import hashlib
import json
import struct
ROOT = Path(__file__).resolve().parents[2]
OUT = ROOT/'test/context-access-20260917'
class Medium(c.Structure):
_fields_ = [('real_helium', c.c_int)] + [(k, c.c_double) for k in ['R','cp','Tref','slope','mu','muT','S']]
class State(c.Structure):
_fields_ = [('medium', Medium)] + [(k, c.c_double) for k in ['p','T','h','rho','mu','isentropic_factor','isentropic_exponent']] + [('valid', c.c_uint),('temperatures',c.c_void_p),('jacobian',c.c_void_p)]
class Pipe(c.Structure):
_fields_ = [('medium',Medium)] + [(k,c.c_double) for k in ['p1','p2','T','diameter','length','roughness','flow']] + [('kind',c.c_int),('valid',c.c_int)]
class Context(c.Structure):
_fields_ = [('states',c.c_void_p),('count',c.c_size_t),('capacity',c.c_size_t),('temperatures',c.c_void_p),('jacobian',c.c_void_p)]
def doubles(value):
data = bytes.fromhex(value)
return list(struct.unpack('<'+'d'*(len(data)//8),data))
def value(data, typ):
if typ == c.c_double:
return struct.unpack('<d', data)[0]
return int.from_bytes(data, 'little')
def named(domain, offset, size):
typ = {'states':State, 'pipes':Pipe, 'context':Context}[domain]
slot, off = divmod(offset,c.sizeof(typ))
if size == c.sizeof(typ):
return slot, '*', None
for name, ft in typ._fields_:
begin = getattr(typ,name).offset
if begin == off and c.sizeof(ft) == size:
return slot,name,ft
if ft == Medium and begin <= off < begin+c.sizeof(ft):
for mn,mt in Medium._fields_:
if getattr(Medium,mn).offset+begin == off:
return slot,'medium.'+mn,mt
raise AssertionError((domain,offset,size))
def first_match(memory, query):
ctx = Context.from_buffer_copy(memory['context'])
key = doubles(query['key']); p, second = key[:2]
matches = []
for i in range(ctx.count):
st = State.from_buffer_copy(memory['states'],i*c.sizeof(State))
flag, actual = (1, st.T) if query['kind']=='PT' else (2, st.h)
if st.valid & flag and st.p == p and actual == second and st.medium.real_helium == query['mediumKind'] and all(getattr(st.medium,k)==v for (k,_),v in zip(Medium._fields_[1:],key[2:])):
matches.append(i)
return matches
def analyze_operation(events):
begin=events[0]; entry=events[1]
assert begin['stateSize']==c.sizeof(State) and begin['pipeSize']==c.sizeof(Pipe)
memory={k:bytearray.fromhex(entry[k]) for k in ['context','states','pipes']}
known={k:bytearray(b'\1'*len(v)) for k,v in memory.items()}
ctx=Context.from_buffer_copy(memory['context'])
memory['states'].extend(bytes((ctx.capacity-ctx.count)*c.sizeof(State)))
known['states'].extend(bytes((ctx.capacity-ctx.count)*c.sizeof(State)))
summary={k:begin[k] for k in ['jac','group','position','t','inputs']}
summary.update(entryCount=ctx.count,capacity=ctx.capacity,queries=[],allocations=[],writes=[],consumed=[],validTests=[],sameValueWrites=0,accessReads=0,accessWrites=0,scalar=[])
assert not ctx.temperatures, 'Non-NULL observer is outside this verified contract.'
summary['bindings']={'jacobian':ctx.jacobian or 0,'temperatures':ctx.temperatures or 0}
st74=State.from_buffer_copy(memory['states'],74*c.sizeof(State)) if ctx.count>74 else None
if st74: summary['state74']={'p':st74.p,'T':st74.T,'valid':st74.valid}
pending=None
for event in events[2:]:
kind=event['event']
if kind=='query':
assert pending is None
pending=dict(event, allMatches=first_match(memory,event), tested=[])
elif kind=='match':
assert pending and pending['kind']==event['kind']
matches=pending['allMatches']; expected=matches[0] if matches else -1
assert event['slot']==expected and event['hit']==bool(matches)
expected_scans=list(range(expected+1 if expected>=0 else pending['count']))
assert pending['tested']==expected_scans,(begin,pending)
summary['queries'].append(dict(kind=event['kind'],key=pending['key'],keyValues=doubles(pending['key']),mediumKind=pending['mediumKind'],hit=event['hit'],slot=event['slot'],allMatches=matches,scanCount=len(expected_scans)))
pending=None
elif kind in ['read','write']:
domain=event['domain']; offset=event['offset']; data=bytes.fromhex(event['value'])
previous=memory[domain][offset:offset+len(data)]
slot,field,typ=named(domain,offset,len(data))
if kind=='read':
assert previous==data,(begin,event,'read mismatch')
summary['accessReads']+=1
# Lookup reads are recorded in raw events; this list isolates
# fields consumed after selection, including observers/keys.
if domain!='context' and pending is None and event['function']!='same_medium':
summary['consumed'].append(dict(domain=domain,slot=slot,field=field,value=event['value'],function=event['function']))
else:
summary['accessWrites']+=1
equal=all(known[domain][offset:offset+len(data)]) and previous==data
summary['sameValueWrites']+=equal
memory[domain][offset:offset+len(data)]=data
known[domain][offset:offset+len(data)]=b'\1'*len(data)
summary['writes'].append(dict(domain=domain,slot=slot,field=field,value=event['value'],sameValue=equal,function=event['function']))
elif kind=='valid_test':
st=State.from_buffer_copy(memory['states'],event['slot']*c.sizeof(State))
assert st.valid & event['mask']==event['value']
if pending is not None:
pending['tested'].append(event['slot'])
else:
summary['validTests'].append(event)
elif kind=='allocate':
assert event['branch']=='append', 'Scratch requires additional object registration; fail closed.'
assert event['slot']==event['countAfter']-1
assert event['countAfter']==Context.from_buffer_copy(memory['context']).count
assert event['countAfter']<=event['capacity']
summary['allocations'].append(event)
elif kind=='snapshot':
assert event['phase']=='exit'
for domain in memory:
expected=bytes.fromhex(event[domain])
assert memory[domain][:len(expected)]==expected,(begin,domain,'write replay mismatch')
summary['exitCount']=event['count']
# Everything not written remains the live probe entry, checked
# across every byte of every existing slot and every pipe slot.
summary['replayExact']=True
elif kind=='end':
summary['outputs']=event['outputs']
summary['outputValues']=doubles(event['outputs'])
elif kind.startswith('scalar_'):
summary['scalar'].append(event)
else:
raise AssertionError(event)
assert pending is None and summary['replayExact']
# Snapshot replay alone cannot detect an omitted equal-valued write.
# Check the reviewed source's mandatory store sequence independently.
pipe_writes=[w for w in summary['writes'] if w['domain']=='pipes']
assert [w['field'] for w in pipe_writes]==['valid','medium','p1','p2','T','diameter','length','roughness','kind','flow','valid']
assert int.from_bytes(bytes.fromhex(pipe_writes[0]['value']),'little')==0
assert int.from_bytes(bytes.fromhex(pipe_writes[-1]['value']),'little')==1
assert pipe_writes[-2]['value']==summary['outputs']
assert all(w['slot']==(28 if begin['position']==52 else 0) for w in pipe_writes)
for allocation in summary['allocations']:
writes=[w for w in summary['writes'] if w['domain']=='states' and w['slot']==allocation['slot']]
assert [w['field'] for w in writes[:7]]==['*','medium','p','T','valid','temperatures','jacobian']
assert not [w for w in summary['writes'] if w['domain']=='states' and w['slot']<summary['entryCount']]
return summary
def compare_logical(b,p):
"""Pair hits by query order and appends by creation order, never slot ID."""
mapping={}
assert b['bindings']==p['bindings'], 'Observer/memo ownership changed within this Jacobian.'
for bq,pq in zip(b['queries'],p['queries']):
assert (bq['kind'],bq['key'],bq['mediumKind'],bq['hit'])==(pq['kind'],pq['key'],pq['mediumKind'],pq['hit'])
if bq['hit']:
assert mapping.setdefault(bq['slot'],pq['slot'])==pq['slot']
assert len(b['allocations'])==len(p['allocations'])
for ba,pa in zip(b['allocations'],p['allocations']):
assert mapping.setdefault(ba['slot'],pa['slot'])==pa['slot']
def normalized(items, translate, writes=False):
result=[]
for event in items:
domain,slot,field,v=event['domain'],event['slot'],event['field'],event['value']
if domain=='states' and translate:slot=mapping[slot]
if domain=='context' and field=='count':v='increment'
if field in ['jacobian','temperatures']:
assert int.from_bytes(bytes.fromhex(v),'little')==b['bindings'][field]
v='current_context.'+field
result.append((domain,slot,field,v))
return result if writes else set(result)
assert normalized(b['consumed'],True)==normalized(p['consumed'],False),(b['jac'],b['position'],'consumed')
assert normalized(b['writes'],True,True)==normalized(p['writes'],False,True),(b['jac'],b['position'],'ordered writes')
assert [(mapping[v['slot']],v['mask'],v['value']) for v in b['validTests']]==[(v['slot'],v['mask'],v['value']) for v in p['validTests']]
return mapping
def write_checklists(examples, summaries):
lines=['# Context 访问级清单:实测数据', '', '由 `analyze_context_access.py` 从访问事件生成。slot、Jacobian 和 position 均从 0 开始;group=-1 为 baseline。', '',
'以下以 Jacobian 200 为主案例,补充 position 52 的 PH 命中/未命中路径。完整逐次记录位于 `test/context-access-20260917/operations.json`;原始访问事件在 `audit/access.jsonl`。', '']
chosen=[s for s in summaries if (s['jac']==200 and s['group'] in [6,18]) or (s['jac'] in [1,2] and s['group']==18)]
for s in chosen:
lines += [f"## Jacobian {s['jac']},group {s['group']},position {s['position']}", '',
f"t={s['t']:.17g};count {s['entryCount']} → {s['exitCount']};capacity={s['capacity']};输出 `{s['outputValues'][0]:.17g}`;model evaluator 返回 `{s['evalReturn']}`。", '',
'### 查询与首次匹配', '', '| 顺序 | 类型 | 完整 key:p, T 或 h, R, cp, Tref, slope, mu, muT, S | medium kind | 首个匹配 slot | 扫描条数 |', '|---|---|---|---|---|---|']
for i,q in enumerate(s['queries']):
lines.append(f"| {i+1} | {q['kind']} | `"+', '.join(format(v,'.17g') for v in q['keyValues'])+f"` | {q['mediumKind']} | {q['slot'] if q['hit'] else 'miss'} | {q['scanCount']} |")
lines += ['', '### 实际选中后的字段读取', '', '| 域 / slot | 字段 |', '|---|---|']
fields={}
for r in s['consumed']:fields.setdefault((r['domain'],r['slot']),set()).add(r['field'])
for (domain,slot),names in sorted(fields.items()):lines.append(f"| {domain}[{slot}] | "+', '.join(f'`{n}`' for n in sorted(names))+' |')
lines += ['', '查询扫描另外按短路次序读取 `valid & PT/H`、`p`、`T/h`、medium;未匹配项的字段不等于被用于物性计算。', '', '### 选中后的 valid 测试', '', '| 顺序 | slot | mask | 结果 |', '|---|---|---|---|']
for i,v in enumerate(s['validTests']):lines.append(f"| {i+1} | {v['slot']} | {v['mask']} | {v['value']} |")
lines += ['', '### 必须保留的 probe 数据', '', f"保留入口 `states[0:{s['entryCount']}]` 的全部字段;此路径没有写已有物性条目。仅追加以下新条目,修改本 operation 的 pipe 槽;其余 pipe 槽保持 probe 值。", '', '### 有序写入动作', '', '| 顺序 | 目标 | 写入值 | 已知同值写入 |', '|---|---|---|---|']
for i,w in enumerate(s['writes']):
field=w['field'];data=bytes.fromhex(w['value'])
if field=='*':text='完整 struct 清零(显式写入)'
elif field=='medium':text='复制上述完整 medium'
elif field in ['temperatures','jacobian']:text='NULL' if not int.from_bytes(data,'little') else '当前 context 的 Jacobian memo 指针'
elif field in ['count','valid','kind']:text=str(int.from_bytes(data,'little'))
else:text=format(struct.unpack('<d',data)[0],'.17g')
lines.append(f"| {i+1} | `{w['domain']}[{w['slot']}].{field}` | {text} | {'是' if w['sameValue'] else '—'} |")
lines += ['', '新条目分配分支均为 positive/finite key 且 `count < capacity`;每次在当前 count 追加,再 count++。未初始化槽的 struct 首次清零不计入“已知同值写入”。', '',
'### Fallback 原因', '', '查询 key/首匹配逻辑条目不同、已消费字段或已测试 valid 位不同、容量不足转 scratch、observer 非空、pipe 命中分支改变,或无法建立无冲突的 slot 映射时,执行原 operation。此清单不授权放宽现有 guard。', '']
(ROOT/'tests/manual/context_access_checklists.md').write_text('\n'.join(lines),encoding='utf-8')
def main():
summaries=[]; events=[]; return_pending=[]; returns=Counter(); examples=[]
with (OUT/'audit/access.jsonl').open(encoding='utf-8') as stream:
for line in stream:
event=json.loads(line)
if event['event']=='eval_return':
assert return_pending
for item in return_pending: item['evalReturn']=event['result']
return_pending=[]; returns[event['result']]+=1
elif event['event']=='begin':
assert not events
events=[event]
else:
assert events
events.append(event)
if event['event']=='end':
item=analyze_operation(events);summaries.append(item);return_pending.append(item)
if item['jac']==200: examples.append(dict(summary=item,events=events))
events=[]
assert not events and not return_pending
by_key={(x['jac'],x['group'],x['position']):x for x in summaries}
cases={}
for group,pos in [(18,52),(6,16)]:
pairs=[]
for jac in range(896):
b=by_key[jac,-1,pos];p=by_key[jac,group,pos]
assert b['inputs']==p['inputs'] and b['outputs']==p['outputs'] and b['evalReturn']==p['evalReturn']==1
equal_query=[(q['kind'],q['key'],q['mediumKind'],q['hit']) for q in b['queries']]==[(q['kind'],q['key'],q['mediumKind'],q['hit']) for q in p['queries']]
mapping=compare_logical(b,p)
pairs.append(dict(jac=jac,queryKeysAndHitsEqual=equal_query,logicalReadsAndOrderedWritesEqual=True,slotMapping=mapping,entryCounts=[b['entryCount'],p['entryCount']],exitCounts=[b['exitCount'],p['exitCount']],slots=[[q['slot'] for q in b['queries']],[q['slot'] for q in p['queries']]],allocations=[[a['slot'] for a in b['allocations']],[a['slot'] for a in p['allocations']]],state74=[b.get('state74'),p.get('state74')]))
cases[f'group{group}_position{pos}']=dict(pairs=len(pairs),equalQueryKeysAndHits=sum(p['queryKeysAndHitsEqual'] for p in pairs),countShiftPairs=sum(p['entryCounts'][0]!=p['entryCounts'][1] for p in pairs),rows=pairs)
result=dict(operations=len(summaries),replayExact=len(summaries),reads=sum(s['accessReads'] for s in summaries),writes=sum(s['accessWrites'] for s in summaries),sameValueWrites=sum(s['sameValueWrites'] for s in summaries),evalReturns=dict(returns),cases=cases)
for name,obj in [('summary.json',result),('operations.json',summaries),('jacobian-200.json',examples)]:
(OUT/name).write_text(json.dumps(obj,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
write_checklists(examples,summaries)
print(json.dumps({k:v for k,v in result.items() if k!='cases'},indent=2))
for name,case in cases.items(): print(name,{k:v for k,v in case.items() if k!='rows'})
if __name__=='__main__':main()
+191
View File
@@ -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()
+109
View File
@@ -0,0 +1,109 @@
"""Audit the two staged shadow runs and write the result document."""
from collections import Counter
from pathlib import Path
import hashlib,json
ROOT=Path(__file__).resolve().parents[2]
OUT=ROOT/'test/context-shadow-20260917'
def read(path):return json.loads(path.read_text(encoding='utf-8'))
def audit(label):
folder=OUT/label;s=read(folder/'shadow-summary.json')
rows=[json.loads(line) for line in (folder/'shadow-trials.jsonl').read_text(encoding='utf-8').splitlines()]
negative=[json.loads(line) for line in (folder/'shadow-negative.jsonl').read_text(encoding='utf-8').splitlines()]
assert len(rows)==s['total']==s['accepted']==896
assert {r['jac'] for r in rows}==set(range(896))
assert all(r['reason']=='accepted' and r['referenceCount']==r['candidateCount'] for r in rows)
assert all(r['candidateCount']==r['probeCount']+r['appends'] for r in rows)
assert sum(r['baselineCount']!=r['probeCount'] for r in rows)==s['countDifferent']
assert sum(any(a!=b for a,b in r['mapping']) for r in rows)==s['slotRelocationTrials']
assert sum(sum(a!=b for a,b in r['mapping']) for r in rows)==s['relocatedSlots']
assert sum(r['appends'] for r in rows)==s['appends']
assert [sum(r['path']==i for r in rows) for i in range(4)]==s['paths']
assert s['mismatches']==s['liveContamination']==s['forbiddenNativeCalls']==s['rejected']==0
for key in ['tailEqual','liveTailEqual','memoImmutable','untouchedProbeEqual','metadataImmutable']:assert s[key]==896
assert len(negative)==s['negativePassed'] and all(n['reason']==n['expected'] and n['unchanged'] for n in negative)
assert not (folder/'first-mismatch.json').exists()
s['negativeReasonCounts']=dict(Counter(n['reason'] for n in negative))
s['example200']=next(r for r in rows if r['jac']==200)
s['measurement']=read(folder/'measurement.json')
return s
def main():
a=audit('r288');b=audit('position52')
worker_sha=hashlib.sha256((OUT/'worker/model.exe').read_bytes()).hexdigest()
assert a['validatedWorkerSha256']==b['validatedWorkerSha256']==worker_sha
assert a['measurement']['hashes']==b['measurement']['hashes']
build=read(OUT/'worker/build.json');assert build['wholeContextGuardUnchanged']
result=dict(r288=a,position52=b,workerSha256=worker_sha,productionPathChanged=False)
(OUT/'validation.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
lines=['# 最小 context shadow replay 验证结果', '',
'## 1. Candidate 能否逐位复现 Reference', '',
'**可以,在本轮两个 operation 的已覆盖路径和严格前置条件下,1,792 次 shadow replay 全部通过,出口不一致为 0。** R288 先完成全部 896 次;同一 worker 二进制通过该阶段后,才运行 position 52 的 896 次。', '',
'参考答案始终是**同一个当前 probe 入口的独立深拷贝,真实执行原 operation 后的出口**。没有把 baseline 出口当参考。baseline 只提供本 Jacobian 内的局部语义记录,不提供用于恢复的完整 context。', '',
'| 统计 | group 6 / R288 / position 16 | group 18 / R475 / position 52 |', '|---|---:|---:|']
for title,key in [('shadow 总次数','total'),('可重放','accepted'),('自然轨迹 reject','rejected'),('Reference/Candidate 不一致','mismatches'),('入口 count 不同','countDifferent'),('发生 slot relocation 的 probe','slotRelocationTrials'),('重定位逻辑 slot 数量','relocatedSlots'),('追加条目总数','appends'),('PT hit','ptHit'),('PT miss','ptMiss'),('PH hit','phHit'),('PH miss','phMiss'),('独立后续 evaluator 返回值/dy/w/context 一致','tailEqual'),('后续 evaluator 与主仿真返回值/dy/w 一致','liveTailEqual'),('memo 条目只读验证','memoImmutable'),('未写入的 probe 数据保持不变','untouchedProbeEqual'),('局部 metadata 未被 Reference/Candidate 改写','metadataImmutable'),('Candidate 物理 native 调用','forbiddenNativeCalls'),('主 context/浮点环境污染','liveContamination')]:
lines.append(f'| {title} | {a[key]} | {b[key]} |')
lines += ['', '### 如何保证比较有意义', '',
'1. 在目标 probe 的 operation 前,深拷贝 property states、全部 pipe 槽、scalar memo 的全部 entries 和计数,分别绑定到 Reference/Candidate 私有存储。两路没有共享可写 context。',
'2. Reference 调用该位置的原始表达式。Candidate 只解释 baseline 捕获的 query/read/valid/allocate/write/OR/scalar-get 语义;物理 native 入口有运行时禁入检查。Reference 执行期间不能补写 Candidate 的记录。',
'3. Candidate 从自己的 probe 副本建立 overlay。重新扫描当前有序 entries,采用原 PT/PH 的 exact `==` 和 medium 比较语义找 first-match;用逻辑 ID 映射结果,追加使用当前 count。',
'4. 所有条件成功后进入无失败分支的 commit,仅按记录顺序更新实际写入字段、valid OR、count、指定 pipe 槽、memo 命中计数及显式输出;不复制 baseline context,也不覆盖 probe 未写字段。reject 前没有向 Candidate 或输出提交任何存储。',
'5. 逐位比较输出、所有 active property 字段、valid bits、全部 pipe 字段、memo entries/计数、warning observer、errno 和 x87/SSE 环境。私有指针按“绑定到各自当前 owner”检查;不要求两个独立 allocation 的地址相等。数值字段没有容差或近似比较。',
'6. 两个独立出口继续执行同一份原 evaluator 后续代码,实测返回状态、完整 dy/w、context 和 memo 计数一致;再与主仿真的真实 evaluator 返回值/dy/w 核对。主仿真仍然执行原 operation,没有采用 Candidate 输出,没有进入真实 skip 路径。',
'7. 两轮主仿真的状态、输出、事件及全部 896 个 132×132 Jacobian 仍与未插桩基线逐字节一致。whole-context guard 源码哈希保持一致。', '',
'operation 本身返回一个 double,没有单独的 int 成功码;报告中的 evaluator 状态来自两条后续执行路径,不是用 `isfinite(output)` 代替。', '',
'### 两个具体入口例子(Jacobian 200)', '', '| 项目 | R288 | position 52 |', '|---|---|---|']
for title,key in [('baseline count','baselineCount'),('当前 probe count','probeCount'),('Reference 出口 count','referenceCount'),('Candidate 出口 count','candidateCount'),('逻辑 slot → 当前 slot','mapping')]:lines.append(f'| {title} | `{a["example200"][key]}` | `{b["example200"][key]}` |')
lines += ['', 'R288 保留 probe 原 slot 12,在 13、14 追加;position 52 保留 probe slot 74 的压力/温度差异,查询重新扫描后仍 miss,再在 75、76 追加。', '',
'## 2. 哪些路径已经可以 replay', '', '| 查询路径 | R288 次数 | position 52 次数 |', '|---|---:|---:|']
for i,name in enumerate(['近零流量,无物性查询','PT miss → PT miss','PH miss → PT miss → PT hit → PT miss','PH hit → PT hit → PT miss']):lines.append(f'| {name} | {a["paths"][i]} | {b["paths"][i]} |')
lines += ['',
'以上“可 replay”指本轮相应 operation 的完整 0–10 s 轨迹,且所有 runtime guard 同时成立。PH miss 的 h 登记与 valid OR、后续对刚追加逻辑条目的 PT hit 都保留。近零流量不追加物性条目,但仍执行 pipe 的有序写入,包括 valid=0 的同值写入。', '',
'R288 的 638 次 count 差异中,67 次属于无查询/无追加路径,因此只有 571 次发生实际 slot relocation。position 52 同理,24 次 count 差异中有一次无追加。不能把 count 差异次数当作重定位次数。', '',
'## 3. 哪些情况仍必须 reject / fallback', '',
'自然轨迹中各 reject 分类均为 0。为防止“全成功但拒绝机制无效”,另在私有副本中执行下列负例,要求 reject 且整个 Candidate 与输出 sentinel 逐字节不变;这些不计入自然轨迹的 896 次。', '',
'| reject 分类 | R288 负例通过次数 | position 52 负例通过次数 |', '|---|---:|---:|']
for reason in sorted(set(a['negativeReasonCounts'])|set(b['negativeReasonCounts'])):lines.append(f'| `{reason}` | {a["negativeReasonCounts"].get(reason,0)} | {b["negativeReasonCounts"].get(reason,0)} |')
lines += ['',
'严格拒绝边界:', '',
'- 记录不属于当前 Jacobian/operation、源代码不再匹配已验证 worker、记录溢出或未知语义事件。',
'- 输入改变、first-match 逻辑映射冲突、hit/miss 路径改变、已消费字段或 valid 掩码结果不一致。',
'- 容量不足或 scratch、非空 observer、未覆盖的已有 entry 原地更新、pipe 命中分支变化、非有限结果或未覆盖路径。',
'- memo owner/lifetime/recording 不符合只读约束、所需 scalar key 未命中或值不匹配。Candidate 不调用物理 fallback 来弥补 memo miss。',
'- 舍入模式、SSE 控制模式或所需异常状态不满足已验证条件;已记录的非零 errno 前置条件不成立。', '',
'负例包括第一次追加完成后第二次容量检查失败、末尾未知事件、末尾非有限输出,因此覆盖了 overlay 已发生大量修改后的回滚,不只是入口早退。position 52 的 PH-hit 样本还直接改变 probe 已有条目的 rho 和 MU 位,确认消费值/valid guard 生效。', '',
'Jacobian memo 的 entries 始终只读,但 Reference 的 scalar get 会增加 reuses 计数。Candidate 在 overlay 中验证同一 bit-key 查找结果,并在 commit 中重放对应计数增量。没有将 baseline 的 recording/put 副作用照搬到 probe。', '',
'验证期间发现并修复了 Windows 诊断隔离问题:该工具链的 `fesetenv` 不完整恢复 SSE 控制寄存器。负例现在保存/恢复完整 x87/SSE 环境,并验证 SSE 单独改变时会拒绝。修复前失败证据保存在 `test/context-shadow-20260917/env-restore-investigation/`;本报告仅使用修复后的同一构建全量重跑结果。', '',
'## 4. Runtime metadata 需要多少', '', '| 当前实现 | R288 | position 52 |', '|---|---:|---:|',
f'| 单条语义事件 | {a["eventBytes"]} B | {b["eventBytes"]} B |',
f'| 最大事件数 | {a["eventMax"]} | {b["eventMax"]} |',
f'| 每条 operation 记录实际使用范围 | {a["metadataMin"]}–{a["metadataMax"]} B | {b["metadataMin"]}–{b["metadataMax"]} B |',
f'| 每阶段为 512 个事件预留 | {a["metadataReserved"]} B | {b["metadataReserved"]} B |', '',
'记录包括:Jacobian/operation 身份、四个显式输入、出口值、query 完整 key/逻辑命中关系、实际消费的字节值与 valid 掩码、创建顺序、字段更新/OR、scalar key/value、memo 绑定及环境前置条件。**不保存 baseline 完整 property/pipe context 用于 replay。** 只保留当前 Jacobian 的一条目标 operation 记录。', '',
f'事务 scratch 另需一个 {a["contextCopyBytes"]} B 的 overlay;本诊断为入口、Reference、Candidate、回滚检查及双路后续比较共保留 7 个 context 副本、2 个 {a["frameCopyBytes"]} B frame。它们是 shadow 验证工作内存,不能算作未来 skip 每条记录都必须长期保存的 metadata。slot 映射临时表为 256 个 int(当前 ABI 1,024 B)。', '',
'这是当前保守事件表示的实测大小,未做去重或压缩,也没有据此评价性能。', '',
'## 5. 是否具备最小真实 skip 实验的条件', '',
'**已具备针对这两个单独 operation、上述四种已验证路径的下一阶段实验条件。** 下一步必须继续保持严格 guard、事务 commit、reject 后原执行以及独立 Reference 抽查/全量对比;先对 R288 单点实验,再单独考虑 position 52。', '',
'本轮没有实现真实 skip,也没有移除或放宽 whole-context guard。结论不适用于整个 R475、全部 reuse interval、新模型、其他 native kernel、容量耗尽/observer 非空/已有 entry 原地更新等未验证分支;Linux 尚未运行本实验。双路执行和详细检查的时长不作性能证据。', '',
'## 复现与证据', '',
'依赖上一轮保留的 access worker 和未插桩 baseline 文件。准备阶段核对 access worker 源码哈希;第二阶段核对第一阶段通过的 worker 二进制 SHA-256。', '',
'```powershell',
r'.venv-win\Scripts\python.exe tests/manual/diagnose_context_shadow.py prepare',
r'.venv-win\Scripts\python.exe tests/manual/diagnose_context_shadow.py run --position 16',
r'.venv-win\Scripts\python.exe tests/manual/diagnose_context_shadow.py run --position 52',
r'.venv-win\Scripts\python.exe tests/manual/analyze_context_shadow.py',
'```', '',
'生成文件位于 `test/context-shadow-20260917/`:`worker/build.json`、各阶段 `shadow-trials.jsonl`、`shadow-summary.json`、`shadow-negative.jsonl`、`measurement.json`,以及汇总的 `validation.json`。若出现不一致,会输出首个不同字段和完整入口/两路 context、frame、metadata 到 `first-mismatch.json`。最终两阶段均未生成该文件。', '',
f'最终 worker SHA-256:`{worker_sha}`。', '',
'全量主轨迹哈希(两阶段相同):', '']
for name,digest in a['measurement']['hashes'].items():lines.append(f'- {name}: `{digest}`')
target=ROOT/'tests/manual/context_shadow_report.md';target.write_text('\n'.join(lines)+'\n',encoding='utf-8')
print(json.dumps(dict(total=a['total']+b['total'],accepted=a['accepted']+b['accepted'],mismatches=0,negativePassed=a['negativePassed']+b['negativePassed'],report=str(target)),ensure_ascii=False,indent=2))
if __name__=='__main__':main()
@@ -0,0 +1,244 @@
"""Read archived profiling only; write a reproducible fallback cost inventory.
No worker build, simulation, native code edit, or extrapolated speedup claim.
Run from any directory with the repository's Python interpreter.
"""
from collections import defaultdict
import hashlib
import json
from pathlib import Path
import re
import statistics
ROOT = Path(__file__).resolve().parents[2]
SOURCE = ROOT / "test/context-fallback-20260917"
OUT = ROOT / "test/fallback-profitability-20260917"
DOCS = ROOT / "tests/manual"
J = 896
MANIFEST = {}
def read(path):
raw = path.read_bytes()
MANIFEST[path.relative_to(ROOT).as_posix()] = hashlib.sha256(raw).hexdigest()
return json.loads(raw)
def close(a, b):
assert abs(a - b) <= 1e-12 * max(1, abs(a), abs(b)), (a, b)
def table(headers, rows):
def cell(x):
return str(x).replace("|", "\\|").replace("\n", " ")
return "\n| " + " | ".join(headers) + " |\n| " + " | ".join(["---"] * len(headers)) + " |\n" + "\n".join("| " + " | ".join(map(cell, r)) + " |" for r in rows) + "\n"
def labels(xs, prefix=""):
return ",".join(prefix + str(x) for x in xs)
def span_stats(xs):
return dict(rounds=xs, mean=statistics.mean(xs), median=statistics.median(xs), min=min(xs), max=max(xs))
def main():
a = read(SOURCE / "analysis.json")
plan = read(SOURCE / "plan.json")
typed = read(ROOT / "test/r288-typed-replay-20260917/performance-summary.json")
layered = read(ROOT / "test/local-probe-profile-direct-20260917/comparison.json")
P = typed["metrics"]["C"]["stages"]["median"]
H = typed["paired"]["C"]["metadataIncrementUs"]["median"]
def budgets(c, m):
# Strict inequality is required for a positive saving. Negative budgets
# are retained: they mean no nonnegative implementation cost can fit.
return dict(originalUs=c, structuralReusePerJacobian=m,
captureMaxAtZeroProbeUs=m*c,
captureMaxAtProbeUs={str(p): m*(c-p) for p in (0.1, 0.25, 0.5, 2, 5, 10, P)},
probeMaxAtTypedCaptureUs=c-H/m,
typedZeroCapturePossible=c>P,
typedWithSharedCapturePossible=c>P+H/m,
typedScenarioNetMs=J*(m*(c-P)-H)/1000)
native = {}
for pos, lines in enumerate(plan["code"]):
native[pos] = sorted(set(re.findall(r"\b(native_\w+)\s*\(", "\n".join(lines))))
for op in a["operations"]:
pos = op["position"]
assert bool(native[pos]) == (plan["versions"][pos] != plan["versions"][pos+1])
pairs = {(x["group"], x["region"]): x for x in a["groupRegions"] if x["failures"]}
gos = {(x["group"], x["position"]): x for x in a["groupOperations"]}
assert len(pairs) == 156 and len(gos) == len(a["groupOperations"]) == 5157
assert sum(x["failures"] for x in pairs.values()) == 139776
assert sum(x["count"] for x in gos.values()) == 4620672
assert all(x["failures"] == J for x in pairs.values())
assert all(x["inputDiffOps"] == x["outputDiffOps"] == x["exitOutputDiff"] == 0 and x["exitContextDiff"] == J for x in pairs.values())
region_runs, op_runs = [], []
for i in range(3):
r = read(SOURCE / f"regions-{i}/context.json")
o = read(SOURCE / f"ops-{i}/context.json")
assert r["sampled"] == o["sampled"] == 112
region_runs.append({(v["group"], v["region"]): v["regionTicks"] * J/r["sampled"]/r["frequency"] for v in r["regions"]})
op_runs.append({(g, pos): ticks * J/o["sampled"]/o["frequency"] for g, pos, n, ticks in o["operations"]})
for key, x in pairs.items():
close(x["seconds"], statistics.mean(r[key] for r in region_runs))
for key, x in gos.items():
close(x["seconds"], statistics.mean(o[key] for o in op_runs))
def composition(ops):
ns = sum(x["seconds"] for x in ops if native[x["position"]])
alg = sum(x["seconds"] for x in ops if not native[x["position"]])
return dict(nativeContainingOperationSeconds=ns, pureAlgebraAliasOperationSeconds=alg)
operations = []
for x in sorted(a["operations"], key=lambda x: -x["seconds"]):
pos = x["position"]
members = [o for (g, p), o in gos.items() if p == pos]
close(x["seconds"], sum(o["seconds"] for o in members))
assert x["count"] == len(x["groups"])*J
calls = native[pos]
kernel_family = any(c in calls for c in ("native_pipe_flow_cached_context", "native_medium_orifice_context"))
operation = dict(**x, nativeCalls=calls, code=plan["code"][pos],
nativeOperationCount=int(bool(calls)), operationCount=1,
contextFreeAlgebraAlias=not calls,
decision="A;停止R288后续优化" if pos == 16 else "A;E只测纯数值尾部" if kernel_family else "A" if calls else "A;C合并代数段",
baselineSharing="显式输入/输出同值;跨组记录共享仅为结构上限,消费字段/分支未普遍验证",
budget=budgets(x["seconds"]*1e6/x["count"], len(x["groups"])),
roundSeconds=span_stats([sum(run[g, pos] for g in x["groups"]) for run in op_runs]),
groupMeanUsRange=[min(o["seconds"]*1e6/o["count"] for o in members), max(o["seconds"]*1e6/o["count"] for o in members)],
functionTimingScope="仅有 regionKernels 的区间函数归因,无本 position 专属函数计时",
**composition(members))
operations.append(operation)
regions, group_regions = [], []
for r in sorted((r for r in a["regions"] if r["failures"]), key=lambda r: -r["seconds"]):
rid = r["id"]
members = [x for x in gos.values() if x["region"] == rid]
comp = composition(members)
close(r["operationSeconds"], sum(comp.values()))
native_positions = [p for p in range(r["start"], r["end"]) if native[p]]
assert native_positions == r["nativeOperationPositions"]
count = r["end"]-r["start"]
kernels = sorted((dict(name=plan["kernels"][k["kernel"]][1], **k) for k in a["regionKernels"] if k["region"] == rid), key=lambda k: -k["exclusiveSeconds"])
m = len(r["failedGroups"])
op_us = r["operationSeconds"]*1e6/r["failures"]
decision = "A" if count == 1 else "C/E预算筛选;native默认A"
if rid in (3, 480, 485, 490):
decision += ";D仅融合区间的待证假设"
region = dict(**r, operationCount=count, nativeOperationCount=len(native_positions),
nativeCalls=sorted(set(c for p in native_positions for c in native[p])),
kernels=kernels, decision=decision, **comp,
baselineSharing="同一 baseline 区间结构可供多个组引用;语义兼容未证" if m>1 else "仅一个失败组,无跨失败组摊销",
budget=budgets(r["meanUs"], m), operationModeBudget=budgets(op_us, m),
bothModesTypedWithSharedCapturePossible=min(r["meanUs"], op_us)>P+H/m,
roundMeanUs=span_stats([sum(run[g, rid] for g in r["failedGroups"])*1e6/r["failures"] for run in region_runs]))
regions.append(region)
for g in r["failedGroups"]:
x = pairs[g, rid]
group_regions.append(dict(**x, operationCount=count, nativeOperationCount=len(native_positions),
nativeCalls=region["nativeCalls"],
functionTimingScope=f"R{rid}汇总,无group级函数拆分", decision=decision,
**composition([v for v in members if v["group"] == g]),
budget=budgets(x["seconds"]*1e6/J, m),
# The net over J*m applies to a homogeneous-cost
# scenario, not the measured total for this group.
sharedBudgetNote="m为整个interval共享上限;本group成本的预算是假设同成本的情景,不是全interval实测净收益",
roundMeanUs=span_stats([run[g, rid]*1e6/J for run in region_runs])))
assert len(regions) == 115 and len(operations) == 343
close(sum(r["seconds"] for r in regions), a["totals"]["regionSeconds"])
close(sum(o["seconds"] for o in operations), a["totals"]["operationSeconds"])
close(sum(r["pureAlgebraAliasOperationSeconds"] for r in regions), sum(o["pureAlgebraAliasOperationSeconds"] for o in operations))
cohorts = []
for p in (2, 5, P, 10):
for capture in (0, H):
eligible = [r for r in regions if r["meanUs"] > p+capture/r["budget"]["structuralReusePerJacobian"]]
cohorts.append(dict(probeCostUs=p, captureUs=capture, regionIds=[r["id"] for r in eligible],
count=len(eligible), originalSeconds=sum(r["seconds"] for r in eligible),
originalShare=sum(r["seconds"] for r in eligible)/a["totals"]["regionSeconds"],
hypotheticalNetSeconds=sum(r["seconds"]-r["failures"]*p/1e6-J*capture/1e6 for r in eligible),
assumption="每个完整interval只付一次固定P,baseline捕获H跨所有失败组共享,100%成功;不等于逐operation replay,也不是预测"))
total_comp = composition(list(gos.values()))
pure_spans = []
for r in regions:
pos = r["start"]
while pos < r["end"]:
if native[pos]:
pos += 1
continue
start = pos
while pos < r["end"] and not native[pos]:
pos += 1
members = [gos[g, p] for g in r["failedGroups"] for p in range(start, pos)]
seconds = sum(x["seconds"] for x in members)
pure_spans.append(dict(region=r["id"], groups=r["failedGroups"], start=start, end=pos,
operationCount=pos-start, count=r["failures"], seconds=seconds,
meanUs=seconds*1e6/r["failures"]))
close(sum(s["seconds"] for s in pure_spans), total_comp["pureAlgebraAliasOperationSeconds"])
pure_spans.sort(key=lambda s: -s["seconds"])
kernel_cohort = [o for o in operations if any(c in o["nativeCalls"] for c in ("native_pipe_flow_cached_context", "native_medium_orifice_context"))]
summary = dict(**a["totals"], **total_comp, intervals=len(regions), groupIntervals=len(pairs),
operations=len(operations), groupOperations=len(gos),
maxOperationMeanUs=max(o["budget"]["originalUs"] for o in operations),
maxGroupOperationMeanUs=max(o["seconds"]*1e6/o["count"] for o in gos.values()),
pureSpanCount=len(pure_spans),
pureSpansAbove2UsSeconds=sum(s["seconds"] for s in pure_spans if s["meanUs"]>2),
kernelContainingPositions=[o["position"] for o in kernel_cohort],
kernelContainingOriginalSeconds=sum(o["seconds"] for o in kernel_cohort),
layeredFallbackSeconds=layered["main"]["totals"]["correctedSeconds"]["schedule_context_fallback"]*J/layered["main"]["samples"],
wholeContextSuccesses=a["totals"]["compares"]-a["totals"]["failures"])
group_ops = []
for x in a["groupOperations"]:
op = next(o for o in operations if o["position"] == x["position"])
group_ops.append(dict(**x, nativeCalls=op["nativeCalls"], decision=op["decision"],
meanUs=x["seconds"]*1e6/x["count"],
structuralReusePerJacobian=op["budget"]["structuralReusePerJacobian"]))
out = dict(scope="archived 0–10 s; diagnostics only", summary=summary,
referenceCosts=dict(typedProbeUs=P, typedCaptureIncrementUs=H, metrics=typed["metrics"], paired=typed["paired"]),
assumptions=dict(sharing="structural upper bound, not established semantic reuse", successRate=1,
originalCost="sampled instrumented mean; not an uninstrumented lower bound",
budgets="strictly less for profit; no incremental failed-guard/fallback-dispatch cost assumed"),
regions=regions, groupRegions=group_regions, operations=operations, groupOperations=group_ops,
pureSpans=pure_spans, groups=a["groups"], kernels=a["kernels"], scenarios=cohorts,
successfulContextRegions=[r for r in a["regions"] if r["contextual"] and not r["failures"]],
inputSha256=MANIFEST,
checks=["139776 interval failures", "4620672 fallback operation executions", "no duplicate (group,position)",
"156 failing group/interval pairs; 115 intervals; 343 positions; 5157 group/position pairs",
"raw three-run timing reconstruction matches archived analysis", "native-call and context-version classification agree",
"region, group and operation partitions conserve totals", "all pure spans conserve algebra time"])
OUT.mkdir(parents=True, exist_ok=True)
(OUT / "analysis.json").write_text(json.dumps(out, ensure_ascii=False, indent=2)+"\n", encoding="utf-8")
preamble = "# 全部 fallback 区间及预算\n\n由 `analyze_fallback_profitability.py` 从历史数据生成。ID、group、position 为0基,范围为[start,end)。ms为完整896个Jacobian折算累计;µs为每次fallback。按累计原计算时间排序。\n\nP=probe总开销,H=每个Jacobian新增baseline捕获,m=结构上最多共享的失败组数。盈利要求 P+H/m<C;Hmax=m(C-P)。所有预算是严格上限,不代表实际可达到;负数表示不可能。m不是已验证成功次数,若只能组内独立捕获则m=1。\n\n原区间时间、op时间和function时间来自不同抽样运行,不相加;纯代数是完全不含native调用的operation计时,native内部代数耗时未知。完整JSON另含全部5157条group/position映射、三轮原始折算值及函数表。\n"
doc = [preamble, "## 115个失败区间:成本和组成\n", table(
["R / 范围", "group", "次数", "累计ms", "均值µs / 三轮min–max", "ops/native", "op累计ms / 纯代数ms", "主要native", "主要function(exclusive排序)", "m / 共享", "选择"],
[(f"R{r['id']} [{r['start']},{r['end']})", labels(r['failedGroups']), r['failures'], f"{r['seconds']*1e3:.4f}",
f"{r['meanUs']:.3f} / {r['roundMeanUs']['min']:.3f}–{r['roundMeanUs']['max']:.3f}", f"{r['operationCount']}/{r['nativeOperationCount']}",
f"{r['operationSeconds']*1e3:.4f} / {r['pureAlgebraAliasOperationSeconds']*1e3:.4f}", labels(r['nativeCalls']),
labels([k['name'] for k in r['kernels'] if k['count']][:3]), f"{len(r['failedGroups'])} / 条件式", r['decision']) for r in regions]),
"\n## 每个区间的break-even边界\n\nPtyped、Htyped取R288最新批次中位数,仅作成本量级情景。区间预算假设一套融合机制处理整个区间,不能按每个native重复付P后仍使用本预算。\n", table(
["R", "Cµs", "m", "Hmax(P=0)µs", "Hmax(P=2/5/10)µs", "Pmax(Htyped)µs", "Ptyped,H=0可行", "Ptyped,Htyped可行", "op模式也支持该情景"],
[(r['id'], f"{r['meanUs']:.3f}", len(r['failedGroups']), f"{r['budget']['captureMaxAtZeroProbeUs']:.3f}",
"/".join(f"{r['budget']['captureMaxAtProbeUs'][str(p)]:.3f}" for p in (2,5,10)), f"{r['budget']['probeMaxAtTypedCaptureUs']:.3f}",
r['budget']['typedZeroCapturePossible'], r['budget']['typedWithSharedCapturePossible'], r['bothModesTypedWithSharedCapturePossible']) for r in regions]),
"\n## 全部156条group/interval成本\n", table(
["group", "R", "次数", "原计算ms", "µs/次", "ops/native", "纯代数ms", "m上限", "Pmax(Htyped/m)µs"],
[(r['group'], r['region'], r['failures'], f"{r['seconds']*1e3:.4f}", f"{r['budget']['originalUs']:.3f}", f"{r['operationCount']}/{r['nativeOperationCount']}",
f"{r['pureAlgebraAliasOperationSeconds']*1e3:.4f}", r['budget']['structuralReusePerJacobian'], f"{r['budget']['probeMaxAtTypedCaptureUs']:.3f}") for r in sorted(group_regions,key=lambda x:-x['seconds'])]),
"\n## 全部纯代数连续段(区间内切分候选)\n", table(
["R", "group", "范围", "ops", "执行次数", "累计ms", "每段µs / 允许的最大新增成本"],
[(s['region'], labels(s['groups']), f"[{s['start']},{s['end']})", s['operationCount'], s['count'], f"{s['seconds']*1e3:.4f}", f"{s['meanUs']:.3f}") for s in pure_spans])]
(DOCS / "fallback_profitability_intervals.md").write_text("\n".join(doc), encoding="utf-8")
doc = ["# 全部343个 fallback operation\n\n按跨group累计原计算时间排序。一个position计作一个operation,native=1表示原语句含native调用(不等于动态native调用总次数)。纯代数operation的纯代数耗时等于其累计时间;native语句内部的代数/函数耗时没有独立position级测量。具体(group,position)到R的关联见JSON groupOperations,不将各列group和R做笛卡尔积。\n\n所有m都是每Jacobian结构可共享上限,非语义证明。Hmax/Pmax使用与区间表相同的严格盈亏公式。逐operation的7.109µs typed量级,即使H=0也全部不盈利。A=原计算;C=批量代数段切分;E=只评估纯kernel memo,绝非跳过native的context副作用。\n", table(
["position / 原ID", "operation", "R", "group", "次数", "累计ms", "每次µs", "native", "主要native", "m", "Hmax(P=0)µs", "Hmax(P=2/5/10)µs", "Pmax(Htyped)µs", "选择"],
[(f"{o['position']}/{o['operationId']}", o['name'], labels(o['regions'],'R'), labels(o['groups']), o['count'], f"{o['seconds']*1e3:.4f}",
f"{o['budget']['originalUs']:.3f}", o['nativeOperationCount'], labels(o['nativeCalls']) or '纯代数/alias', o['budget']['structuralReusePerJacobian'],
f"{o['budget']['captureMaxAtZeroProbeUs']:.3f}", "/".join(f"{o['budget']['captureMaxAtProbeUs'][str(p)]:.3f}" for p in (2,5,10)),
f"{o['budget']['probeMaxAtTypedCaptureUs']:.3f}", o['decision']) for o in operations])]
(DOCS / "fallback_profitability_operations.md").write_text("\n".join(doc), encoding="utf-8")
print(json.dumps(dict(summary=summary, scenarios=cohorts, topPureSpans=pure_spans[:8], checks=out['checks']), ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
+156
View File
@@ -0,0 +1,156 @@
"""Reproducible accounting/validation report; never rescales categories to fit a target."""
from pathlib import Path
import hashlib,json,statistics
from profile_local_probe import ROOT,OUT,SOURCE,write
LABELS={
'other':'其余 callback 时间','perturbation_amount':'扰动量计算','state_copy_perturb':'状态复制/施加扰动',
'matrix_zero':'矩阵清零','difference_matrix_write':'差分计算/矩阵写回','baseline_compute':'baseline 原求值(不含 snapshot)',
'initialization':'probe initialization','gas_state_preparation':'probe gas state preparation',
'schedule_retained':'保守依赖保留的原计算','schedule_context_fallback':'context 失败后的 fallback 原计算',
'context_compare':'context/cache 比较','snapshot_capture_save':'baseline snapshot 捕获/保存',
'context_output_restore':'probe context/output restore','node_energy':'node energy','port_outputs':'端口输出赋值',
'mechanical_equations':'mechanical equations','gas_mass_energy':'气体质量/能量方程','remaining_outputs':'remaining outputs',
'pipe_diagnostics':'pipe diagnostics','finite_check':'finite check','schedule_dispatch':'schedule dispatch/管理',
'whole_probe_fallback':'整次 probe fallback'}
def read(label,file='measurement.json'):return json.loads((OUT/label/file).read_text(encoding='utf-8'))
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 profile(label):
p=read(label,'profile.json');f=p['frequency'];cal=statistics.median(p['calibrationTicksPerMarker'])
assert sum(sum(r['ticks']) for r in p['rows'])==p['ledgerSumTicks']==p['sampledCallbackTicks']
assert sum(sum(r['intervals']) for r in p['rows'])==p['markerCount']
assert all(r['evaluations']==p['sampledCallbacks'] for r in p['rows'][1:])
rows=[]
for r in p['rows']:
raw={n:t/f for n,t in zip(p['categories'],r['ticks'])}
corrected={n:(t-cal*c)/f for n,t,c in zip(p['categories'],r['ticks'],r['intervals'])}
assert min(corrected.values())>=0,(label,r['group'],'calibration below resolution',corrected)
rows.append({**r,'seconds':raw,'correctedSeconds':corrected})
boundary=p['sampledCallbackQpcTicks']/p['qpcFrequency']-p['sampledCallbackTicks']/f
# The two pairs of enclosing clocks delimit a real wrapper interval. Keep
# it visible, rather than rescale the TSC buckets to match QPC.
assert abs(boundary)<.01,(label,'clock cross-check',boundary)
rows[0]['seconds']['other']+=boundary
rows[0]['correctedSeconds']['other']+=boundary
return {**p,'rows':rows,'calibrationNs':cal/f*1e9,'timerSeconds':cal*p['markerCount']/f}
def aggregate(labels):
ps=[profile(n) for n in labels];categories=ps[0]['categories'];samples=sum(p['sampledCallbacks'] for p in ps)
rows=[]
for i in range(len(ps[0]['rows'])):
totals={key:{c:sum(p['rows'][i][key][c] for p in ps) for c in categories} for key in ('seconds','correctedSeconds')}
rows.append(dict(group=i-2,evaluations=sum(p['rows'][i]['evaluations'] for p in ps),**totals,
operations=[sum(p['rows'][i].get('operations',[0,0])[k] for p in ps) for k in range(2)]))
totals={key:{c:sum(r[key][c] for r in rows) for c in categories} for key in ('seconds','correctedSeconds')}
return dict(labels=labels,samples=samples,rows=rows,totals=totals,
callbackSeconds=sum(p['sampledCallbackQpcTicks']/p['qpcFrequency'] for p in ps),
timerSeconds=sum(p['timerSeconds'] for p in ps),
calibrationNs=[p['calibrationNs'] for p in ps])
def main():
# Compare every saved run, not only the runs chosen for timing summaries.
reference=json.loads((SOURCE.parent/'all-run-0/measurement.json').read_text(encoding='utf-8'))
keys=['statesSha256','outputsSha256','eventsSha256','finalState','final','propertyWarnings','acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu']
counter_keys=['newtonIterations','newtonConvergenceFailures','contextComparedBytes','contextCopiedBytes','modelCalls','groups']
checks=[]
for path in sorted(OUT.glob('*/measurement.json')):
r=json.loads(path.read_text(encoding='utf-8'))
diff=[k for k in keys if r[k]!=reference[k]]+[k for k in counter_keys if r['diagnostic'][k]!=reference['diagnostic'][k]]
assert not diff,(path,diff);checks.append(dict(run=path.parent.name,differences=diff))
full=profile('validate-all');plan=json.loads((SOURCE.parent/'plan.json').read_text(encoding='utf-8'))
audit=json.loads((SOURCE.parent/'all-audit/probe.json').read_text(encoding='utf-8'))
for g,row in enumerate(full['rows'][2:]):
retained=len(plan['groups'][g]['affectedOperations'])*896
total=sum(audit['groups'][g]['executed'])
assert row['operations']==[retained,total-retained]
assert full['rows'][28]['operations']==[0,0]
for name in ('jacobians','states','outputs','events'):
def digest(p):
with p.open('rb') as f:return hashlib.file_digest(f,'sha256').hexdigest()
assert digest(OUT/f'validate-all/{name}.bin')==digest(SOURCE.parent/f'all-audit/{name}.bin')
# Check the original sources, and the original numerical evaluator in the
# copied translation unit, have not been rewritten by this measurement.
build=json.loads((OUT/'build.json').read_text(encoding='utf-8'))
for name,expected in build['sourceHashes'].items():
assert hashlib.sha256((SOURCE/name).read_text(encoding='utf-8').encode()).hexdigest()==expected
old_model=(SOURCE/'model.c').read_text(encoding='utf-8')
assert old_model in (OUT/'worker/model.c').read_text(encoding='utf-8')
native=json.loads((SOURCE/'build-metadata.json').read_text(encoding='utf-8'))['sourceHashes']
for name,expected in native.items():assert hashlib.sha256((ROOT/'native'/name).read_text(encoding='utf-8').encode()).hexdigest()==expected
main_labels=[f'profile-{i}' for i in range(5)];data=aggregate(main_labels);scale=896/data['samples']
metrics=('jacobianSeconds','solveCpuSeconds','solveSeconds','processSeconds')
def metric(r,k):return r['diagnostic'][k] if k=='jacobianSeconds' else r[k]
controls=[read(f'control-{i}') for i in range(5)];runs=[read(n) for n in main_labels]
medians={mode:{k:statistics.median(metric(r,k) for r in rs) for k in metrics} for mode,rs in [('control',controls),('profile',runs)]}
deltas={k:medians['profile'][k]/medians['control'][k]-1 for k in metrics}
paired={k:[metric(b,k)/metric(a,k)-1 for a,b in zip(controls,runs)] for k in metrics}
densities={str(s):aggregate([f'density{s}-{i}' for i in range(2)]) for s in (8,32)}
coarse=aggregate([f'coarse-{i}' for i in range(3)])
result=dict(numericalChecks=checks,allMatrices=dict(count=896,entries=896*132*132,differentEntries=0),
originalSourcesUnchanged=True,main=data,medians=medians,deltas=deltas,pairedDeltas=paired,densityChecks=densities,
fullCoverageOperationCounts=[r['operations'] for r in full['rows'][2:]],allInstrumented=full,coarse=coarse)
write(OUT/'comparison.json',result)
report=['**局部 probe performance worker:互斥耗时分解(2026-09-17)**\n']
report.append('仅增加独立诊断构建/分析工具及计时副本;原局部优化脚本、生产实现、普通 residual、物性算法、accepted-step check、线性求解器均未修改。模型为 `tests/data/test-mql-8-corrected.json`,0–10 s、BDF、rtol=1e-8,原 atol/步长设置不变。\n')
estimate=sum(data['totals']['correctedSeconds'].values())*scale
cg=coarse['rows'][28];cg_us=sum(cg['seconds'].values())/cg['evaluations']*1e6
report.append(f'**先说明精度边界:** 低密度插桩相对未插桩的callback中位数变化{deltas["jacobianSeconds"]:+.2%}、积分CPU变化{deltas["solveCpuSeconds"]:+.2%};但细分数据扣空标记后折算{estimate:.6f}s,仍比未插桩{medians["control"]["jacobianSeconds"]:.6f}s高{estimate/medians["control"]["jacobianSeconds"]-1:.2%}。所以本轮完成了互斥分类、账本闭合和数值核验,但细分值尚未达到可直接当作未插桩精确耗时的精度。下面使用它判断热点量级与排序,不以这些百分比承诺优化收益。粗粒度独立对照中group26约{cg_us:.3f}µs,是固定底座总量的更可靠参考。\n')
report.append('**测量方法与互斥口径**\n')
report.append('主测量为5对交替串行运行,预热不计入。每连续16次 callback 分层随机抽1次,种子固定可复现;每轮56次,5轮共280次,baseline和每组probe各280次。未抽中callback走保留的原数值函数;抽中才进入计时副本,不开启shadow求值、物性内核入口计数或矩阵落盘。全量插桩+矩阵落盘仅用于数值核验。\n')
report.append('外层总时间仍用QPC;内部用带lfence的TSC读取及约10 KB互斥计数桶,避免每次记录事件数组。启动时要求CPU支持invariant TSC;用覆盖整个积分的QPC/TSC成对读数校准TSC频率。每个相邻区间只归属一个分类、一个row(外层/baseline/group),桶的TSC tick总和必须精确等于抽中callback内部总tick。外层QPC包络与内部TSC包络的差单列计入outer other,是时钟边界间的包装开销;不缩放内部分类。归并后的原始分类总和与抽中callback的QPC时间在浮点精度内相等。这只证明互斥账本闭合,不证明没有插桩扰动。\n')
report.append('在积分完成后,以相同的带屏障读时钟和计数桶更新测9批空标记,取每标记平均成本的中位数。保留原始时间,并另外给出“原始时间−标记数×空标记成本”的估计;未以未插桩总时间强制归一化,也不把任何负数截成0(出现负分类即报告失败)。校正不能消除屏障引起的执行串行化、编译布局、缓存、额外分支和操作分类的间接扰动;极短分类只作数量级参考。原QPC事件记录版保存在test/local-probe-profile-20260917,其校正后总量高估约10.8%,因此本报告继续展示残余偏差,而不宣称空标记校准能消除它。\n')
report.append(f'主测量标记成本:{min(data["calibrationNs"]):.2f}–{max(data["calibrationNs"]):.2f} ns/次。5轮被抽中callback合计 **{data["callbackSeconds"]:.9f} s**,原始分类加和完全相等;估计计时标记成本 **{data["timerSeconds"]:.9f} s**。\n')
report.append('baseline原求值列排除snapshot捕获/保存,baseline完整求值小计包含两者,只作小计不重复相加。snapshot包含入口t/y保存、选定property/pipe检查点复制、schedule结果数组保存。context比较包含元数据和memcmp;restore包含出口context/cache和区间结果恢复。schedule原计算在case原语句两侧划界,switch/循环/区间分派及测量分类开销另计schedule dispatch。\n')
report.append('initialization包括生成模型开头的数组/cache初始化及第一处gas求值前的准备赋值;gas state preparation从第一处gas求值到schedule入口,包括所有gas memo查找、必要物性计算、gas输出赋值及property context seed。schedule外的端口列仅为端口输出赋值。pipe diagnostics包括调用参数中的PH反算、诊断内核和acc累加;将acc写入w及ff的fmin限幅算remaining outputs。后者不是单纯memcpy,不能据名称假定廉价。\n')
report.append('**未插桩/插桩总耗时对照**\n')
labels={'jacobianSeconds':'Jacobian callback累计墙钟','solveCpuSeconds':'积分CPU','solveSeconds':'积分墙钟','processSeconds':'完整native进程墙钟'}
report.append(table(['5轮中位数','未插桩 / s','1/16插桩 / s','变化'],[(labels[k],f'{medians["control"][k]:.6f}',f'{medians["profile"][k]:.6f}',f'{deltas[k]:+.2%}') for k in metrics]))
report.append(table(['配对','control Jacobian','profile Jacobian','control积分','profile积分','control进程','profile进程'],[(i,*[f'{metric(r,k):.6f}' for k in ('jacobianSeconds','solveSeconds','processSeconds') for r in (controls[i],runs[i])]) for i in range(5)]))
report.append('所有样本保留,没有因较慢而删除。整进程包含启动、输出重放/落盘、诊断文件写入、标记校准和退出,不含预先完成的编译和浏览器/API流程。机器频率与调度噪声仍存在,不能把很小的负变化当作计时插桩带来的加速。\n')
report.append('**互斥分类:按抽样折算到一轮896次Jacobian**\n')
report.append('下面为5轮采样累计分类时间×896/280,未使用目标总时间做比例缩放。原始列含时间标记成本;估计列仅扣除空标记成本。\n')
raw=data['totals']['seconds'];adj=data['totals']['correctedSeconds'];total=sum(adj.values())
report.append(table(['分类','原始折算 / s','扣标记估计 / s','估计占比'],[(LABELS[c],f'{raw[c]*scale:.6f}',f'{adj[c]*scale:.6f}',f'{adj[c]/total:.2%}') for c in raw]))
report.append(f'原始折算合计 **{sum(raw.values())*scale:.6f} s**;扣标记估计合计 **{total*scale:.6f} s**;未插桩callback中位数 **{medians["control"]["jacobianSeconds"]:.6f} s**。估计合计与未插桩相差 **{(total*scale/medians["control"]["jacobianSeconds"]-1):+.2%}**,这部分不强行塞进其他分类。\n')
report.append(table(['范围(小计,不再相加)','原始每次 / µs','扣标记每次 / µs','一轮扣标记 / s'],[(name,f'{sum(data["rows"][i]["seconds"].values())/data["rows"][i]["evaluations"]*1e6:.3f}',f'{sum(data["rows"][i]["correctedSeconds"].values())/data["rows"][i]["evaluations"]*1e6:.3f}',f'{sum(data["rows"][i]["correctedSeconds"].values())*scale:.6f}') for name,i in [('baseline完整求值',1)]]))
report.append('**probe总体及逐group平均**\n')
probe_raw=sum(sum(r['seconds'].values()) for r in data['rows'][2:]);probe_adj=sum(sum(r['correctedSeconds'].values()) for r in data['rows'][2:])
report.append(f'共测量{data["samples"]*27:,}次probe,平均原始 **{probe_raw/(data["samples"]*27)*1e6:.3f} µs/probe**,扣标记估计 **{probe_adj/(data["samples"]*27)*1e6:.3f} µs/probe**。组平均范围为lp_eval入口至jac_rhs_reuse返回前,包含数值入口管理及后置统计;外层扰动/矩阵写回不归入probe。\n')
report.append(table(['group','次数','原始总均值 µs','扣标记总均值 µs','保留原计算 µs','fallback原计算 µs','全覆盖实际保留/回退操作'],[(r['group'],r['evaluations'],f'{sum(r["seconds"].values())/r["evaluations"]*1e6:.3f}',f'{sum(r["correctedSeconds"].values())/r["evaluations"]*1e6:.3f}',f'{r["correctedSeconds"]["schedule_retained"]/r["evaluations"]*1e6:.3f}',f'{r["correctedSeconds"]["schedule_context_fallback"]/r["evaluations"]*1e6:.3f}',str(full['rows'][r['group']+2]['operations'])) for r in data['rows'][2:]]))
report.append('各group完整分类数据(不只保留/回退)见comparison.json中main.rows[group+2],seconds与correctedSeconds除以evaluations即为单次均值。\n')
report.append('**低扰动粗粒度交叉核验**\n')
report.append('另构建coarse-worker:只有callback外层以及baseline/完整probe入口和出口的标记,数值调用直接进入原model_eval_local_internal,内部没有细分标记。3轮覆盖全部896次callback(非抽样)。以下原始均值不扣空标记,额外的每probe边界成本很小;它用于检查细分计时对每组总量的偏差,不用于强制缩放细分分类。\n')
report.append(table(['group','粗粒度完整probe µs','细分扣标记 µs','细分相对偏差'],[(g,f'{sum(coarse["rows"][g+2]["seconds"].values())/coarse["rows"][g+2]["evaluations"]*1e6:.3f}',f'{sum(data["rows"][g+2]["correctedSeconds"].values())/data["rows"][g+2]["evaluations"]*1e6:.3f}',f'{(sum(data["rows"][g+2]["correctedSeconds"].values())/data["rows"][g+2]["evaluations"])/(sum(coarse["rows"][g+2]["seconds"].values())/coarse["rows"][g+2]["evaluations"])-1:+.2%}') for g in range(27)]))
report.append(table(['粗粒度轮次','callback s','积分 s','group26 µs'],[(f'coarse-{i}',f'{read(f"coarse-{i}")["diagnostic"]["jacobianSeconds"]:.6f}',f'{read(f"coarse-{i}")["solveSeconds"]:.6f}',f'{sum(profile(f"coarse-{i}")["rows"][28]["seconds"].values())/896*1e6:.3f}') for i in range(3)]))
report.append('**group 26:schedule全跳过后的固定成本**\n')
g=data['rows'][28];n=g['evaluations'];ga=sum(g['correctedSeconds'].values())
cg=coarse['rows'][28];coarse_g26=sum(cg['seconds'].values())/cg['evaluations']*1e6
report.append(f'全量896次核验中,保留原操作=0、fallback原操作=0,与原audit逐操作计数相符。主测量均值:原始 **{sum(g["seconds"].values())/n*1e6:.3f} µs**,扣标记估计 **{ga/n*1e6:.3f} µs**;更低扰动的粗粒度完整probe均值 **{coarse_g26:.3f} µs**。这是本模型、本轨迹、group 26下的固定底座,不是所有group通用的固定常数,也不包含外层差分写回。\n')
report.append(table(['分类','原始均值 / µs','扣标记均值 / µs','估计占比'],[(LABELS[c],f'{g["seconds"][c]/n*1e6:.3f}',f'{v/n*1e6:.3f}',f'{v/ga:.2%}') for c,v in g['correctedSeconds'].items() if g['seconds'][c]]))
report.append('**采样密度与数值校验**\n')
density_rows=[]
for name,d in [('1/8',densities['8']),('1/16',data),('1/32',densities['32'])]:
factor=896/d['samples'];g=d['rows'][28]
density_rows.append((name,d['samples'],f'{sum(d["totals"]["correctedSeconds"].values())*factor:.6f}',f'{d["totals"]["correctedSeconds"]["schedule_retained"]*factor:.6f}',f'{d["totals"]["correctedSeconds"]["schedule_context_fallback"]*factor:.6f}',f'{sum(g["correctedSeconds"].values())/g["evaluations"]*1e6:.3f}'))
report.append(table(['密度','抽中callback','一轮扣标记估计 s','保留计算 s','fallback计算 s','group26 µs'],density_rows))
report.append(table(['只编入插桩、采样关闭','Jacobian s','积分 s','完整进程 s'],[(f'disabled-{i}',*[f'{metric(read(f"disabled-{i}"),k):.6f}' for k in ('jacobianSeconds','solveSeconds','processSeconds')]) for i in range(2)]))
report.append(f'共{len(checks)}次完整运行的states/outputs/event二进制、全部warning字段、最终状态、accepted/rejected steps、Newton iterations/failures、nfev/njev/nlu、context复制/比较字节数、各group保护命中/回退计数均与既有performance worker一致。全量计时副本另核对896个132×132矩阵,共15,611,904元素及各矩阵输入t/y,逐字节一致。内部互斥计时桶的闭合误差为0 TSC tick;加上外层边界差后,分类与QPC总量在浮点精度内闭合。\n')
report.append('全量插桩的每组“保守保留”和“context回退”操作数分别等于依赖计划应执行数、原audit实际执行数减去应执行数;group26两者均为0。整probe fallback未发生。生产native源文件hash、旧performance worker所有源文件hash以及原生成模型函数完整文本均验证未变。\n')
report.append(table(['求解器项目','所有本轮运行保持一致'],[(k,reference[k]) for k in ['acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu']]+[(k,reference['diagnostic'][k]) for k in ['newtonIterations','newtonConvergenceFailures']]))
report.append('**可以支持的诊断结论**\n')
report.append('1. schedule实际保留计算1,390,592次,context保护失败后的回退计算4,620,672次;139,776次区间context校验失败。细分估计回退原计算约0.61s,保守保留原计算约0.42s,1/8和1/32密度下排序相同。保护的主要时间影响体现在失败后的原计算,并非memcmp本身;不能据此推断去掉保护是安全的。\n')
report.append('2. 比较约0.032s,baseline快照/结果保存约0.053s,恢复约0.030s。三者不是零成本,但合计量级显著小于回退原计算。所有这些时间均与计算阶段互斥,baseline小计没有再次加入总和。\n')
report.append('3. 未裁剪的初始化、gas准备和schedule外方程/输出/诊断合计估计约0.58s;其中remaining outputs约0.24s、pipe diagnostics约0.19s,而gas准备约0.056s。remaining outputs包含各管道ff的fmin限幅,pipe diagnostics包含参数PH反算,不能把前者等同简单写内存、后者等同单一内核调用。此处未对某个具体函数做内部剖析,不宣称fmin就是已证实的单函数热点。\n')
report.append('4. group26的schedule保留/回退均为0,依然要执行未裁剪阶段和恢复操作。粗粒度三轮均值21.109–21.263µs;细分扣标记得到24.749µs,高约16.8%,提示小probe的细分扰动占比更高。其细分热点主要是remaining outputs、pipe diagnostics与finite check,但精确占比应保留上述测量误差。\n')
report.append('5. 随机分层采样减少了整轮测量干扰,不能消除被抽中callback自身的串行化/缓存/编译布局影响。细分校正总量仍有6.98%残差;本报告没有把残差摊进other或按比例缩放各分类来制造闭合。跨轮机器负载差异也会影响粗/细计时对比。本轮没有据此修改任何优化实现。\n')
report.append('**证据与复现**\n')
report.append('脚本:tests/manual/profile_local_probe.py、local_probe_profile.h/.c、analyze_local_probe_profile.py。prepare只在本目录生成计时worker;run --stride 1 --matrices做完整数值核验;batch做预热、5对主测量和密度对照;分析脚本生成本报告。各运行目录保留profile.json、measurement.json、probe.json、result.json和二进制结果;build.json保存原文件hash和阶段边界。\n')
(OUT/'report.md').write_text('\n'.join(report),encoding='utf-8')
print(json.dumps(dict(medians=medians,deltas=deltas,rawEstimated=sum(raw.values())*scale,correctedEstimated=total*scale,group26Us=ga/n*1e6,densityRows=density_rows,numericalRuns=len(checks)),ensure_ascii=False,indent=2))
if __name__=='__main__':main()
+236
View File
@@ -0,0 +1,236 @@
"""Read-only coverage and amplitude analysis of saved, phase-paired MQL8 curves.
No simulation, re-pairing, resampling, filtering, or production changes. Integral
metrics are trapezoidal estimates on the saved common grid, not bounds on any
unsampled transient. Occupancy durations use sample-cell weights and are not
located threshold-crossing times. Run with --plots in a matplotlib environment.
"""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
import numpy as np
ROOT = Path(__file__).resolve().parents[2]
BASE = ROOT / 'test/lstp-mainline-20260917'
def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def ratio(numerator, denominator):
return float(100 * numerator / denominator) if denominator else None
def intervals(mask, time, weights):
indices = np.flatnonzero(mask)
blocks = np.split(indices, np.flatnonzero(np.diff(indices) > 1) + 1)
return [dict(firstSample=float(time[b[0]]), lastSample=float(time[b[-1]]),
sampleCount=len(b), cellDurationEstimate=float(weights[b].sum()))
for b in blocks if len(b)]
def mask_summary(mask, time, weights):
return dict(sampleCount=int(mask.sum()), samplePercent=ratio(mask.sum(), len(mask)),
cellDurationEstimate=float(weights[mask].sum()),
timePercentEstimate=ratio(weights[mask].sum(), weights.sum()),
intervals=intervals(mask, time, weights))
def curve_stats(row, actual, reference, time, weights):
error = actual - reference
absolute = np.abs(error)
magnitude = np.abs(reference)
epsilon = row['epsilon']
active = magnitude > epsilon
near = ~active
relative = np.zeros(len(time))
relative[active] = absolute[active] / magnitude[active] * 100
peak = float(magnitude.max())
significant = magnitude > max(epsilon, .01 * peak)
near_bad = near & (absolute > epsilon)
signed_integral = np.concatenate(([0.], np.cumsum(
.5 * (error[1:] + error[:-1]) * np.diff(time))))
result = dict(key=row['key'], quantity=row['quantity'], unit=row['unit'],
epsilon=epsilon, sampleCount=len(time), referencePeak=peak,
activeCount=int(active.sum()), significantCount=int(significant.sum()),
maximumAbsolute=float(absolute.max()),
worstAbsoluteTime=float(time[np.argmax(absolute)]),
maximumAbsolutePercentOfPeak=ratio(absolute.max(), peak),
rmse=float(np.sqrt(np.dot(weights, error**2) / weights.sum())),
relativeL2Percent=ratio(np.sqrt(np.dot(weights, error**2)),
np.sqrt(np.dot(weights, reference**2))),
integratedAbsoluteError=float(np.dot(weights, absolute)),
integratedReferenceMagnitude=float(np.dot(weights, magnitude)),
relativeL1Percent=ratio(np.dot(weights, absolute), np.dot(weights, magnitude)),
signedIntegralError=float(signed_integral[-1]),
maxCumulativeSignedError=float(np.abs(signed_integral).max()),
activeRelativePercentiles={str(p): float(np.percentile(relative[active], p))
if active.any() else None for p in (50, 95, 99, 100)},
significantMaxRelativePercent=float(relative[significant].max())
if significant.any() else None,
nearZeroCount=int(near.sum()),
nearZeroMaxAbsolute=float(absolute[near].max()) if near.any() else None,
nearZeroAboveEpsilon=mask_summary(near_bad, time, weights),
sensitivity={str(factor): int(((magnitude > epsilon * factor)
& (absolute > .05 * magnitude)).sum()) for factor in (.1, 1., 10.)})
masks = {}
for threshold in (1, 5):
mask = active & (relative > threshold)
masks[str(threshold)] = mask
result['above' + str(threshold)] = mask_summary(mask, time, weights) | dict(
activePercent=ratio(mask.sum(), active.sum()))
result['relative5OrNearZeroAbsolute'] = mask_summary(masks['5'] | near_bad, time, weights)
result['examplesAbove5'] = [dict(time=float(time[i]), platform=float(actual[i]),
amesim=float(reference[i]), absoluteError=float(absolute[i]),
relativePercent=float(relative[i])) for i in np.flatnonzero(masks['5'])]
result['nearZeroExamples'] = [dict(time=float(time[i]), platform=float(actual[i]),
amesim=float(reference[i]), absoluteError=float(absolute[i]))
for i in np.flatnonzero(near_bad)]
# Cross-check the earlier diagnostic without changing its epsilon or pairing.
assert result['above5']['sampleCount'] == row['above5PercentCount']
assert int(near_bad.sum()) == row['nearZeroBeyondEpsilon']
return result, masks['5'], near_bad
def analyze(source):
paths = [source / name for name in ('comparison.json', 'curves.npz')]
before = {str(path): digest(path) for path in paths}
comparison = json.loads(paths[0].read_bytes())
arrays = np.load(paths[1], allow_pickle=False)
time = arrays['time']
assert arrays['phaseMatched'].all() and np.all(np.diff(time) > 0)
dt = np.diff(time)
weights = np.r_[dt[0] / 2, (dt[:-1] + dt[1:]) / 2, dt[-1] / 2]
assert np.isclose(weights.sum(), time[-1] - time[0])
rows, masks, near_masks = [], {}, {}
for row in comparison['curves']:
actual, reference = (arrays[s + '|' + row['key']] for s in ('platform', 'amesim'))
assert np.isfinite(actual).all() and np.isfinite(reference).all()
stats, mask, near = curve_stats(row, actual, reference, time, weights)
rows.append(stats)
masks[row['key']], near_masks[row['key']] = mask, near
groups = {}
for quantity in sorted({r['quantity'] for r in rows}):
selected = [r for r in rows if r['quantity'] == quantity]
all_count = len(selected) * len(time)
active_count = sum(r['activeCount'] for r in selected)
union = np.any([masks[r['key']] for r in selected], axis=0)
near_union = np.any([near_masks[r['key']] for r in selected], axis=0)
def worst(field):
candidates = [r for r in selected if r[field] is not None]
if not candidates:
return None
r = max(candidates, key=lambda r: r[field])
return dict(key=r['key'], value=r[field])
groups[quantity] = dict(curveCount=len(selected), sampleCount=all_count,
activeCount=active_count,
above5Count=sum(r['above5']['sampleCount'] for r in selected),
above5SamplePercent=ratio(sum(r['above5']['sampleCount'] for r in selected), all_count),
above5ActivePercent=ratio(sum(r['above5']['sampleCount'] for r in selected), active_count),
anyCurveAbove5=mask_summary(union, time, weights),
nearZeroAboveEpsilonCount=sum(r['nearZeroAboveEpsilon']['sampleCount'] for r in selected),
nearZeroAboveEpsilonSamplePercent=ratio(sum(r['nearZeroAboveEpsilon']['sampleCount'] for r in selected), all_count),
anyCurveNearZeroAboveEpsilon=mask_summary(near_union, time, weights),
anyCurveRelative5OrNearZeroAbsolute=mask_summary(union | near_union, time, weights),
worst={field: worst(field) for field in ('maximumAbsolute',
'maximumAbsolutePercentOfPeak', 'relativeL2Percent', 'relativeL1Percent',
'significantMaxRelativePercent', 'integratedAbsoluteError',
'maxCumulativeSignedError')})
union = np.any(list(masks.values()), axis=0)
near_union = np.any(list(near_masks.values()), axis=0)
windows = []
for start, end in [(0., 1.), (1., 10.8), (10.8, 21.6), (21.6, 32.4), (32.4, 43.2), (43.2, 50.)]:
include = (time >= start - 1e-12) & (time < end - 1e-12 if end < 50 else time <= end)
details = {}
for quantity in ('mass_flow', 'enthalpy_flow'):
selected = [r for r in rows if r['quantity'] == quantity]
details[quantity] = dict(
above5Count=sum(int((masks[r['key']] & include).sum()) for r in selected),
nearZeroAboveEpsilonCount=sum(int((near_masks[r['key']] & include).sum()) for r in selected),
maximumAbsolute=max(float(np.abs(arrays['platform|' + r['key']]
- arrays['amesim|' + r['key']])[include].max()) for r in selected))
windows.append(dict(start=start, end=end, sampleCount=int(include.sum()), groups=details))
assert sum(r['above5']['sampleCount'] for r in rows) == comparison['above5PercentCount']
assert all(digest(path) == before[str(path)] for path in paths)
return dict(source=str(source), inputHashes=before, sourceUnchanged=True,
grid=dict(start=float(time[0]), end=float(time[-1]), count=len(time),
interval=float(np.median(dt))), curveCount=len(rows),
affectedCurveCount=sum(r['above5']['sampleCount'] > 0 for r in rows),
above5Count=sum(r['above5']['sampleCount'] for r in rows),
anyCurveAbove5=mask_summary(union, time, weights),
anyCurveNearZeroAboveEpsilon=mask_summary(near_union, time, weights),
anyCurveRelative5OrNearZeroAbsolute=mask_summary(union | near_union, time, weights),
groups=groups, windows=windows, curves=rows)
def plots(source, out):
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import font_manager
font = Path('C:/Windows/Fonts/msyh.ttc')
if font.exists():
font_manager.fontManager.addfont(str(font))
plt.rcParams['font.family'] = font_manager.FontProperties(fname=str(font)).get_name()
plt.rcParams.update({'font.size': 10, 'axes.unicode_minus': False,
'axes.spines.top': False, 'axes.spines.right': False})
arrays = np.load(source / 'curves.npz', allow_pickle=False)
t = arrays['time']
fig, axes = plt.subplots(2, 3, figsize=(15, 8), constrained_layout=True)
selected = [('amesim_pn3node2_3.reference_mass_flow', '质量流量', 'kg/s', 1e6, 'mg/s'),
('amesim_p4node2_4.reference_enthalpy_flow', '焓流', 'W', 1., 'W')]
for row, (key, label, unit, scale, small_unit) in enumerate(selected):
y, ref = arrays['platform|' + key], arrays['amesim|' + key]
for col, bounds in enumerate(((0, 50), (.27, .35))):
ax = axes[row, col]
mask = (t >= bounds[0] - 1e-12) & (t <= bounds[1] + 1e-12)
factor = 1. if col == 0 else scale
ax.plot(t[mask], ref[mask] * factor, color='#dd863b', lw=2, label='Amesim')
ax.plot(t[mask], y[mask] * factor, color='#126ca6', lw=1, ls='--', label='平台')
ax.set(xlabel='时间 / s', ylabel=unit if col == 0 else small_unit,
title=label + (':50 s 全程' if col == 0 else ':接近零的衰减尾部放大'))
if col == 1:
ax.axvspan(.295, .325, color='#d84b43', alpha=.12, label='差异集中区')
ax.legend(fontsize=8)
ax = axes[row, 2]
group_keys = [k for k in arrays.files if k.startswith('platform|')
and k.endswith('.reference_' + ('mass_flow' if row == 0 else 'enthalpy_flow'))]
envelope = np.max([np.abs(arrays[k] - arrays[k.replace('platform|', 'amesim|', 1)])
for k in group_keys], axis=0)
ax.plot(t, envelope * scale, color='#8b3d50', lw=.9)
ax.set(xlabel='时间 / s', ylabel=small_unit, title=label + ':16 条曲线最大绝对差包络')
for ax in axes[row]:
ax.grid(alpha=.2)
fig.suptitle('八路基线剩余差异:全程、初始衰减段与绝对差\n0–50 s,共同网格 10 ms;事件侧已配对;不代表网格间瞬态的误差上界', fontsize=14)
fig.savefig(out / 'coverage.png', dpi=150)
plt.close(fig)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', type=Path, default=ROOT / 'test/mql8-curve-coverage-20260917')
parser.add_argument('--plots', action='store_true')
args = parser.parse_args()
args.output.mkdir(parents=True, exist_ok=True)
sources = dict(cyclic=BASE / 'event-output-comparison', noncyclic=BASE / 'baseline/noncyclic')
result = dict(method='Saved-grid statistics; original phase pairing and epsilon preserved. '
'L1/L2 normalized by each reference curve; no time interpolation. '
'Integrals and durations are grid estimates only. Noncyclic reference uses ordinary output.',
profiles={name: analyze(path) for name, path in sources.items()})
(args.output / 'coverage.json').write_text(json.dumps(result, ensure_ascii=False,
indent=2, allow_nan=False) + '\n', encoding='utf-8')
if args.plots:
plots(sources['cyclic'], args.output)
for name, profile in result['profiles'].items():
print(name, 'above5:', profile['above5Count'], 'any time:', profile['anyCurveAbove5'])
for quantity in ('force', 'mass_flow', 'enthalpy_flow', 'pressure', 'temperature', 'gap', 'velocity'):
print(quantity, json.dumps(profile['groups'][quantity], ensure_ascii=False))
if __name__ == '__main__':
main()
+144
View File
@@ -0,0 +1,144 @@
"""Summarize independent serial measurements; audit times are never performance data."""
from pathlib import Path
import json,statistics
import real_skip_experiment as experiment
OUT=experiment.OUT
def load(path):return json.loads(path.read_text(encoding='utf-8'))
def write(path,value):experiment.write(path,value)
def compare_bytes(actual,reference):
offset=0
with actual.open('rb') as a,reference.open('rb') as b:
while True:
x=a.read(1024*1024);y=b.read(1024*1024)
if x!=y:
local=next((i for i,(u,v) in enumerate(zip(x,y)) if u!=v),min(len(x),len(y)))
different=offset+local
record={'file':str(actual),'reference':str(reference),'firstByte':different}
if actual.name=='jacobians.bin':
width=(1+132+132*132)*8;j,within=divmod(different,width);element=within//8
record.update(jacobian=j+1,field='t' if element==0 else 'y' if element<=132 else 'Jacobian',element=element)
if element>132:record.update(row=(element-133)%132,column=(element-133)//132)
write(OUT/'first-file-mismatch.json',record)
raise AssertionError(record)
if not x:break
offset+=len(x)
return offset
def main():
checks=[]
for label in ['audit-control-run','audit-skip-run','audit-forced-reject']:
for name in ['jacobians','states','outputs','events']:
ref=experiment.BASE.parent/('all-audit' if name=='jacobians' else 'all-run-0')/(name+'.bin')
n=compare_bytes(OUT/label/(name+'.bin'),ref)
checks.append(dict(run=label,field=name,bytes=n,exact=True))
write(OUT/'byte-comparison.json',checks)
rows=load(OUT/'performance.json');audit=load(OUT/'audit-skip-run/validation.json');forced=load(OUT/'audit-forced-reject/validation.json')
assert len(rows)==14 and all(r['exact'] for r in rows)
a=[r for r in rows if not r['skip']];b=[r for r in rows if r['skip']]
assert all(r['counters']['realSkips']==896 and r['counters']['nativeOriginalExecutions']==0 and r['counters']['rejects']==0 for r in b)
def timing(row,key):return row[key] if key in row else row['counters'][key]
keys=['originalSeconds','validationSeconds','overlayPatchSeconds','commitSeconds','fallbackSeconds','pathSeconds','baselineRecordSeconds','jacobianSeconds','solveCpuSeconds','solveSeconds']
def stat(values):return dict(median=statistics.median(values),min=min(values),max=max(values))
summary={side:{k:stat([timing(r,k) for r in rs]) for k in keys} for side,rs in [('control',a),('skip',b)]}
pairs=[]
for i in range(1,8):
c=next(r for r in a if r['label']==f'pair-{i}-control');s=next(r for r in b if r['label']==f'pair-{i}-skip')
stage=sum(s['counters'][k] for k in ['validationSeconds','overlayPatchSeconds','commitSeconds'])
pairs.append(dict(pair=i,originalUs=c['counters']['originalSeconds']/896*1e6,replayStagesUs=stage/896*1e6,
targetNetSavingSeconds=c['counters']['pathSeconds']-s['counters']['pathSeconds'],
originalMinusReplaySeconds=c['counters']['originalSeconds']-stage,
incrementalMetadataSeconds=s['counters']['baselineRecordSeconds']-c['counters']['baselineRecordSeconds'],
jacobianDeltaSeconds=s['jacobianSeconds']-c['jacobianSeconds'],
integrationCpuDeltaSeconds=s['solveCpuSeconds']-c['solveCpuSeconds'],integrationWallDeltaSeconds=s['solveSeconds']-c['solveSeconds']))
summary['paired']={k:stat([r[k] for r in pairs]) for k in pairs[0] if k!='pair'}
summary['pairs']=pairs;write(OUT/'performance-summary.json',summary)
med=lambda side,k:summary[side][k]['median']
orig=summary['paired']['originalUs']['median'];replay=summary['paired']['replayStagesUs']['median']
lines=['# R288 / position16 最小 real skip 实验','',
'## 结论','',
'1. **完整求解结果逐位一致。** 0–10 s 全轨迹,896 个 132×132 Jacobian(15,611,904 个元素)及每个 t/y、states、outputs、事件、最终状态与既有基线一致;没有使用数值容差。',
'2. **896/896 次真实 skip;自然 reject/fallback 为 0。** commit 896 次,原目标 native operation 实际执行 0 次,Reference 双路执行 0 次。',
f'3. **当前 replay 更贵。** 7 轮中位数:原 operation {orig:.3f} µs/次,validation + overlay/patch + commit {replay:.3f} µs/次({replay/orig:.2f} 倍)。目标完整路径 control {med("control","pathSeconds")/896*1e6:.3f} µs/次,real skip {med("skip","pathSeconds")/896*1e6:.3f} µs/次。',
'4. **暂不把这一实现直接扩展到 position52。** 正确性门槛已满足,净收益门槛未满足。应先降低 metadata 采集、事务复制和解释执行成本;本轮不能据此推断 position52 或其他 interval 的收益。','',
'## 范围与实现','',
'- 全部改动仅在独立生成的实验 worker 及 `tests/manual`;生产路径、默认开关、property cache 语义、原 whole-context guard 未改动。',
'- 入口仅为 `lp_color == 6 && region == 288` 的 `case 16`,位于原 `lp_reuse` 失败之后。其他位置继续原执行。',
'- baseline position16 用单独命名空间的 kernels 采集必需的有序事件 metadata。其他 operation 使用原 kernels,没有全局访问插桩。',
'- guard 来自已验证的 shadow 源码,构建时校验其 SHA-256,并断言 guard 函数保持一致;唯一变量替换是将原事务入口 count 改为当前真实 probe 入口 count。',
'- 复制当前 probe 到私有 overlay;按当前 entries 验证 first-match/miss,重定位逻辑 slot,从当前 count 追加。通过后构造地址已转换的有序 write set,再 commit。已有 entries、未写字段和其他 pipe slots 保持原值。',
'- Observer、capacity/scratch、memo lifetime/value、消费字段、valid、pipe branch、未知副作用/非有限值等原保护条件保留;失败只回退原 operation。',
'- 性能版不执行 Reference、不导出访问日志或 Jacobian、不逐次比较完整 context。保留运行所需 metadata、严格 guard、overlay/patch、计时和累计计数。','',
'## 正确性证据','',
'| 检查 | 结果 |','|---|---:|',
'| Jacobian、t/y 与既有 all-audit 文件逐字节比较 | 896/896 一致 |',
'| 每个 Jacobian 的 baseline + 27 group evaluator 出口 | 25,088/25,088 一致 |',
'| 目标 operation 入口 / 出口 | 896 / 896 一致 |',
'| 返回状态、dy/w、active property entries 有序字段/valid、全部 pipe slots | 逐位一致 |',
'| memo 每次 Jacobian 的表内容、owner 绑定、recording 生命周期 | 一致 |',
'| 所有 probe 的 memo entries 只读检查 | 通过 |',
'| gas memo 内容、kernel 绑定及计数 | 一致 |',
'| errno、x87/SSE flags/rounding、warning/observer | 一致 |',
'| count 差异 / slot relocation 的目标 probe | 638 / 571 |',
'| 顺序 append | 1,658 |',
'| PT miss → PT miss / 近零流量无查询 | 829 / 67 |','',
'Audit 对照来自**独立 control 进程真实执行当前 probe**的出口,约 1.88 GB 二进制数据。跨进程地址比较采用 owner/function 绑定身份,数值字段保持原始位模式;不把 C padding 当成数值。memo 表逐 Jacobian 比较,后续每个 probe 同时检查整表未变。目标入口、出口与所有 group 的完整 evaluator 出口均参与比较。audit 的 I/O 保留并恢复 errno、x87 和 SSE 环境。','',
'| 求解器计数 | control / real skip |','|---|---:|',
'| accepted / rejected | 10840 / 918 |','| Newton iterations / convergence failures | 19371 / 798 |',
'| nfev / njev / nlu | 44467 / 896 / 3106 |','| solverStarts / stateTransitions | 4 / 1 |','',
'额外负例:每 128 个目标 probe 注入一次晚期 nonfinite-output reject,发生在 overlay 已执行有序更新之后。7 次 reject 均完成无污染检查,fallback/native 原执行各 7 次,commit/skip 各 889 次;全轨迹仍与同一 control 和既有基线逐位一致。没有放宽 guard,也未改变容差。','',
'## 性能方法与原始数据','',
'Windows / MinGW GCC,原构建优化选项(`-O3 -ffp-contract=off -fno-fast-math`)。完成正确性和回退测试后单独编译性能 worker;control、skip 各预热一次,再交替串行运行 7 组。表中顺序就是实际顺序。每轮 states/outputs/events 指纹和全部指定求解器计数保持一致,skip 每轮均为 896、reject 0、原执行 0。','',
'QPC 粗粒度计时;累计整数 tick,积分过程中不做浮点时间换算。validation 包括语义校验所必需的临时有序更新;overlay/patch 包括当前 context 复制及提交 write set 构造;commit 是真实字段写回。完整 target 路径另计,包含调度、计数和额外计时开销。未减去计时器自身成本,未使用 shadow/audit 时间推断性能。','',
'| 运行顺序 | 原执行 µs/次 | validation µs/次 | overlay/patch µs/次 | commit µs/次 | fallback ms | target 总 ms | baseline pos16 总 ms | Jacobian s | 积分 CPU s | 积分 wall s | skip/reject |',
'|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|']
for r in rows:
c=r['counters'];us=lambda k:c[k]/896*1e6
lines.append(f'| {r["label"]} | {us("originalSeconds"):.3f} | {us("validationSeconds"):.3f} | {us("overlayPatchSeconds"):.3f} | {us("commitSeconds"):.3f} | {c["fallbackSeconds"]*1e3:.3f} | {c["pathSeconds"]*1e3:.3f} | {c["baselineRecordSeconds"]*1e3:.3f} | {r["jacobianSeconds"]:.6f} | {r["solveCpuSeconds"]:.6f} | {r["solveSeconds"]:.6f} | {c["realSkips"]}/{c["rejects"]} |')
lines+=['','baseline pos16:control 为原 baseline operation;skip 包含原 baseline operation + 本次 replay 必需 metadata 采集,不能漏算这部分成本。性能各轮没有自然 reject,因此 fallback 总时间为 0;这不代表一次 fallback 的成本为零,本轮未估计该分支的单次性能。','',
'### 中位数及 min/max','', '| 项目 | control 中位数 [min, max] | real skip 中位数 [min, max] |','|---|---:|---:|']
for k in keys:
def cell(side):
v=summary[side][k];return f'{v["median"]:.9f} [{v["min"]:.9f}, {v["max"]:.9f}]'
lines.append(f'| {k}(s,896 次累计) | {cell("control")} | {cell("skip")} |')
lines+=['','### 配对差值','',
'| 组 | 原计算 − replay 三阶段 ms | target 完整路径净节省 ms | 新增 metadata 采集 ms | Jacobian Δ s | 积分 CPU Δ s | 积分 wall Δ s |','|---|---:|---:|---:|---:|---:|---:|']
for r in pairs:lines.append(f'| {r["pair"]} | {r["originalMinusReplaySeconds"]*1e3:.3f} | {r["targetNetSavingSeconds"]*1e3:.3f} | {r["incrementalMetadataSeconds"]*1e3:.3f} | {r["jacobianDeltaSeconds"]:.6f} | {r["integrationCpuDeltaSeconds"]:.6f} | {r["integrationWallDeltaSeconds"]:.6f} |')
paired=summary['paired']
lines+=['','净节省为正表示节省,Δ = skip − control。','',
f'- 原计算成本 − replay 三阶段成本:配对中位数 **{paired["originalMinusReplaySeconds"]["median"]*1e3:.3f} ms / 896 次**。',
f'- target 完整路径净节省:配对中位数 **{paired["targetNetSavingSeconds"]["median"]*1e3:.3f} ms / 896 次**;范围 [{paired["targetNetSavingSeconds"]["min"]*1e3:.3f}, {paired["targetNetSavingSeconds"]["max"]*1e3:.3f}] ms。',
f'- 此外 baseline metadata 采集增加:配对中位数 **{paired["incrementalMetadataSeconds"]["median"]*1e3:.3f} ms**。',
f'- Jacobian callback 配对 Δ:中位数 {paired["jacobianDeltaSeconds"]["median"]:.6f} s,范围 [{paired["jacobianDeltaSeconds"]["min"]:.6f}, {paired["jacobianDeltaSeconds"]["max"]:.6f}] s。',
f'- 积分 wall 配对 Δ:中位数 {paired["integrationWallDeltaSeconds"]["median"]:.6f} s,范围 [{paired["integrationWallDeltaSeconds"]["min"]:.6f}, {paired["integrationWallDeltaSeconds"]["max"]:.6f}] s。',
'','全局时间受调频、调度和系统负载影响,不能把某轮 Jacobian/积分变快归因于这个 operation。目标路径在全部配对中均更慢,已经足以否定当前实现的局部净收益;本轮不预测整个 local probe 的最终加速比例。','',
'## 成本解释及下一阶段条件','',
'原 operation 在现有 Jacobian memo 的只读复用环境下已经很便宜;当前严格 replay 仍要初始化/复制 156,816 B 的 overlay(含完整 memo),解释事件并构造 write set。运行所需有效 metadata 为 2,224–23,344 B,Plan 固定预留 90,224 B,patch 预留 16,384 B。这些是当前隔离实现的实际成本,不是机制理论上的下限。',
'','因此,本轮证明了目标路径可以安全真实跳过,但没有证明性能优化成立。下一步应先针对事务存储和 metadata 表达做最小化,再重复同样的正确性与交替性能验收;不因 position16 正确就直接扩大到 position52/整个 R475/全部 reuse interval。','',
'## 复现与证据位置','',
'运行目录:`test/r288-real-skip-20260917/`。依赖上一轮生成的 local-probe worker、context-access worker 和 shadow-certified 源码;工具链复用原实验配置,不安装依赖。','',
'```powershell',
'.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py prepare --audit',
'.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py prepare --audit --skip',
'.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py run --audit',
'.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py run --audit --skip',
'.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py run --audit --skip --label audit-forced-reject --force 128',
'.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py prepare',
'.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py prepare --skip',
'.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py benchmark --pairs 7',
'.venv-win/Scripts/python.exe tests/manual/analyze_real_skip.py','```','',
'- `audit-{control,skip}-run/validation.json`:正确性、独立执行计数、完整求解器计数及文件指纹。',
'- `audit-forced-reject/validation.json`:真实回退及 rollback 检查。',
'- `byte-comparison.json`:全部 Jacobian/t/y、states、outputs、events 与原始基线的逐字节比较。',
'- `audit-control-run/audit.bin`:所有 group 和目标 operation 的实际执行对照出口。',
'- `performance.json`:14 次按实际顺序记录的原始数据;各轮目录保留独立日志和结果。',
'- `performance-summary.json`:中位数、min/max 和逐对差值。',
'- `{audit,perf}-{control,skip}/build.json`:源文件哈希、guard 一致性和构建记录。','']
report=Path(__file__).with_name('r288_real_skip_report.md');report.write_text('\n'.join(lines),encoding='utf-8')
print(json.dumps(summary['paired'],ensure_ascii=False,indent=2));print(report)
if __name__=='__main__':main()
+178
View File
@@ -0,0 +1,178 @@
"""Write the R288 cost-floor report from raw audit and serial A/B/C results."""
from pathlib import Path
import json,statistics,subprocess,re,hashlib
import specialized_replay_experiment as experiment
import analyze_real_skip as bytecheck
ROOT=experiment.ROOT;OUT=experiment.OUT;HERE=Path(__file__).parent
def read(p):return json.loads(p.read_text(encoding='utf-8'))
def write(p,o):experiment.base.write(p,o)
def stat(v):return dict(median=statistics.median(v),min=min(v),max=max(v))
def fmt(s,d=3):return f'{s["median"]:.{d}f} [{s["min"]:.{d}f}, {s["max"]:.{d}f}]'
def main():
bytecheck.OUT=OUT;checks=[]
for mode in 'CD':
for label in ['audit-skip-run','forced-reject']:
r=read(OUT/mode/label/'validation.json');t=read(OUT/mode/label/'typed-summary.json')
assert r['exact'] and t['contractChecks']==t['oracleChecks']==896
assert t['negativeChecks']==(20 if label=='audit-skip-run' else 0)
for name in ['jacobians','states','outputs','events']:
ref=experiment.base.BASE.parent/('all-audit' if name=='jacobians' else 'all-run-0')/(name+'.bin')
size=bytecheck.compare_bytes(OUT/mode/label/(name+'.bin'),ref)
checks.append(dict(mode=mode,run=label,name=name,bytes=size,exact=True))
write(OUT/'byte-comparison.json',checks)
cc,_,_=experiment.base.ex.builder.toolchain();nm=Path(cc).with_name('nm.exe')
symbols=subprocess.check_output([str(nm),'--defined-only',str(OUT/'C/perf-skip/model.exe')],text=True)
assert not re.search(r'(?m)\b(?:plan|overlay|before_reject|after_reject|oracle_generic|oracle_typed|replay_overlay|ax_access)$',symbols)
write(OUT/'C/perf-skip/stripped-audit-proof.json',dict(noGenericInterpreter=True,noFullOverlay=True,noAuditOracle=True))
for mode in 'CD':
for build in (OUT/mode).glob('*/build.json'):
v=read(build);v['guardMatchesShadow']=False;v['wholeContextGuardUnchanged']=True
v['typedGuardValidation']='C and D audit: 896 dynamic contracts, 896 generic decisions/patches, 20 negative cases, full exit comparison'
write(build,v)
rows=read(OUT/'performance.json');assert len(rows)==27
by={m:[r for r in rows if r['mode']==m] for m in 'ABC'}
metrics={}
for m,rs in by.items():
transformed=[]
for r in rs:
assert r['exact'];c=r['counters'];u=lambda k:c[k]/896*1e6
if m!='A':assert c['realSkips']==896 and c['nativeOriginalExecutions']==c['rejects']==0
transformed.append(dict(original=u('originalSeconds'),validation=u('validationSeconds'),patch=u('overlayPatchSeconds'),commit=u('commitSeconds'),outsideStages=u('pathSeconds')-(u('originalSeconds') if m=='A' else u('validationSeconds')+u('overlayPatchSeconds')+u('commitSeconds')),
stages=u('validationSeconds')+u('overlayPatchSeconds')+u('commitSeconds'),path=u('pathSeconds'),baseline=u('baselineRecordSeconds'),jacobian=r['jacobianSeconds'],cpu=r['solveCpuSeconds'],wall=r['solveSeconds']))
metrics[m]={k:stat([v[k] for v in transformed]) for k in transformed[0]}
pairs=[]
for i in range(1,10):
a=next(r for r in by['A'] if r['label']==f'round-{i}');ac=a['counters']
for m in 'BC':
r=next(r for r in by[m] if r['label']==f'round-{i}');c=r['counters']
stages=c['validationSeconds']+c['overlayPatchSeconds']+c['commitSeconds']
metadata=c['baselineRecordSeconds']-ac['baselineRecordSeconds']
local=(ac['originalSeconds']-stages)*1e6/896
pairs.append(dict(round=i,mode=m,probeSavingUs=local,metadataIncrementUs=metadata*1e6/896,
mechanismSavingMs=((ac['originalSeconds']-stages)-metadata)*1e3,
completePathSavingMs=((ac['pathSeconds']-c['pathSeconds'])-metadata)*1e3,
jacobianDelta=r['jacobianSeconds']-a['jacobianSeconds'],cpuDelta=r['solveCpuSeconds']-a['solveCpuSeconds'],wallDelta=r['solveSeconds']-a['solveSeconds']))
pair_stats={m:{k:stat([p[k] for p in pairs if p['mode']==m]) for k in pairs[0] if k not in ['mode','round']} for m in 'BC'}
summary=dict(metrics=metrics,paired=pair_stats,rounds=pairs)
write(OUT/'performance-summary.json',summary)
profile_folder=OUT/'P/detailed'
if not (profile_folder/'attribution.json').exists():profile_folder=OUT/'P/perf-skip-run'
profile=read(profile_folder/'attribution.json');pc=read(profile_folder/'real-skip.json');frequency=profile['frequency']
cat={v['id']:dict(microsecondsPerProbe=v['ticks']/frequency/896*1e6,calls=v['calls']) for v in profile['categories']}
empty=profile['emptyTimerTicks']/frequency/profile['emptyTimerCalls']*1e6
write(OUT/'attribution-summary.json',dict(emptyBracketUs=empty,categories=cat,dispatchOnlyUs=profile['dispatchTicks']/frequency/profile['dispatchRepeats']/896*1e6,source=str(profile_folder)))
a=metrics['A'];b=metrics['B'];c=metrics['C'];paired=pair_stats['C']
below=c['stages']['median']<a['original']['median'];gain=paired['mechanismSavingMs']['median']>0
conclusion='当前实现对 R288 没有净收益;这种低成本 operation 应直接原计算。' if not gain else '当前测量显示 R288 有净收益;仍只授权本案例,下一步应独立验证 codegen contract 和更昂贵 operation。'
lines=['# R288 / position16:typed semantic replay 成本下限实验','',
'## 三个问题的答案','',
f'1. 去掉完整 overlay 与通用事件解释器后,三阶段 replay 的每轮均值中位数为 **{c["stages"]["median"]:.3f} µs/次**,各轮范围 **{c["stages"]["min"]:.3f}–{c["stages"]["max"]:.3f} µs/次**。包含调度、计时及计数的完整目标路径中位数为 **{c["path"]["median"]:.3f} µs/次**。这是本实现、工具链和机器的实测结果,不是理论最低成本。',
f'2. 同批 A 的原 operation 为 **{a["original"]["median"]:.3f} µs/次**;专用 replay **{"低于" if below else "仍高于"}原计算**。与历史 1.143 µs 不直接跨批比较。',
f'3. 计入 baseline 捕获新增成本,配对完整机制净节省中位数为 **{paired["mechanismSavingMs"]["median"]:.3f} ms / 896 次**;含完整路径计时/统计开销的口径为 **{paired["completePathSavingMs"]["median"]:.3f} ms**。**{conclusion}**','',
'## 范围与事务语义','',
'所有实验仅针对 group6 / R288 / position16,在独立 worker 中、原 whole-context guard 失败之后启用。没有扩展 position52、R475 或其他 interval,没有接入生产默认路径。已有工作区改动不属于本实验的修改范围。','',
'- `fp_validate` 只读当前真实 probe;保留输入位比较、有限值、FP/errno、observer、count/capacity、memo owner/recording、全部 active entry 绑定、pipe branch 等保护。',
'- 两次 PT 查询依次按当前有序 entries 验证 miss;第二次查询还显式检查第一个 pending entry 的虚拟匹配。append 位置由当前 count 决定,不使用 baseline slot。',
'- memo key 的 hash 在 baseline 捕获时计算;probe 仍按相同容量、同一 bounded linear-probe 顺序查找,并逐位比较完整 key 和 value。没有复制 memo,也没有绕过 memo 验证。',
'- 只支持已验证的两个固定 schema:67 次近零流量无查询、829 次 PT miss → PT miss。不支持的路径直接 reject。',
'- 这两个 schema 中,物性计算消费的条目均是本 operation 新建的条目。初始化、字段写入、valid 测试和读取之间的条件由固定 schema 保证,并在正确性版对每个 baseline 的所有原始访问逐条证明;不是把这些 guard 删除。',
'- `fp_prepare` 最多构造两个 pending property entries、一个 pipe[0]、新 count、q[45] 和 density/pipe memo reuse 增量。所有真实写入仅在 `fp_commit` 发生。未被写入的 probe 数据保持原值。','',
'专用版是固定 R288 schema 的 typed 原型,不是通用生产 codegen。静态 schema 的核对表从全轨迹真实 generic 记录自动生成;原 native 源文件受既有 SHA-256 约束,未知源代码变化会停止构建。','',
'## Metadata 与临时存储','',
'| 项目 | generic | specialized |','|---|---:|---:|',
'| 每次 baseline 有效 metadata | 2,224–23,344 B | 240 B |',
'| 持久 metadata 固定预留 | 90,224 B | 240 B |',
'| 捕获临时区 | 通用事件记录器 | 96 B |',
'| context/memo/pipe 完整 overlay | 156,816 B | 0 B |',
'| pending patch | 16,384 B write-set,另有 overlay | 456 B,总计且不重复计算 |','',
'- **静态可确定**:medium 常量、字段布局、读取/写入顺序、valid 位演变、最多两个追加条目、memo 类型、pipe[0]、q[45]。',
'- **每个 baseline 动态捕获**:输入、输出、U/D 物性字段、pipe memo key/value、三个 memo hash、入口 count、owner/Jacobian 生命周期及 FP/errno 条件,共 240 B。',
'- **probe 才解析**:当前有序 entries、query miss、当前 count/capacity、memo 实际位置和值、owner/observer/pipe 条件;验证成功后构造 456 B pending。','',
'## 正确性验收','',
'C 验收版:同一次 baseline 真实执行同时生成 generic 事件与小型 metadata;核对 896 份完整访问 schema、896 次 generic/typed accept/reject 和最终 patch,包括逻辑 slot 映射。',
'D 验收版:使用性能版相同的最小 native 捕获 hooks,逐位核对先前真实 generic baseline 记录(跨进程 memo 地址转换为 owner 绑定身份),然后重复全部求解验收。该离线记录只用于 audit;性能 worker 不读取这些记录。','',
'| 检查 | 结果 |','|---|---:|',
'| Jacobian 全元素与 t/y | 896/896 逐字节一致,15,611,904 个矩阵元素 |',
'| evaluator 返回状态、dy/w 和完整 context 出口 | 25,088/25,088 一致 |',
'| 目标 operation 入口/出口、property count/有序字段/valid、pipe cache | 全部一致 |',
'| memo entries、只读生命周期、owner/kernel 绑定及计数 | 全部一致 |',
'| states/outputs/events、最终状态、warning、FP/errno | 全部一致 |',
'| 正常目标 attempts / skip / reject / 原执行 | 896 / 896 / 0 / 0 |',
'| 负例 guard 判定及无写入检查 | C、D 各 20 项通过 |',
'| 晚期 forced reject | C、D 各 7 次,无污染;889 skip、7 原执行 |',
'| count 差异 / relocation / append | 638 / 571 / 1,658 |','',
'负例包含 observer、无容量/第二次追加不足、pipe hit、memo recording/miss/value、输入改变、晚期非有限输出、过期生命周期、FP flags/rounding、entry owner、U/D 查询转 hit,以及已拒绝 metadata 的 consumed/valid/已有条目更新/未知副作用标记。后四项是错误记录传播检查;逐字段读写与 valid 演变的实质检查来自 896 份完整访问 contract 对照。','',
'| accepted | rejected | Newton iterations | convergence failures | nfev | njev | nlu | solverStarts | stateTransitions |',
'|---:|---:|---:|---:|---:|---:|---:|---:|---:|',
'|10840|918|19371|798|44467|896|3106|4|1|','',
'全部使用位比较和逐字节比较,没有更改容差。性能版二进制符号检查确认无 generic interpreter、完整 overlay 或 audit oracle。','',
'## Generic 成本归因','',
f'单独归因 worker 的总 replay 阶段为 {(pc["validationSeconds"]+pc["overlayPatchSeconds"]+pc["commitSeconds"])/896*1e6:.3f} µs/次;该数值含细粒度插桩,不与历史 22.521 µs 强行逐项相加。空 QPC bracket 均值 {empty:.5f} µs,细粒度嵌套计时会额外放大短事件成本。',
'以下分类均为每个目标 probe 的平均原始计时;父项含子项,明确标记重叠,不能相加当作净成本。','',
'| 分类 | µs/次 | 口径 |','|---|---:|---|']
def row(name,k,note):lines.append(f'| {name} | {cat[k]["microsecondsPerProbe"]:.4f} | {note} |')
lines.append(f'| baseline 原执行 + metadata 捕获 | {pc["baselineRecordSeconds"]/896*1e6:.4f} | 新增成本由后面的同批 A/B 配对得出 |')
row('PT first-match / miss',10,'真实顺序扫描;包含一组自身计时')
row('consumed-field 读取事件',1,'含 metadata 分派和嵌套字段比较')
row('其中:字段值/绑定比较',13,'是上一行的子项')
row('valid 测试事件',8,'含事件分派')
row('memo 验证',12,'完整 key/value 与 bounded lookup;SCALAR 事件的子项')
row('完整 overlay 初始化/复制/绑定',14,'包含 context、memo、全部 pipe')
row('logical slot relocation',11,'包含映射冲突检查;属于 ALLOCATE/QUERY 子项')
for name,k in [('pending entry 字段写入',2),('pending valid 更新',3),('追加与 count 更新',7),('pipe patch 写入',5),('write-set 构造',15),('commit',16)]:row(name,k,'原始 generic 实测,含自身计时')
for name,k in [('公共 guard(映射初始化之前)',17),('overlay 清零/poison 初始化',18),('property context/entries 复制',19),('全部 pipe 复制',20),('memo header/entries 复制',21),('overlay 指针绑定',22)]:
if cat[k]['calls']:row(name,k,'细分项;已经包含在对应父项内')
dispatch=profile['dispatchTicks']/frequency/profile['dispatchRepeats']/896*1e6
lines+=[f'| 仅 event metadata 遍历/类型分派微基准 | {dispatch:.4f} | 每份真实 plan 重复 100 次;不做 guard/write,不能当作真实解释器的独立可加项 |',
f'| 空计时 bracket | {empty:.5f} | 每组含首尾 QPC;累计统计另有开销,最终以低扰动 A/B/C 为准 |','',
'通用解释器的实际成本分布在 metadata 访问、字段比较、slot 映射、写入分派和循环控制中,没有一个能独立相减的精确“dispatch 时间”。归因版保留原始嵌套数据,最终性能版移除这些细粒度计时。','',
'## 低扰动 A/B/C 性能','',
'A=原 operation,B=原 generic replay,C=typed/minimal replay。各预热一次,9 组按 ABC → BCA → CAB 循环,全部串行。无 Reference、完整 context 比较或详细访问日志;保留机制所需 metadata、guard、patch 和少量累计计时。每轮 B/C 均 skip 896、reject 0、原执行 0,并核对采样结果和全部指定计数。','',
'### 中位数 [min, max]','',
'| 项目 | A | B | C |','|---|---:|---:|---:|']
for k in ['original','validation','patch','commit','stages','path','outsideStages','baseline','jacobian','cpu','wall']:
unit='s' if k in ['jacobian','cpu','wall'] else 'µs/次'
lines.append(f'| {k} ({unit}) | {fmt(a[k])} | {fmt(b[k])} | {fmt(c[k])} |')
lines+=['','replay 三阶段先在每一轮内求和再取中位数,因此不必等于三个分项中位数之和。baseline 项包括该次 baseline operation 本身;捕获新增成本应减去 A 的 baseline 项。B 的 patch 项含全 overlay copy + write-set;C 的 patch 项仅小型 pending 构造。outsideStages 是完整路径减去被包围的阶段时间,包含外层计时、统计、调度与元数据 bookkeeping;它不是纯计时器成本。无自然 reject,fallback 总时间为 0,不代表单次 fallback 免费。','',
'### 每轮原始数据(实际执行顺序)','',
'| 轮 / 模式 | 原 op µs | validation µs | patch µs | commit µs | replay 三阶段 µs | 完整路径 µs | baseline µs | Jacobian s | CPU s | wall s |','|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|']
for r in rows:
v=r['counters'];u=lambda k:v[k]/896*1e6
lines.append(f'| {r["label"]} / {r["mode"]} | {u("originalSeconds"):.3f} | {u("validationSeconds"):.3f} | {u("overlayPatchSeconds"):.3f} | {u("commitSeconds"):.3f} | {u("validationSeconds")+u("overlayPatchSeconds")+u("commitSeconds"):.3f} | {u("pathSeconds"):.3f} | {u("baselineRecordSeconds"):.3f} | {r["jacobianSeconds"]:.6f} | {r["solveCpuSeconds"]:.6f} | {r["solveSeconds"]:.6f} |')
lines+=['','### 两种收益口径','',
'- probe 局部净节省 = A 原 operation − validation − patch − commit。',
'- 完整机制净节省 = 896 × probe 局部净节省 − (baseline 捕获总耗时 − A baseline 原执行耗时)。',
'- 再给出包含外层调度、计时与统计的保守口径:A 完整目标路径 − replay 完整目标路径 − baseline 新增成本。',
'- 正数为节省,负数为额外成本。时间均使用同组配对,不拿历史 1.143 µs 作分母。','',
'| 轮 / 模式 | probe 净节省 µs/次 | baseline 新增 µs/次 | 完整机制净节省 ms/896 次 | 含外层开销净节省 ms | Jacobian Δ s | CPU Δ s | wall Δ s |','|---|---:|---:|---:|---:|---:|---:|---:|']
for p in pairs:lines.append(f'| {p["round"]} / {p["mode"]} | {p["probeSavingUs"]:.3f} | {p["metadataIncrementUs"]:.3f} | {p["mechanismSavingMs"]:.3f} | {p["completePathSavingMs"]:.3f} | {p["jacobianDelta"]:.6f} | {p["cpuDelta"]:.6f} | {p["wallDelta"]:.6f} |')
lines+=['','| 配对统计:中位数 [min, max] | B | C |','|---|---:|---:|']
for k in pair_stats['B']:lines.append(f'| {k} | {fmt(pair_stats["B"][k],6)} | {fmt(pair_stats["C"][k],6)} |')
lines+=['','所有 operation 阶段使用 QPC 墙钟计时,包含调度长尾;积分 CPU 时间单独来自求解器统计。系统负载和频率使本批数据存在较大波动(包括 commit 分项的极端值),因此报告同时保留中位数、min/max 和每组配对结果,不将归因插桩或调度延迟声称为精确的算法 CPU 成本。9 组局部与完整机制净收益均为负;不从总 Jacobian/积分墙钟的正负波动推断整个 local probe 的收益,也不声称已证明理论成本下限。计时未做不可靠的逐事件扣减。','',
'## 停止条件与交付','',conclusion,
'本轮到此停止,不继续为 R288 寻找更多微优化,也不扩展到 position52。保留生产原计算路径和原 whole-context guard。','',
'代码入口:`tests/manual/specialized_replay_experiment.py`。核心为 `typed_replay_core.inc`、`typed_replay_runtime.inc`;`typed_contract_check.inc` 与 `typed_oracle.inc` 只用于 audit。报告生成器为 `analyze_typed_replay.py`。','',
'复现顺序(项目根目录,使用既有工具链,不安装依赖):','',
'```powershell',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode P',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode P',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode C --audit',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode C --audit',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode C --audit --label forced-reject --force 128',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode D --audit',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode D --audit',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode D --audit --label forced-reject --force 128',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode A',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode B',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode C',
'.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py benchmark --rounds 9',
'.venv-win/Scripts/python.exe tests/manual/analyze_typed_replay.py','```','',
'证据目录:`test/r288-typed-replay-20260917/`。`performance.json` 保存 27 次原始记录;`performance-summary.json` 保存统计和逐组净收益;`byte-comparison.json` 保存逐字节结果;C/D 的 `validation.json` 和 `typed-summary.json` 保存全部正确性计数。P 的 `attribution.json` 保存原始 tick、调用次数和计时器测量。','']
(HERE/'r288_typed_replay_report.md').write_text('\n'.join(lines),encoding='utf-8')
print(json.dumps(dict(A=a['original'],B=b['stages'],C=c['stages'],C_path=c['path'],net=paired),ensure_ascii=False,indent=2))
if __name__=='__main__':main()
+102
View File
@@ -0,0 +1,102 @@
"""Summarize full-trajectory position379 keys and directly timed tail work."""
from collections import Counter,defaultdict
import hashlib,json,statistics,struct
from pathlib import Path
ROOT=Path(__file__).resolve().parents[2]
OUT=ROOT/'test/position379-tail-20260917'
RECORD=struct.Struct('<Qii5QddIHHiIHHi3Qii')
def stats(xs):
return dict(count=len(xs),mean=statistics.mean(xs),median=statistics.median(xs),min=min(xs),max=max(xs),total=sum(xs))
def analyze(folder):
info=json.loads((folder/'tail.json').read_text());assert info['recordBytes']==RECORD.size==128
records=list(RECORD.iter_unpack((folder/'tail-records.bin').read_bytes()))
assert len(records)==info['entries']
clock=info['frequency'];scale=1e6/clock
empty=statistics.median(x[0] for x in struct.iter_unpack('<Q',(folder/'empty-clock.bin').read_bytes()))
groups=defaultdict(list)
for r in records:groups[r[0]].append(r)
jacobians=[];key_hits=0;general_hits=0;group_hits=Counter();env_changes=0;output_mismatches=0
environment_changes={name:Counter() for name in ('mxcsr','x87_control','x87_status','errno')}
rejects=Counter();baseline_env_changes=0
for j,rr in groups.items():
baseline=[r for r in rr if r[1]==-1];assert len(baseline)==1
b=baseline[0];assert len({r[1] for r in rr})==len(rr)
baseline_env_changes+=b[10:14]!=b[14:18]
buckets=defaultdict(list)
for r in rr:
key=r[3:8];buckets[key].append(r[1]);env_changes+=r[10:14]!=r[14:18]
for i,name in enumerate(environment_changes):
if r[10+i]!=r[14+i]:environment_changes[name][r[10+i],r[14+i]]+=1
if r[2]:
assert r[10:14]==r[14:18]==b[10:14]==b[14:18]
assert r[3:8]==b[3:8]
elif r[1]>=0:
if b[10:14]!=b[14:18]:rejects['baseline_changes_x87_status']+=1
elif r[10:14]!=b[10:14]:rejects['environment_differs']+=1
elif key!=b[3:8]:rejects['key_differs']+=1
else:rejects['other']+=1
if r[1]>=0 and key==b[3:8]:
key_hits+=1;group_hits[r[1]]+=1
if struct.pack('<dd',r[8],r[9])!=struct.pack('<dd',b[8],b[9]):output_mismatches+=1
general_hits+=len(rr)-len(buckets)
jacobians.append(dict(jacobian=j,baselineKey=[f'{x:016x}' for x in b[3:8]],
uniqueKeys=len(buckets),baselineSharingGroups=[r[1] for r in rr if r[1]>=0 and r[3:8]==b[3:8]],
groups=[dict(key=[f'{x:016x}' for x in key],groups=colors) for key,colors in buckets.items()],
keyHits=sum(r[1]>=0 and r[3:8]==b[3:8] for r in rr),
guardedHits=sum(r[2] for r in rr)))
assert len(groups)==896 and output_mismatches==0
baseline=[r for r in records if r[1]<0];probes=[r for r in records if r[1]>=0];hits=[r for r in records if r[2]];executed=[r for r in records if r[21]]
assert sum(r[18] for r in records)==info['tailTicks']
# Conservative gate: charge raw lookup/capture/reset (including their timer
# overhead), save tail work after subtracting one empty timer per hit.
saved=sum(max(0,r[18]-empty) for r in hits)*scale
lookup=sum(r[19] for r in probes)*scale
baseline_cost=(info['baseLookupTicks']+info['captureTicks']+info['resetTicks'])*scale
guarded_hit_rate=len(hits)/len(probes)
result=dict(label=folder.name,clockHz=clock,emptyClockNs=empty/clock*1e9,entries=len(records),tailExecutions=len(executed),baselineEntries=len(baseline),probeEntries=len(probes),
tailRawUs=stats([r[18]*scale for r in executed]),tailCorrectedUs=stats([max(0,r[18]-empty)*scale for r in executed]),
hitTailCorrectedUs=stats([max(0,r[18]-empty)*scale for r in hits if r[21]]) if hits and any(r[21] for r in hits) else None,
lookupHitUs=stats([r[19]*scale for r in hits]),lookupMissUs=stats([r[19]*scale for r in probes if not r[2]]),
baselineAddedUsPerJacobian=baseline_cost/896,keyHits=key_hits,keyHitRate=key_hits/len(probes),
baselineCaptureWithoutResetUs=stats([(r[19]+r[20])*scale for r in baseline]),
unlimitedMemoTheoreticalHits=general_hits,unlimitedMemoHitRate=general_hits/len(probes),
guardedHits=len(hits),guardedHitRate=guarded_hit_rate,envChanges=env_changes,bitKeyOutputMismatches=output_mismatches,
baselineEnvChanges=baseline_env_changes,rejects=dict(rejects),
environmentChanges={name:[dict(before=f'0x{a:x}',after=f'0x{b:x}',count=n) for (a,b),n in c.items()] for name,c in environment_changes.items()},
uniqueKeysHistogram=dict(Counter(j['uniqueKeys'] for j in jacobians)),groupHits=dict(group_hits),
rawTailSavedMs=sum(r[18] for r in hits)*scale/1000,correctedTailSavedMs=saved/1000,
allProbeLookupMs=lookup/1000,baselineAddedMs=baseline_cost/1000,
conservativeDiagnosticNetMs=(saved-lookup-baseline_cost)/1000,
hitBudgetUsAfterBaselineAndMisses=(saved-baseline_cost-sum(r[19] for r in probes if not r[2])*scale)/len(hits) if hits else 0,
inputSha256=hashlib.sha256((folder/'tail-records.bin').read_bytes()).hexdigest())
(folder/'key-groups.json').write_text(json.dumps(jacobians,indent=2)+'\n',encoding='utf-8')
(folder/'analysis.json').write_text(json.dumps(result,indent=2)+'\n',encoding='utf-8')
return result
def main():
rows=[analyze(p) for p in sorted(OUT.glob('diagnostic-*')) if (p/'tail-records.bin').exists()]
assert rows
# An original-execution miss may legitimately change FP status. Only the
# proposed hit subset must have zero effect (asserted above).
gate=all(r['conservativeDiagnosticNetMs']>0 and r['lookupHitUs']['mean']<r['hitTailCorrectedUs']['mean'] and r['keyHitRate']>.8 for r in rows)
timing=[]
for folder in sorted(OUT.glob('tail-only-*')):
if not (folder/'tail-records.bin').exists():continue
info=json.loads((folder/'tail.json').read_text());records=list(RECORD.iter_unpack((folder/'tail-records.bin').read_bytes()))
assert len(records)==info['entries']==info['executions']==24192
empty=statistics.median(x[0] for x in struct.iter_unpack('<Q',(folder/'empty-clock.bin').read_bytes()))
scale=1e6/info['frequency']
timing.append(dict(label=folder.name,entries=len(records),
tailRawUs=stats([r[18]*scale for r in records]),
tailCorrectedUs=stats([max(0,r[18]-empty)*scale for r in records]),
warning='K-only control: all key/environment sampling occurs AFTER tail. Ignore hit/G/H in this worker.',
inputSha256=hashlib.sha256((folder/'tail-records.bin').read_bytes()).hexdigest()))
result=dict(rounds=rows,tailOnlyTiming=timing,profitableDiagnosticGate=gate,
note='Preliminary gate only: full in-solver mechanism cost and exact context audit required before a benefit claim.')
(OUT/'diagnostic-analysis.json').write_text(json.dumps(result,indent=2)+'\n',encoding='utf-8')
print(json.dumps(result,indent=2))
if __name__=='__main__':main()
+120
View File
@@ -0,0 +1,120 @@
"""Exact numerical gates and measured performance report for local probes."""
from pathlib import Path
import hashlib, json, statistics, subprocess
ROOT=Path(__file__).resolve().parents[2]
OUT=ROOT/'test/local-probe-20260917'
def read(path):return json.loads((OUT/path).read_text(encoding='utf-8'))
def table(headers,rows):return '\n| '+' | '.join(headers)+' |\n| '+' | '.join(['---']*len(headers))+' |\n'+'\n'.join('| '+' | '.join(map(str,row))+' |' for row in rows)+'\n'
def compare_matrices(left,right,n):
size=8*(1+n+n*n);checked=0
with left.open('rb') as a,right.open('rb') as b:
while True:
x,y=a.read(size),b.read(size)
if not x and not y:break
assert len(x)==len(y)==size,('matrix record size',checked)
assert x[:8*(1+n)]==y[:8*(1+n)],('Jacobian input t/y differs',checked)
assert x[8*(1+n):]==y[8*(1+n):],('Jacobian matrix bits differ',checked)
checked+=1
return dict(matrices=checked,entries=checked*n*n,differentEntries=0,inputTimesAndStatesIdentical=True)
def main():
plan=read('plan.json');reference=read('reference-audit/measurement.json')
cases=['group26-final-audit','small-final-audit','all-audit']
exact=['statesSha256','outputsSha256','eventsSha256','finalState','final','propertyWarnings',
'acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu']
checks=[]
for path in sorted(OUT.glob('*/measurement.json')):
obj=json.loads(path.read_text(encoding='utf-8'))
differences=[k for k in exact if obj[k]!=reference[k]]
for k in ('newtonIterations','newtonConvergenceFailures'):
if obj['diagnostic'][k]!=reference['diagnostic'][k]:differences.append(k)
checks.append(dict(run=path.parent.name,differences=differences))
assert not any(x['differences'] for x in checks),checks
matrices={name:compare_matrices(OUT/'reference-audit/jacobians.bin',OUT/name/'jacobians.bin',len(plan['stateKeys'])) for name in cases}
allrun=read('all-audit/measurement.json');diag=allrun['diagnostic'];groups=[]
for theory,actual in zip(plan['groups'],diag['groups']):
assert all(a+b==896 for a,b in zip(actual['executed'],actual['skipped']))
assert not any(actual['skipped'][i] for i in theory['affectedOperations'])
groups.append({**theory,**actual,'executedTotal':sum(actual['executed']),'skippedTotal':sum(actual['skipped'])})
metrics=['solveCpuSeconds','solveSeconds','processSeconds','jacobianSeconds']
measurements=[];medians={}
for name in ('reference','all'):
runs=[read(f'{name}-run-{i}/measurement.json') for i in range(5)]
for i,r in enumerate(runs):
measurements.append(dict(mode=name,run=i,**{k:r[k] for k in metrics[:-1]},jacobianSeconds=r['diagnostic']['jacobianSeconds']))
medians[name]={k:statistics.median(r['diagnostic'][k] if k=='jacobianSeconds' else r[k] for r in runs) for k in metrics}
reductions={k:1-medians['all'][k]/medians['reference'][k] for k in metrics}
source_meta=read('audit/build-metadata.json')
source_unchanged=all(hashlib.sha256((ROOT/'native'/key).read_text(encoding='utf-8').encode()).hexdigest()==value for key,value in source_meta['sourceHashes'].items())
assert source_unchanged
# The original evaluator, ordinary residual, temperature checker and linear
# solver are not patched. The added evaluator is reachable only from lp_eval.
original=(OUT/'original-model.c').read_text(encoding='utf-8')
for mode in ('audit','worker'):
emitted=(OUT/mode/'model.c').read_text(encoding='utf-8')
assert original in emitted,'original model source changed'
old=ROOT/'test/probe-state-residual-20260916/baseline-run-1'
prior={}
for name in ('states','outputs'):
if (old/f'{name}.bin').exists():
with (old/f'{name}.bin').open('rb') as f:prior[name]=hashlib.file_digest(f,'sha256').hexdigest()==reference[name+'Sha256']
assert all(prior.values())
result=dict(numericalChecks=checks,matrixComparisons=matrices,
shadow=dict(comparisons=diag['auditComparisons'],differentComparisons=diag['auditDifferences'],
comparedDerivativeAndOutputValues=diag['auditComparisons']*(len(plan['stateKeys'])+len(reference['final']))),
originalNativeSourcesUnchanged=source_unchanged,originalGeneratedModelVerbatim=True,priorUninstrumentedOutputsEqual=prior,
medians=medians,reductions=reductions,measurements=measurements,groups=groups,
workspaceBytes=diag['workspaceBytes'],copiedBytes=diag['contextCopiedBytes'],comparedBytes=diag['contextComparedBytes'])
(OUT/'comparison.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
report=[]
report.append('**局部 Jacobian probe 实验结果(2026-09-17)**\n')
report.append('本次增加独立实验构建工具,未接入前端或生产默认路径。生产 ordinary residual、物性算法、accepted-step check、线性求解器及原始生成模型源码保持不变;实验 worker 只在 Jacobian callback 中选择额外生成的局部求值函数。对象为 `tests/data/test-mql-8-corrected.json`,原提交 `1aac220`,CVODE 7.4.0,0–10 s,BDF,rtol=1e-8,沿用原 atol、步长和线性求解设置。\n')
report.append('**实施范围与保护**\n')
report.append('从现有 StateDependencies 和 EvaluationSchedule 保守计算各 group 的状态依赖闭包,未解析用户 C 或手写组件白名单。未知依赖按全部状态处理;当前实验拒绝包含循环代数块的模型。每组生成连续的不受扰动调度区间。扰动时间和组外状态必须与本次 Jacobian baseline 逐位一致,跨 Jacobian 基准失效。\n')
report.append('原型只裁剪 flow/stream/linear/alias 调度段。气体状态准备、后续节点能量、端口传播、机械方程、各管路诊断输出和有限值检查继续执行原代码。纯代数区间依据闭包复制基准的所有区间输出;包含上下文调用的区间,还要求入口的完整 live property states、count/capacity、observer/Jacobian 指针及全部 pipe cache 与 baseline 逐位一致,再恢复基准出口上下文和所有区间输出。任何不一致均执行原计算,无近似匹配、无放宽容差。\n')
report.append('保留 property context 中的 h/rho/黏度/等熵派生字段及种子顺序,避免只按显式 p,T 或 p,h 误判等价。已有 Jacobian 标量表在 baseline 后保持只读,跳过调用只减少其评估/复用统计,不改变表内数值。温度观察路径不走局部函数。\n')
report.append('初版第26组通过数值验证,但无条件保存所有快照导致约5.11 GB复制。最终版按启用组选择快照,并在纯代数操作之间共享上下文检查点:321个调度边界只需57个上下文检查点。只共享确定不含上下文调用的检查点;未放宽任何数值或上下文比较条件。\n')
report.append('**先验证数值等价**\n')
report.append(table(['验证阶段','完整矩阵数','逐位比较矩阵元素数','矩阵差异','每次Jacobian求值的dy/w对照'],[(name,matrices[name]['matrices'],matrices[name]['entries'],0,'25,088次,差异0') for name in cases]))
report.append('每份矩阵记录同时比较 t、132维输入 y 和132×132矩阵的原始双精度位,包含稀疏结构之外的零元素。另用独立 Jacobian memo workspace 在同一(t,y)执行原实现,比较每次 baseline/probe 的全部132个dy及1784个w;任意差异立即终止。该影子计算只在 audit worker 启用,不计入求解器nfev;性能 worker 完全关闭影子计算、矩阵落盘及内核入口计数。\n')
report.append(table(['项目','原实现','局部全部group','结果'],[
('states / outputs 原始二进制','完整文件','完整文件','逐字节一致'),('warning','全部告警字段','全部告警字段','一致'),
('event','事件时刻及132维事件后状态','同左','逐位一致'),
*[(k,reference[k],allrun[k],'一致') for k in ('acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu')],
('Newton iterations',reference['diagnostic']['newtonIterations'],diag['newtonIterations'],'一致'),
('Newton convergence failures',reference['diagnostic']['newtonConvergenceFailures'],diag['newtonConvergenceFailures'],'一致')]))
report.append('所有已保存运行的完整结果均对照通过;还核对了前一轮真正未插桩 worker 的 states/outputs,两者逐字节相同。图闭包及循环块拒绝的5项单元检查通过;原生 guard 测试验证了同p,T但派生rho改变、pipe cache改变、上下文长度/指针改变、t改变、组外状态改变和跨基准失效均不会误命中,并验证纯代数区间恢复不会覆盖当前物性上下文。未发现需要用容差掩盖的依赖裁剪差异。\n')
report.append('**实际执行范围**\n')
total=sum(g['executedTotal']+g['skippedTotal'] for g in groups);skipped=sum(g['skippedTotal'] for g in groups)
report.append(f'Jacobian次数仍为896,每个Jacobian仍调用1次baseline及27次probe,合计25,088次,其中probe为24,192次。probe 调度操作从 **{total:,}** 次降为 **{total-skipped:,}** 次,实际跳过 **{skipped:,}({skipped/total:.2%})**。与此前88.12%的结构无关操作比例相比,完整上下文保护、保留的诊断/后续操作限制了可安全跳过的范围。\n')
report.append(table(['group','扰动状态数','图上受影响操作数/484','实际执行操作/896次probe','跳过比例','上下文保护回退次数'],[(g['color'],len(g['stateIndices']),len(g['affectedOperations']),g['executedTotal'],f'{g["skippedTotal"]/(484*896):.2%}',g['contextMisses']) for g in groups]))
report.append('每组的被扰动状态、受影响操作编号、每一项操作的执行和跳过计数均保存在 `comparison.json`;操作编号、名称、输入输出和依赖状态在 `plan.json`。所有图上可能受影响操作的跳过次数均为0。下面统计24,192次probe实际进入内核的次数,不含baseline、影子计算或普通residual。\n')
kernel_rows=[]
for i,name in enumerate(diag['kernelNames']):
a=sum(row[i] for row in reference['diagnostic']['kernelCalls'][1:]);b=sum(row[i] for row in diag['kernelCalls'][1:])
kernel_rows.append((name,a,b,f'{1-b/a:.2%}' if a else '—'))
report.append(table(['实际计算','原实现','局部probe','减少'],kernel_rows))
report.append('gas、PH、density和pipe root的实际调用未变;已有精确缓存本来就已避免大量无关求根。此次节省来自流量、等熵、黏度及属性查找的实际执行。管路诊断仍全部执行,未将其删除或替换为未验证的近似值。\n')
report.append('**实际耗时**\n')
report.append('同一不含影子核验的可执行文件,通过 LOCAL_PROBE_MASK=0 和全组mask切换;排除一次预热,5对交替顺序串行测量,每次均完整输出states/outputs。Jacobian为整个callback的QPC墙钟时间;积分CPU为现有整进程CPU计时;完整native仿真时间从启动worker到输出完成并退出,包含DLL加载、初始化、积分、采样、输出重放/编码/落盘及退出。该数字不包含浏览器、API排队或已完成的编译。\n')
names={'jacobianSeconds':'Jacobian阶段墙钟','solveCpuSeconds':'积分CPU','solveSeconds':'积分墙钟','processSeconds':'完整native仿真墙钟'}
report.append(table(['中位数','原实现 / s','局部全部group / s','减少'],[(names[k],f'{medians["reference"][k]:.6f}',f'{medians["all"][k]:.6f}',f'{reductions[k]:.2%}') for k in ('jacobianSeconds','solveCpuSeconds','solveSeconds','processSeconds')]))
report.append(table(['序号/模式','Jacobian / s','积分CPU / s','积分墙钟 / s','完整native / s'],[(f'{r["run"]}/{r["mode"]}',*[f'{r[k]:.6f}' for k in ('jacobianSeconds','solveCpuSeconds','solveSeconds','processSeconds')]) for r in measurements]))
report.append('全部样本保留,包括原实现第2次较慢的样本,没有按有利结果筛选。当前测量显示收益,但绝对秒数受机器负载和频率影响;不能把前一日7.07s与今日结果直接相减归为优化收益。\n')
report.append(f'最终局部工作区占 **{diag["workspaceBytes"]:,} 字节**。整轮额外上下文复制 **{diag["contextCopiedBytes"]:,} 字节**、比较请求覆盖 **{diag["contextComparedBytes"]:,} 字节**;memcmp可能提前终止,后者不是硬件实际读取字节数。上述机制的开销已经包含在性能测量中。还包括switch分派、组外状态检查和恢复输出。\n')
cold=[]
for name in ('reference','all'):
path=OUT/f'cold-{name}-pipeline.json'
if path.exists():cold.append(json.loads(path.read_text()))
if cold:
report.append('另外从工程JSON开始执行实验的冷构建完整链(读取/方程生成/实验代码生成/GCC编译/DLL准备/worker运行/输出),实测如下。两种运行使用同一实验构建机制,冷编译时间会波动;这不是前端API链路测量,也不能用单次冷启动数据判断probe收益。\n')
report.append(table(['冷构建链','准备与构建 / s','完整native / s','完整实验流水线 / s'],[(x['mode'],f'{x["prepareSeconds"]:.6f}',f'{x["workerSeconds"]:.6f}',f'{x["pipelineSeconds"]:.6f}') for x in cold]))
report.append('**边界与后续方向**\n')
report.append('此版本可作为验证原型,暂不建议直接接入默认求解路径:仅支持当前无循环调度的生成模型,快照内存随模型规模增长,实验状态为独立worker内的静态运行期对象,不适合直接嵌入同进程并行仿真。较大group的属性上下文经常变化,保守整上下文比较会使本可独立的区间回退,收益小于最初理论无关计算预算。\n')
report.append('下一步若继续推进,应先研究保留完整读写语义的更细粒度context依赖,再扩展到诊断/其他阶段。不能直接删除上下文保护,也不能把88.12%的无关操作比例解释为可实现的同等加速。当前实验已证明可以在不改变数值轨迹的前提下减少一部分probe计算,但没有消除全部无关执行。\n')
(OUT/'report.md').write_text('\n'.join(report),encoding='utf-8')
print(json.dumps(dict(medians=medians,reductions=reductions,matrices=matrices,skippedOperations=skipped,operationReduction=skipped/total,numericalRuns=len(checks)),ensure_ascii=False,indent=2))
if __name__=='__main__':main()
+339
View File
@@ -0,0 +1,339 @@
# Context 访问级清单:实测数据
由 `analyze_context_access.py` 从访问事件生成。slot、Jacobian 和 position 均从 0 开始;group=-1 为 baseline。
以下以 Jacobian 200 为主案例,补充 position 52 的 PH 命中/未命中路径。完整逐次记录位于 `test/context-access-20260917/operations.json`;原始访问事件在 `audit/access.jsonl`。
## Jacobian 1,group 18,position 52
t=1.3389986581645337e-08;count 11 → 12;capacity=256;输出 `0.033351539732391834`;model evaluator 返回 `1`。
### 查询与首次匹配
| 顺序 | 类型 | 完整 key:p, T 或 h, R, cp, Tref, slope, mu, muT, S | medium kind | 首个匹配 slot | 扫描条数 |
|---|---|---|---|---|---|
| 1 | PH | `141437.08341422619, 200554.29912985844, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | 7 | 8 |
| 2 | PT | `141437.08341422619, 336.75814451285157, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | 7 | 8 |
| 3 | PT | `100000.73403195803, 293.15086386132788, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | miss | 11 |
### 实际选中后的字段读取
| 域 / slot | 字段 |
|---|---|
| pipes[28] | `valid` |
| states[7] | `T`, `isentropic_exponent`, `isentropic_factor`, `medium`, `medium.Tref`, `medium.cp`, `medium.real_helium`, `medium.slope`, `mu`, `p`, `rho`, `temperatures` |
| states[11] | `T`, `isentropic_exponent`, `isentropic_factor`, `jacobian`, `medium.R`, `medium.S`, `medium.Tref`, `medium.cp`, `medium.mu`, `medium.muT`, `medium.real_helium`, `medium.slope`, `p`, `rho`, `temperatures` |
查询扫描另外按短路次序读取 `valid & PT/H`、`p`、`T/h`、medium;未匹配项的字段不等于被用于物性计算。
### 选中后的 valid 测试
| 顺序 | slot | mask | 结果 |
|---|---|---|---|
| 1 | 7 | 8 | 8 |
| 2 | 7 | 16 | 16 |
| 3 | 11 | 16 | 0 |
| 4 | 11 | 4 | 0 |
| 5 | 7 | 4 | 4 |
### 必须保留的 probe 数据
保留入口 `states[0:11]` 的全部字段;此路径没有写已有物性条目。仅追加以下新条目,修改本 operation 的 pipe 槽;其余 pipe 槽保持 probe 值。
### 有序写入动作
| 顺序 | 目标 | 写入值 | 已知同值写入 |
|---|---|---|---|
| 1 | `context[0].count` | 12 | — |
| 2 | `states[11].*` | 完整 struct 清零(显式写入) | — |
| 3 | `states[11].medium` | 复制上述完整 medium | — |
| 4 | `states[11].p` | 100000.73403195803 | — |
| 5 | `states[11].T` | 293.15086386132788 | — |
| 6 | `states[11].valid` | 1 | — |
| 7 | `states[11].temperatures` | NULL | 是 |
| 8 | `states[11].jacobian` | 当前 context 的 Jacobian memo 指针 | — |
| 9 | `states[11].rho` | 0.16419111112032281 | — |
| 10 | `states[11].valid` | 5 | — |
| 11 | `states[11].isentropic_factor` | 0.59983509720368744 | — |
| 12 | `states[11].isentropic_exponent` | 0.4000169505487694 | — |
| 13 | `states[11].valid` | 21 | — |
| 14 | `pipes[28].valid` | 0 | 是 |
| 15 | `pipes[28].medium` | 复制上述完整 medium | — |
| 16 | `pipes[28].p1` | 141437.08341422619 | — |
| 17 | `pipes[28].p2` | 100000.73403195803 | — |
| 18 | `pipes[28].T` | 336.75814451285157 | — |
| 19 | `pipes[28].diameter` | 0.02 | — |
| 20 | `pipes[28].length` | 1 | — |
| 21 | `pipes[28].roughness` | 0.0022499999999999998 | — |
| 22 | `pipes[28].kind` | 1 | — |
| 23 | `pipes[28].flow` | 0.033351539732391834 | — |
| 24 | `pipes[28].valid` | 1 | — |
新条目分配分支均为 positive/finite key 且 `count < capacity`;每次在当前 count 追加,再 count++。未初始化槽的 struct 首次清零不计入“已知同值写入”。
### Fallback 原因
查询 key/首匹配逻辑条目不同、已消费字段或已测试 valid 位不同、容量不足转 scratch、observer 非空、pipe 命中分支改变,或无法建立无冲突的 slot 映射时,执行原 operation。此清单不授权放宽现有 guard。
## Jacobian 2,group 18,position 52
t=2.5903206074281044e-08;count 16 → 18;capacity=256;输出 `0.13649710784608116`;model evaluator 返回 `1`。
### 查询与首次匹配
| 顺序 | 类型 | 完整 key:p, T 或 h, R, cp, Tref, slope, mu, muT, S | medium kind | 首个匹配 slot | 扫描条数 |
|---|---|---|---|---|---|
| 1 | PH | `755686.80622119282, 1871284.0942772815, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | miss | 16 |
| 2 | PT | `755686.80622119282, 658.33729595925956, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | miss | 16 |
| 3 | PT | `755686.80622119282, 658.33729595925956, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | 16 | 17 |
| 4 | PT | `100004.87065313551, 293.15011545458538, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | miss | 17 |
### 实际选中后的字段读取
| 域 / slot | 字段 |
|---|---|
| pipes[28] | `valid` |
| states[16] | `T`, `isentropic_exponent`, `isentropic_factor`, `jacobian`, `medium`, `medium.R`, `medium.S`, `medium.Tref`, `medium.cp`, `medium.mu`, `medium.muT`, `medium.real_helium`, `medium.slope`, `mu`, `p`, `rho`, `temperatures` |
| states[17] | `T`, `isentropic_exponent`, `isentropic_factor`, `jacobian`, `medium.R`, `medium.S`, `medium.Tref`, `medium.cp`, `medium.mu`, `medium.muT`, `medium.real_helium`, `medium.slope`, `p`, `rho`, `temperatures` |
查询扫描另外按短路次序读取 `valid & PT/H`、`p`、`T/h`、medium;未匹配项的字段不等于被用于物性计算。
### 选中后的 valid 测试
| 顺序 | slot | mask | 结果 |
|---|---|---|---|
| 1 | 16 | 2 | 0 |
| 2 | 16 | 8 | 0 |
| 3 | 16 | 16 | 0 |
| 4 | 16 | 4 | 0 |
| 5 | 17 | 16 | 0 |
| 6 | 17 | 4 | 0 |
| 7 | 16 | 4 | 4 |
### 必须保留的 probe 数据
保留入口 `states[0:16]` 的全部字段;此路径没有写已有物性条目。仅追加以下新条目,修改本 operation 的 pipe 槽;其余 pipe 槽保持 probe 值。
### 有序写入动作
| 顺序 | 目标 | 写入值 | 已知同值写入 |
|---|---|---|---|
| 1 | `context[0].count` | 17 | — |
| 2 | `states[16].*` | 完整 struct 清零(显式写入) | — |
| 3 | `states[16].medium` | 复制上述完整 medium | — |
| 4 | `states[16].p` | 755686.80622119282 | — |
| 5 | `states[16].T` | 658.33729595925956 | — |
| 6 | `states[16].valid` | 1 | — |
| 7 | `states[16].temperatures` | NULL | 是 |
| 8 | `states[16].jacobian` | 当前 context 的 Jacobian memo 指针 | — |
| 9 | `states[16].h` | 1871284.0942772815 | — |
| 10 | `states[16].valid` | 3 | — |
| 11 | `states[16].mu` | 3.4336896373307344e-05 | — |
| 12 | `states[16].valid` | 11 | — |
| 13 | `states[16].rho` | 0.55213584825878159 | — |
| 14 | `states[16].valid` | 15 | — |
| 15 | `states[16].isentropic_factor` | 0.59936841974574895 | — |
| 16 | `states[16].isentropic_exponent` | 0.4000342868314512 | — |
| 17 | `states[16].valid` | 31 | — |
| 18 | `context[0].count` | 18 | — |
| 19 | `states[17].*` | 完整 struct 清零(显式写入) | — |
| 20 | `states[17].medium` | 复制上述完整 medium | — |
| 21 | `states[17].p` | 100004.87065313551 | — |
| 22 | `states[17].T` | 293.15011545458538 | — |
| 23 | `states[17].valid` | 1 | — |
| 24 | `states[17].temperatures` | NULL | 是 |
| 25 | `states[17].jacobian` | 当前 context 的 Jacobian memo 指针 | — |
| 26 | `states[17].rho` | 0.16419832110282509 | — |
| 27 | `states[17].valid` | 5 | — |
| 28 | `states[17].isentropic_factor` | 0.59983509001364188 | — |
| 29 | `states[17].isentropic_exponent` | 0.40001695130827536 | — |
| 30 | `states[17].valid` | 21 | — |
| 31 | `pipes[28].valid` | 0 | 是 |
| 32 | `pipes[28].medium` | 复制上述完整 medium | — |
| 33 | `pipes[28].p1` | 755686.80622119282 | — |
| 34 | `pipes[28].p2` | 100004.87065313551 | — |
| 35 | `pipes[28].T` | 658.33729595925956 | — |
| 36 | `pipes[28].diameter` | 0.02 | — |
| 37 | `pipes[28].length` | 1 | — |
| 38 | `pipes[28].roughness` | 0.0022499999999999998 | — |
| 39 | `pipes[28].kind` | 1 | — |
| 40 | `pipes[28].flow` | 0.13649710784608116 | — |
| 41 | `pipes[28].valid` | 1 | — |
新条目分配分支均为 positive/finite key 且 `count < capacity`;每次在当前 count 追加,再 count++。未初始化槽的 struct 首次清零不计入“已知同值写入”。
### Fallback 原因
查询 key/首匹配逻辑条目不同、已消费字段或已测试 valid 位不同、容量不足转 scratch、observer 非空、pipe 命中分支改变,或无法建立无冲突的 slot 映射时,执行原 operation。此清单不授权放宽现有 guard。
## Jacobian 200,group 6,position 16
t=0.37285444606381923;count 13 → 15;capacity=256;输出 `-3.34452871903457e-06`;model evaluator 返回 `1`。
### 查询与首次匹配
| 顺序 | 类型 | 完整 key:p, T 或 h, R, cp, Tref, slope, mu, muT, S | medium kind | 首个匹配 slot | 扫描条数 |
|---|---|---|---|---|---|
| 1 | PT | `15019678.822746754, 291.84964142606623, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | miss | 13 |
| 2 | PT | `15019662.638804033, 291.84951601402554, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | miss | 14 |
### 实际选中后的字段读取
| 域 / slot | 字段 |
|---|---|
| pipes[0] | `valid` |
| states[13] | `T`, `isentropic_exponent`, `isentropic_factor`, `jacobian`, `medium`, `medium.R`, `medium.S`, `medium.Tref`, `medium.cp`, `medium.mu`, `medium.muT`, `medium.real_helium`, `medium.slope`, `mu`, `p`, `rho`, `temperatures` |
| states[14] | `T`, `isentropic_exponent`, `isentropic_factor`, `jacobian`, `medium.R`, `medium.S`, `medium.Tref`, `medium.cp`, `medium.mu`, `medium.muT`, `medium.real_helium`, `medium.slope`, `p`, `rho`, `temperatures` |
查询扫描另外按短路次序读取 `valid & PT/H`、`p`、`T/h`、medium;未匹配项的字段不等于被用于物性计算。
### 选中后的 valid 测试
| 顺序 | slot | mask | 结果 |
|---|---|---|---|
| 1 | 13 | 8 | 0 |
| 2 | 13 | 16 | 0 |
| 3 | 13 | 4 | 0 |
| 4 | 14 | 16 | 0 |
| 5 | 14 | 4 | 0 |
| 6 | 13 | 4 | 4 |
### 必须保留的 probe 数据
保留入口 `states[0:13]` 的全部字段;此路径没有写已有物性条目。仅追加以下新条目,修改本 operation 的 pipe 槽;其余 pipe 槽保持 probe 值。
### 有序写入动作
| 顺序 | 目标 | 写入值 | 已知同值写入 |
|---|---|---|---|
| 1 | `context[0].count` | 14 | — |
| 2 | `states[13].*` | 完整 struct 清零(显式写入) | — |
| 3 | `states[13].medium` | 复制上述完整 medium | — |
| 4 | `states[13].p` | 15019678.822746754 | — |
| 5 | `states[13].T` | 291.84964142606623 | — |
| 6 | `states[13].valid` | 1 | — |
| 7 | `states[13].temperatures` | NULL | 是 |
| 8 | `states[13].jacobian` | 当前 context 的 Jacobian memo 指针 | — |
| 9 | `states[13].mu` | 1.9556955415450165e-05 | — |
| 10 | `states[13].valid` | 9 | — |
| 11 | `states[13].rho` | 23.797175883728954 | — |
| 12 | `states[13].valid` | 13 | — |
| 13 | `states[13].isentropic_factor` | 0.5638664372147334 | — |
| 14 | `states[13].isentropic_exponent` | 0.39880100414729064 | — |
| 15 | `states[13].valid` | 29 | — |
| 16 | `context[0].count` | 15 | — |
| 17 | `states[14].*` | 完整 struct 清零(显式写入) | — |
| 18 | `states[14].medium` | 复制上述完整 medium | — |
| 19 | `states[14].p` | 15019662.638804033 | — |
| 20 | `states[14].T` | 291.84951601402554 | — |
| 21 | `states[14].valid` | 1 | — |
| 22 | `states[14].temperatures` | NULL | 是 |
| 23 | `states[14].jacobian` | 当前 context 的 Jacobian memo 指针 | — |
| 24 | `states[14].rho` | 23.797161425154815 | — |
| 25 | `states[14].valid` | 5 | — |
| 26 | `states[14].isentropic_factor` | 0.56386646597889145 | — |
| 27 | `states[14].isentropic_exponent` | 0.39880100666377111 | — |
| 28 | `states[14].valid` | 21 | — |
| 29 | `pipes[0].valid` | 0 | 是 |
| 30 | `pipes[0].medium` | 复制上述完整 medium | — |
| 31 | `pipes[0].p1` | 15019662.638804033 | — |
| 32 | `pipes[0].p2` | 15019678.822746754 | — |
| 33 | `pipes[0].T` | 291.84964142606623 | — |
| 34 | `pipes[0].diameter` | 0.014 | — |
| 35 | `pipes[0].length` | 1 | — |
| 36 | `pipes[0].roughness` | 0.0032142857142857142 | — |
| 37 | `pipes[0].kind` | 1 | — |
| 38 | `pipes[0].flow` | -3.34452871903457e-06 | — |
| 39 | `pipes[0].valid` | 1 | — |
新条目分配分支均为 positive/finite key 且 `count < capacity`;每次在当前 count 追加,再 count++。未初始化槽的 struct 首次清零不计入“已知同值写入”。
### Fallback 原因
查询 key/首匹配逻辑条目不同、已消费字段或已测试 valid 位不同、容量不足转 scratch、observer 非空、pipe 命中分支改变,或无法建立无冲突的 slot 映射时,执行原 operation。此清单不授权放宽现有 guard。
## Jacobian 200,group 18,position 52
t=0.37285444606381923;count 75 → 77;capacity=256;输出 `-1.4080633385291488e-06`;model evaluator 返回 `1`。
### 查询与首次匹配
| 顺序 | 类型 | 完整 key:p, T 或 h, R, cp, Tref, slope, mu, muT, S | medium kind | 首个匹配 slot | 扫描条数 |
|---|---|---|---|---|---|
| 1 | PT | `15019625.056587901, 529.09774891777454, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | miss | 75 |
| 2 | PT | `15019613.376778807, 529.09758439456209, 2077.2643940499802, 5193.1609851249505, 293.14999999999998, 0, 1.9599999999999999e-05, 293.14999999999998, 79.400000000000006` | 1 | miss | 76 |
### 实际选中后的字段读取
| 域 / slot | 字段 |
|---|---|
| pipes[28] | `valid` |
| states[75] | `T`, `isentropic_exponent`, `isentropic_factor`, `jacobian`, `medium`, `medium.R`, `medium.S`, `medium.Tref`, `medium.cp`, `medium.mu`, `medium.muT`, `medium.real_helium`, `medium.slope`, `mu`, `p`, `rho`, `temperatures` |
| states[76] | `T`, `isentropic_exponent`, `isentropic_factor`, `jacobian`, `medium.R`, `medium.S`, `medium.Tref`, `medium.cp`, `medium.mu`, `medium.muT`, `medium.real_helium`, `medium.slope`, `p`, `rho`, `temperatures` |
查询扫描另外按短路次序读取 `valid & PT/H`、`p`、`T/h`、medium;未匹配项的字段不等于被用于物性计算。
### 选中后的 valid 测试
| 顺序 | slot | mask | 结果 |
|---|---|---|---|
| 1 | 75 | 8 | 0 |
| 2 | 75 | 16 | 0 |
| 3 | 75 | 4 | 0 |
| 4 | 76 | 16 | 0 |
| 5 | 76 | 4 | 0 |
| 6 | 75 | 4 | 4 |
### 必须保留的 probe 数据
保留入口 `states[0:75]` 的全部字段;此路径没有写已有物性条目。仅追加以下新条目,修改本 operation 的 pipe 槽;其余 pipe 槽保持 probe 值。
### 有序写入动作
| 顺序 | 目标 | 写入值 | 已知同值写入 |
|---|---|---|---|
| 1 | `context[0].count` | 76 | — |
| 2 | `states[75].*` | 完整 struct 清零(显式写入) | — |
| 3 | `states[75].medium` | 复制上述完整 medium | — |
| 4 | `states[75].p` | 15019625.056587901 | — |
| 5 | `states[75].T` | 529.09774891777454 | — |
| 6 | `states[75].valid` | 1 | — |
| 7 | `states[75].temperatures` | NULL | 是 |
| 8 | `states[75].jacobian` | 当前 context 的 Jacobian memo 指针 | — |
| 9 | `states[75].mu` | 2.9451615911502958e-05 | — |
| 10 | `states[75].valid` | 9 | — |
| 11 | `states[75].rho` | 13.352686365896215 | — |
| 12 | `states[75].valid` | 13 | — |
| 13 | `states[75].isentropic_factor` | 0.58140881158784896 | — |
| 14 | `states[75].isentropic_exponent` | 0.39986603165187307 | — |
| 15 | `states[75].valid` | 29 | — |
| 16 | `context[0].count` | 77 | — |
| 17 | `states[76].*` | 完整 struct 清零(显式写入) | — |
| 18 | `states[76].medium` | 复制上述完整 medium | — |
| 19 | `states[76].p` | 15019613.376778807 | — |
| 20 | `states[76].T` | 529.09758439456209 | — |
| 21 | `states[76].valid` | 1 | — |
| 22 | `states[76].temperatures` | NULL | 是 |
| 23 | `states[76].jacobian` | 当前 context 的 Jacobian memo 指针 | — |
| 24 | `states[76].rho` | 13.35268032881546 | — |
| 25 | `states[76].valid` | 5 | — |
| 26 | `states[76].isentropic_factor` | 0.58140882193020493 | — |
| 27 | `states[76].isentropic_exponent` | 0.39986603223234823 | — |
| 28 | `states[76].valid` | 21 | — |
| 29 | `pipes[28].valid` | 0 | 是 |
| 30 | `pipes[28].medium` | 复制上述完整 medium | — |
| 31 | `pipes[28].p1` | 15019613.376778807 | — |
| 32 | `pipes[28].p2` | 15019625.056587901 | — |
| 33 | `pipes[28].T` | 529.09774891777454 | — |
| 34 | `pipes[28].diameter` | 0.02 | — |
| 35 | `pipes[28].length` | 1 | — |
| 36 | `pipes[28].roughness` | 0.0022499999999999998 | — |
| 37 | `pipes[28].kind` | 1 | — |
| 38 | `pipes[28].flow` | -1.4080633385291488e-06 | — |
| 39 | `pipes[28].valid` | 1 | — |
新条目分配分支均为 positive/finite key 且 `count < capacity`;每次在当前 count 追加,再 count++。未初始化槽的 struct 首次清零不计入“已知同值写入”。
### Fallback 原因
查询 key/首匹配逻辑条目不同、已消费字段或已测试 valid 位不同、容量不足转 scratch、observer 非空、pipe 命中分支改变,或无法建立无冲突的 slot 映射时,执行原 operation。此清单不授权放宽现有 guard。
+69
View File
@@ -0,0 +1,69 @@
/* Access events are primary evidence; snapshots are only the replay oracle. */
#include "context_access_diag.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
static FILE *ax_file;
static NativePropertyCache *ax_cache;
static NativePipeCache *ax_pipes;
static int ax_active,ax_pending;
static unsigned long long ax_id;
static void hex(const void *p,size_t n){const unsigned char *b=p;for(size_t i=0;i<n;i++)fprintf(ax_file,"%02x",b[i]);}
static void blob(const char *name,const void *p,size_t n){fprintf(ax_file,"\"%s\":\"",name);hex(p,n);fputc('"',ax_file);}
static void snapshot(const char *phase){
fprintf(ax_file,"{\"event\":\"snapshot\",\"phase\":\"%s\",\"count\":%llu,",phase,(unsigned long long)ax_cache->count);
blob("context",ax_cache,sizeof(*ax_cache));fputc(',',ax_file);
blob("states",ax_cache->states,ax_cache->count*sizeof(*ax_cache->states));fputc(',',ax_file);
blob("pipes",ax_pipes,40*sizeof(*ax_pipes));fputs("}\n",ax_file);
}
static int locate(const void *ptr,size_t n,const char **domain,size_t *offset){
uintptr_t p=(uintptr_t)ptr;
const void *bases[]={ax_cache,ax_cache?ax_cache->states:NULL,ax_pipes};
size_t sizes[]={sizeof(*ax_cache),ax_cache?ax_cache->capacity*sizeof(*ax_cache->states):0,40*sizeof(*ax_pipes)};
const char *names[]={"context","states","pipes"};
for(int i=0;i<3;i++)if(bases[i] && p>=(uintptr_t)bases[i] && p-(uintptr_t)bases[i]+n<=sizes[i]){
*domain=names[i];*offset=p-(uintptr_t)bases[i];return 1;
}
return 0;
}
void ax_access(const char *action,const void *p,size_t n,const char *field,const char *fn){
if(!ax_active){return;}const char *domain;size_t offset;
if(!locate(p,n,&domain,&offset))return;
fprintf(ax_file,"{\"event\":\"%s\",\"domain\":\"%s\",\"offset\":%llu,\"field\":\"%s\",\"function\":\"%s\",",action,domain,(unsigned long long)offset,field,fn);
blob("value",p,n);fputs("}\n",ax_file);
}
void ax_bind(NativePropertyCache *p,NativePipeCache *pipes){ax_cache=p;ax_pipes=pipes;}
void ax_begin(unsigned long long jac,int group,int pos,double t,const double *inputs,int n){
ax_active=(pos==52 && (group==-1 || group==18)) || (pos==16 && (group==-1 || group==6));
if(!ax_active){return;}
if(!ax_file){ax_file=fopen("access.jsonl","wb");if(!ax_file)abort();setvbuf(ax_file,NULL,_IOFBF,1024*1024);}
ax_pending=1;
fprintf(ax_file,"{\"event\":\"begin\",\"id\":%llu,\"jac\":%llu,\"group\":%d,\"position\":%d,\"t\":%.17g,\"stateSize\":%llu,\"pipeSize\":%llu,",++ax_id,jac,group,pos,t,(unsigned long long)sizeof(NativePropertyState),(unsigned long long)sizeof(NativePipeCache));
blob("inputs",inputs,n*sizeof(double));fputs("}\n",ax_file);snapshot("entry");
}
void ax_end(const double *outputs,int n){if(!ax_active){return;}snapshot("exit");fputs("{\"event\":\"end\",\"completed\":true,",ax_file);blob("outputs",outputs,n*sizeof(double));fputs("}\n",ax_file);ax_active=0;}
void ax_result(int result){if(ax_pending){fprintf(ax_file,"{\"event\":\"eval_return\",\"result\":%d}\n",result);ax_pending=0;}}
void ax_finish(void){if(ax_file){fclose(ax_file);ax_file=NULL;}}
void ax_query(const char *kind,const NativeMedium *m,double p,double second){
if(!ax_active){return;}double key[]={p,second,m->R,m->cp,m->Tref,m->slope,m->mu,m->muT,m->S};
fprintf(ax_file,"{\"event\":\"query\",\"kind\":\"%s\",\"mediumKind\":%d,\"count\":%llu,",kind,m->real_helium,(unsigned long long)ax_cache->count);
blob("key",key,sizeof(key));fputs("}\n",ax_file);
}
void ax_match(const char *kind,NativePropertyState *s){if(!ax_active){return;}fprintf(ax_file,"{\"event\":\"match\",\"kind\":\"%s\",\"hit\":%s,\"slot\":%lld}\n",kind,s?"true":"false",s?(long long)(s-ax_cache->states):-1LL);}
void ax_new(NativePropertyCache *cache,NativePropertyState *s,int valid){
if(!ax_active){return;}const char *domain;size_t offset;int stored=locate(s,sizeof(*s),&domain,&offset) && !strcmp(domain,"states");
fprintf(ax_file,"{\"event\":\"allocate\",\"validInput\":%s,\"slot\":%lld,\"countAfter\":%llu,\"capacity\":%llu,\"branch\":\"%s\"}\n",valid?"true":"false",stored?(long long)(offset/sizeof(*s)):-1LL,(unsigned long long)(cache?cache->count:0),(unsigned long long)(cache?cache->capacity:0),stored?"append":"scratch");
}
unsigned ax_test(NativePropertyState *s,unsigned mask,const char *fn){
unsigned value=s->valid&mask;
if(ax_active){
const char *domain;size_t offset;
int stored=locate(s,sizeof(*s),&domain,&offset) && !strcmp(domain,"states");
fprintf(ax_file,"{\"event\":\"valid_test\",\"slot\":%lld,\"mask\":%u,\"value\":%u,\"function\":\"%s\"}\n",stored?(long long)(offset/sizeof(*s)):-1LL,mask,value,fn);
}
return value;
}
void ax_scalar(const char *action,NativeJacobianScalars *cache,int kind,int medium,const double *inputs,size_t n,int hit,const double *value){
if(!ax_active){return;}fprintf(ax_file,"{\"event\":\"scalar_%s\",\"kind\":%d,\"mediumKind\":%d,\"hit\":%d,\"recording\":%d,\"capacity\":%llu,",action,kind,medium,hit,cache?cache->recording:-1,(unsigned long long)(cache?cache->capacity:0));
blob("key",inputs,n*sizeof(double));if(value){fputc(',',ax_file);blob("value",value,sizeof(double));}fputs("}\n",ax_file);
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef CONTEXT_ACCESS_DIAG_H
#define CONTEXT_ACCESS_DIAG_H
#include "kernels.h"
#include <string.h>
/* GCC-only diagnostic worker: each expression is evaluated once. */
void ax_access(const char *,const void *,size_t,const char *,const char *);
void ax_bind(NativePropertyCache *,NativePipeCache *);
void ax_begin(unsigned long long,int,int,double,const double *,int);
void ax_end(const double *,int);
void ax_result(int);
void ax_finish(void);
void ax_query(const char *,const NativeMedium *,double,double);
void ax_match(const char *,NativePropertyState *);
void ax_new(NativePropertyCache *,NativePropertyState *,int);
unsigned ax_test(NativePropertyState *,unsigned,const char *);
void ax_scalar(const char *,NativeJacobianScalars *,int,int,const double *,size_t,int,const double *);
#define AX_R(x) ({ __typeof__(x) ax_v=(x); ax_access("read",&(x),sizeof(x),#x,__func__); ax_v; })
#define AX_W(x,v) do { (x)=(v); ax_access("write",&(x),sizeof(x),#x,__func__); } while(0)
#define AX_OR(x,v) do { (x)|=(v); ax_access("write",&(x),sizeof(x),#x,__func__); } while(0)
#define AX_INC(x) ({ __typeof__(x) ax_v=(x)++; ax_access("write",&(x),sizeof(x),#x,__func__); ax_v; })
#endif
+169
View File
@@ -0,0 +1,169 @@
# 两个 operation 的 context 访问级诊断
## 结论
两个案例均能按“查询 key 和首次匹配关系 → 消费字段 → 有序更新与追加”建立局部清单。完整仿真中,每例各验证 896 对 baseline/probe:查询 key、命中状态、逻辑条目对应后的消费值、valid 测试及有序写入一致,operation 输出逐位一致。
- **group 18 / R475 / position 52**:Jacobian 200 中 `states[74].p` 确实被两次 PT 查询扫描读取,但两次都在压力比较处排除。它没有成为物性计算输入;`states[74].T/h/rho/isentropic` 均未被该 operation 读取。因此,**本次入口差异不影响查询选择和计算**,不能表述为“这个字段完全没被读取”,也不能无条件忽略任意压力变化。
- **group 6 / R288 / position 16**:Jacobian 200 的入口 count 从 baseline 12 变为 probe 13。两次 PT 查询均 miss,逻辑新条目应从 baseline 12/13 映射到 probe 13/14;退出 count 应为 15。probe 原有 slot 12 必须保留。
这是独立诊断 worker 的验证结果。生产求解器、现有 local-probe guard 和整段回放策略没有修改;不能据此直接允许整个 R475 复用。
具体数值、每次查询的完整 key、valid 测试和逐项有序写入见 [实测清单](context_access_checklists.md)。以下解释这些清单如何用于后续设计。
## 验证范围与方法
输入 `tests/data/test-mql-8-corrected.json`,BDF,0–10 s,采样间隔 0.01 s,rtol=1e-8。位置、group、Jacobian 序号均采用已有诊断的 0 起始编号。Windows / GCC 8.1.0;本次没有执行 Linux 验收。
独立 worker 来源为 `test/context-fallback-20260917/worker`。生成时,仅在副本内包装实际字段读取、valid 位测试、赋值、结构体清零、count++、查询和分配点,保留原表达式、短路条件和原 guard。Jacobian scalar get 经输出指针写入 `rho` 的路径也单独记录。
原始事件带有执行顺序、函数、context 域、字节偏移及原始位值。PT/PH 查询记录 medium kind、全部七个 medium 浮点参数、p 和 T/h;校验器独立扫描当前重放状态,检查所有匹配项、首个匹配 slot,以及实际扫描到的 valid 测试序列。miss 后的 `allocate` 事件给出所选追加 slot。
观察器和 memo 指针还校验了当前 context 的归属,以及同一 Jacobian 内 baseline/probe 的绑定一致性,不只是比较“是否非空”。日志中的原始地址仅用于本次进程诊断,不作为跨进程、跨机器的逻辑 key。
**前后快照仅作校验终点**:逐条读取须与当时 context 一致;逐条写入须重建退出 context 的每个有效字节,包括未改动旧条目和其余 pipe 槽。另独立检查原代码规定的新条目初始化顺序及 pipe 的 11 次写入,因而删除 `pipe.valid=0` 这类同值写入也会验收失败。未初始化槽清零前的内容未知,不把它计作“已知同值写入”。
`operations.json` 中的 `consumed` 是选择完成后的实际字段读取集合;查询扫描的 `p/T/h/medium` 和 valid 测试在原始事件中单独保留。新条目内部的计算读取也在集合内,但这些值不是需要和入口旧条目比较的前置条件。
## 案例一:group 18,R475 内 position 52
operation 为 `flow:amesim_pnl0001_17.port_1`,输出 `q[152]`,使用 `pipe_cache[28]`。
### 查询及必要前置条件
Jacobian 200,t≈0.372854446 s,入口 count=75,capacity=256。
| 项目 | baseline | probe |
|---|---:|---:|
| states[74].p | 15019640.74937454 | 15019641.148960622 |
| states[74].T | 460.10768147887495 | 460.1076863719882 |
| states[74].valid | 21 | 21 |
| 上游 PT key 的 p | 15019625.056587901 | 相同 |
| 上游 PT key 的 T | 529.0977489177745 | 相同 |
| 下游 PT key 的 p | 15019613.376778807 | 相同 |
| 下游 PT key 的 T | 529.0975843945621 | 相同 |
完整 medium key 为 `(real_helium=1, R=2077.26439404998, cp=5193.1609851249505, Tref=293.15, slope=0, mu=1.96e-5, muT=293.15, S=79.4)`。实际二进制位值保存在事件和清单来源 JSON 中,不依赖上表小数显示精度。
两次 PT 都 miss,分别扫描 75、76 项,追加到 slot 75、76。slot 74 的两次访问均为 `valid & 1 → p == query.p`;压力不等后短路,未读取其 T 或 medium。满足同样排除关系即可允许这项差异,不要求两次入口的 slot 74 压力相等。
此时 `p[44] <= g[40].p`:温度来自 `g[40].T`,没有 PH 查询,`h[178]` 没有被本次分支消费。需要保持压力方向、近零流量分支、实际温度来源、medium 和管道常量一致。
### 实际读取 → 必须保留的 probe 数据
- 上游新条目:p、T、medium、mu、rho、isentropic_factor、isentropic_exponent,以及温度观察器/Jacobian memo 指针。
- 下游新条目:p、T、medium、rho、两个 isentropic 字段和上述指针;没有消费下游 mu/h。
- 选中后的 valid 测试依次为:上游 MU=0、ISENTROPIC=0、RHO=0;下游 ISENTROPIC=0、RHO=0;再次上游 RHO=4。
- `pipe_cache[28]` 入口只读 valid=0,其余旧键字段被短路跳过。
- 保留 probe 的 `states[0:75]` 全部内容,尤其是 slot 74 的新 p/T;其他 39 个 pipe 槽保持 probe 值。这条实测路径没有写任何已有物性条目。
### 有序字段更新与追加动作
1. count 75→76;在 slot 75 整体清零,依次写 medium、p、T、valid=PT、temperatures、jacobian。
2. 写 mu,`valid |= MU`;写 rho,`valid |= RHO`;写 factor、exponent,`valid |= ISENTROPIC`。上游最终 valid=29。
3. count 76→77;同样初始化 slot 76;写 rho、RHO 位、factor、exponent、ISENTROPIC 位。下游最终 valid=21,未写 h/mu 的计算值,但初始化清零必须执行。
4. pipe[28] 按顺序写 valid=0、medium、p1、p2、T、diameter、length、roughness、kind、flow、valid=1。首次 valid=0 是真实的 0→0 写入。
5. 输出 `q[152]=-1.4080633385291488e-6`;model evaluator 返回 1。
### 不能省略的其他实测分支
| position 52 查询序列 | probe 次数 | 代表 Jacobian |
|---|---:|---:|
| PT miss → PT miss | 727 | 200 |
| PH miss → PT miss(登记 h)→ PT hit → PT miss | 167 | 2 |
| PH hit → PT hit → PT miss | 1 | 1 |
| 无物性查询,近零流量 | 1 | 0 |
PH miss 路径需要测试 H 位并显式写 h、`valid |= H`,随后 pipe 的 PT 查询复用这个刚创建的逻辑条目;不能把后一次 hit 另当一次追加。PH hit 路径实际消费已有条目的 T,随后读取其已有效的 mu/rho/isentropic 字段;这些入口载荷必须比较。Jacobian 1 中首次匹配为 slot 7,仅新增下游条目。两条补充分支的完整 key 和写入顺序已收入实测清单。
全程有 24 对入口 count 不同,偏移为 +1 或 +2;其查询和逻辑字段仍一致。位置 52 也需要动态 slot 映射,不能固定为 75/76。
## 案例二:group 6,R288 / position 16
operation 为 `flow:amesim_pnl0001_1.port_1`,输出 `q[45]`,使用 `pipe_cache[0]`。
### 查询及必要前置条件
Jacobian 200 的 medium 与上例相同:
| 逻辑条目 / 查询顺序 | p | T | baseline slot | probe slot |
|---|---:|---:|---:|---:|
| 上游 U | 15019678.822746754 | 291.8496414260662 | miss → 12 | miss → 13 |
| 下游 D | 15019662.638804033 | 291.84951601402554 | miss → 13 | miss → 14 |
baseline 扫描 12、13 项;probe 扫描 13、14 项。probe 增加的入口项都不匹配当前查询,且两次追加时均有容量。**相同逻辑条目由查询 key 和创建次序定义,不能用“相同数组下标”定义。**
此时 `p[3] <= g[4].p`,使用 `g[4].T`,未消费 `h[64]`。需要比较实际压力/温度输入、medium/管道常量,以及查询和分支前置条件。
### 实际读取 → 必须保留的 probe 数据
U、D 的消费字段及 valid 测试与上例的两次 PT miss 路径一致,只需映射到 probe slot 13/14。pipe[0] 只消费入口 valid=0。
**保留 probe `states[0:13]` 的每个字段,包括 slot 12,以及其他全部 pipe 槽。** 本 operation 没有对已有物性条目的写入。入口 count 不需要等于 baseline,但它决定下一次追加位置,不能忽略其作用。
### 有序字段更新与追加动作
1. 在 probe 当前尾部追加 U:count 13→14,slot 13 完整初始化,写 mu/rho/factor/exponent 和对应 valid 位,最终 valid=29。
2. 重新按更新后的 probe context 查询 D;miss 后 count 14→15,slot 14 初始化,写 rho/factor/exponent 和对应 valid 位,最终 valid=21。
3. pipe[0] 执行同样的 11 次有序写入,包括同值 valid=0。
4. `q[45]=-3.34452871903457e-6`,model evaluator 返回 1。
把 baseline slot 12/13 的退出快照直接覆盖到 probe 会损坏原 slot 12;把 count 恢复为 baseline 14 会丢失 probe 的有效尾项。正确动作是“保留 probe 前缀,在 probe 当前 count 顺序追加”。
896 对中,638 对存在 count 差异,最大偏移 +4;829 对走两次 PT miss,67 对走近零流量路径且不增加 count。全部能建立一致的逻辑映射。
## 比较、保留、写回与 fallback 的边界
| 入口差异 / 动作 | 本次清单要求 |
|---|---|
| 未选中的条目 p/T/h/medium 不同 | 可允许数值不同,但必须验证仍被当前查询排除,不改变首次匹配关系 |
| count / slot 平移 | 可允许;用当前 probe count 检查容量,按 key/首次命中和创建顺序建立映射 |
| 选中已有条目的有效载荷不同 | 必须比较本分支实际消费的 T、mu、rho、factor、exponent 等;不一致则 fallback |
| valid 位不同 | 比较实际测试的掩码结果;未知分支不可放行。`valid |= flag` 应保留其余 probe 位,不能泛化为整字覆盖 |
| 入口未被读取的 h 或 pipe 旧键值不同 | 本分支可以忽略比较;仍须保留未被实际写入的 probe 数据 |
| 新条目 | 当前尾部追加;完整初始化再按事件次序赋值,同值写入也不能删除 |
| 已有条目更新 | 两例的完整运行未观察到。插桩已覆盖相关赋值,但不能声称已实测该分支;若后续出现,需要保留其原有有效字段、按映射定点更新 |
| 容量不足、无效 key、scratch 路径 | 本次未出现。清单要求 fallback,不能套用“必定追加两项” |
| 非空 temperature observer | 本次均为 NULL;非空会有外部观察器副作用,未验证,fallback |
| pipe 入口命中分支改变 | 本次目标槽均 valid=0;若变为有效需重新核验完整 pipe key、flow 载荷和分支,不能沿用本清单 |
| 首匹配不同 / 重复 key 改变匹配先后 | fallback;单纯“某处有同 key”不够 |
Jacobian scalar memo 是另一个需要保留的作用域:其 key、get 命中、put 尝试、recording/capacity 已记录。Jacobian 200 baseline 的两次 density 和一次 pipe scalar get 为 miss,probe 均 hit;返回数值一致。此处不能简单回放 baseline 的 memo 写入或统计副作用。当前诊断不修改该 memo;后续局部复用必须维持其生命周期和统计语义。
operation 本身是一次 double 赋值,没有独立 int 状态码。记录的是实际输出/pipe flow,以及包围它的 model evaluator 返回值;没有虚构 operation 成功码。
## 验收结果
- 3,584 次 operation(2 案例 × baseline/probe × 896),全部访问校验和退出 context 重放一致。
- 1,382,612 次字段/基址读取逐条核对通过;valid 掩码测试另外记录并校验。
- 每例 896 对的完整查询 key、hit/miss、首次匹配关系经过独立验证;按逻辑映射后的消费字段集合、valid 测试及有序写入一致。
- 136,606 次显式写入,其中 **10,614 次已知同值写入**;检查包括 pipe valid 的 0→0,未把未初始化槽的清零算作已知同值写入。
- 状态、输出、事件及所有 896 个 132×132 Jacobian 的 SHA-256 与已有未插桩基线一致;矩阵元素共 15,611,904 个。
- accepted=10,840,rejected=918,nfev=44,467,njev=896,nlu=3,106;Newton iterations=19,371,Newton convergence failures=798,均与基线一致。
- 2,688 次包含目标 operation 的 model evaluator 调用均返回 1;baseline 每次含两个目标 operation,所以该数小于 operation 数量。
- 证据校验器另有 6 个负例:遗漏同值写入、rho 读取错误、追加使用 baseline slot、恢复 baseline count、错误首匹配、错误 valid 位结果。它们是对证据校验器的反例测试,**不是已实现的生产 fallback 测试**。
插桩会引入大量日志开销,本次时长不用于评价优化收益。容量耗尽、scratch、非空 observer、已有物性条目更新及 pipe 命中分支未在这两个案例内实测,仍是未来实现时的明确边界。
## 复现与交付文件
需先保留已有 local-probe、context-fallback worker 及其 baseline 验证文件。它们的生成入口分别是 `local_probe_experiment.py`、`diagnose_context_fallback.py`;本次脚本会对缺失来源报错,不生成替代基线。
```powershell
.venv-win\Scripts\python.exe tests/manual/diagnose_context_access.py prepare
.venv-win\Scripts\python.exe tests/manual/diagnose_context_access.py run
.venv-win\Scripts\python.exe tests/manual/analyze_context_access.py
.venv-win\Scripts\python.exe tests/manual/verify_context_access_evidence.py
```
所有路径由脚本所在项目目录推导。原始证据和生成 worker 位于 `test/context-access-20260917/`,属于被 Git 忽略的运行产物:
- `worker/build.json`:来源及插桩源码 SHA-256。
- `audit/access.jsonl`:逐次实际查询、读取、测试、写入、分配、输出和返回事件。
- `audit/measurement.json`:全量仿真数值一致性哈希。
- `summary.json`:逐 Jacobian 的两路径对应关系。
- `operations.json`:全部 3,584 次 operation 的查询、消费字段、valid 测试、有序更新与追加。
- `jacobian-200.json`:两个主案例的原始事件和结构化清单。
- `negative-checks.json`:六项证据负例结果。
本报告及 [实测数值清单](context_access_checklists.md) 保存在 `tests/manual/`,可与诊断脚本一起提交。
+213
View File
@@ -0,0 +1,213 @@
/* Included after the unchanged local_probe_support.c, so baseline snapshots
are observed directly. None of the original guard or restore code is edited. */
#include "context_fallback_diag.h"
#include <stddef.h>
/* DIAG_TABLES */
int dx_mode,dx_selected,dx_region=-1,dx_position=-1,dx_shadow;
static uint64_t dx_jac,dx_sampled,dx_tick0,dx_q0,dx_region_tick,dx_op_tick;
static unsigned dx_stride=16,dx_seed=1,dx_pick;
static double dx_time;
typedef struct {
uint64_t attempts,compares,failures,region_ticks,op_ticks,ops;
uint64_t input_diff_ops,output_diff_ops,exit_output_diff,exit_context_diff;
} DxRegion;
static DxRegion dx_stats[LP_NC][LP_NR];
static uint64_t dx_op_count[LP_NC][LP_NO],dx_op_ticks[LP_NC][LP_NO];
static uint64_t dx_kernel_count[LP_NR][DX_NK],dx_kernel_inclusive[LP_NR][DX_NK],dx_kernel_exclusive[LP_NR][DX_NK];
static double dx_inputs[DX_NIN],dx_outputs[DX_NOUT];
static FILE *dx_fail_file,*dx_matrix_file;
static FILE *dx_input_file;
static FILE *dx_create_file;
static int dx_trace,dx_details,dx_first_context=-999,dx_seen[LP_NB],dx_source[LP_NB];
static Snapshot dx_history[LP_NB];
static struct {uint64_t start,child;int kind,region;} dx_stack[64];
static int dx_depth;
typedef struct {const char *name;size_t offset,size;int floating;} DxField;
#define DX_FIELD(type,field,float_flag) {#field,offsetof(type,field),sizeof(((type*)0)->field),float_flag}
#define DX_MEDIUM(type,field,float_flag) {"medium." #field,offsetof(type,medium)+offsetof(NativeMedium,field),sizeof(((NativeMedium*)0)->field),float_flag}
#define DX_MEDIUM_FIELDS(type) DX_MEDIUM(type,real_helium,0),DX_MEDIUM(type,R,1),DX_MEDIUM(type,cp,1),DX_MEDIUM(type,Tref,1),DX_MEDIUM(type,slope,1),DX_MEDIUM(type,mu,1),DX_MEDIUM(type,muT,1),DX_MEDIUM(type,S,1)
static const DxField dx_state_fields[]={DX_MEDIUM_FIELDS(NativePropertyState),
DX_FIELD(NativePropertyState,p,1),DX_FIELD(NativePropertyState,T,1),DX_FIELD(NativePropertyState,h,1),
DX_FIELD(NativePropertyState,rho,1),DX_FIELD(NativePropertyState,mu,1),DX_FIELD(NativePropertyState,isentropic_factor,1),
DX_FIELD(NativePropertyState,isentropic_exponent,1),DX_FIELD(NativePropertyState,valid,0),
DX_FIELD(NativePropertyState,temperatures,0),DX_FIELD(NativePropertyState,jacobian,0)};
static const DxField dx_pipe_fields[]={DX_MEDIUM_FIELDS(NativePipeCache),
DX_FIELD(NativePipeCache,p1,1),DX_FIELD(NativePipeCache,p2,1),DX_FIELD(NativePipeCache,T,1),
DX_FIELD(NativePipeCache,diameter,1),DX_FIELD(NativePipeCache,length,1),DX_FIELD(NativePipeCache,roughness,1),
DX_FIELD(NativePipeCache,flow,1),DX_FIELD(NativePipeCache,kind,0),DX_FIELD(NativePipeCache,valid,0)};
typedef struct {int kind,index,field;size_t offset,size;uint64_t baseline,trial;} DxDifference;
static DxDifference dx_difference(NativePropertyCache*,NativePipeCache*,Snapshot*);
static void dx_boundary(int pos,int source,NativePropertyCache *p,NativePipeCache *pipes){
if(!dx_trace || lp_color<0)return;
int v=dx_version[pos];if(dx_seen[v])return;
Snapshot *s=&dx_history[v];memset(s,0,sizeof(*s));
s->count=p->count;s->capacity=p->capacity;s->temperatures=p->temperatures;s->jacobian=p->jacobian;
memcpy(s->states,p->states,p->count*sizeof(*p->states));memcpy(s->pipes,pipes,sizeof(s->pipes));
dx_seen[v]=1;dx_source[v]=source;
if(dx_first_context==-999 && dx_difference(p,pipes,&saved->snapshots[v]).kind)dx_first_context=source;
}
static int dx_field_equal(Snapshot *a,Snapshot *b,DxDifference d){
if(d.kind==1){uint64_t x[]={a->count,a->capacity,(uintptr_t)a->temperatures,(uintptr_t)a->jacobian};uint64_t y[]={b->count,b->capacity,(uintptr_t)b->temperatures,(uintptr_t)b->jacobian};return x[d.field]==y[d.field];}
if(d.kind==2 && ((size_t)d.index>=a->count || (size_t)d.index>=b->count))return (size_t)d.index>=a->count && (size_t)d.index>=b->count;
unsigned char *x=(unsigned char*)(d.kind==2?(void*)&a->states[d.index]:(void*)&a->pipes[d.index]);
unsigned char *y=(unsigned char*)(d.kind==2?(void*)&b->states[d.index]:(void*)&b->pipes[d.index]);
return memcmp(x+d.offset,y+d.offset,d.size)==0;
}
static int dx_origin(DxDifference d,int v,int *first){
int previous_equal=1,origin=-999;*first=-999;
for(int i=0;i<=v;i++)if(dx_seen[i]){
int equal=dx_field_equal(&dx_history[i],&saved->snapshots[i],d);
if(!equal && previous_equal){origin=dx_source[i];if(*first==-999)*first=origin;}
previous_equal=equal;
}
return origin;
}
static DxDifference dx_difference(NativePropertyCache *p,NativePipeCache *pipes,Snapshot *b){
DxDifference d={0,-1,-1,0,0,0,0};
uint64_t bm[]={b->count,b->capacity,(uintptr_t)b->temperatures,(uintptr_t)b->jacobian};
uint64_t pm[]={p->count,p->capacity,(uintptr_t)p->temperatures,(uintptr_t)p->jacobian};
for(int i=0;i<4;i++)if(bm[i]!=pm[i]){d.kind=1;d.field=i;d.baseline=bm[i];d.trial=pm[i];return d;}
for(int kind=2;kind<=3;kind++){
size_t size=kind==2?sizeof(NativePropertyState):sizeof(NativePipeCache),count=kind==2?b->count:LP_NPC;
const unsigned char *a=(const unsigned char*)(kind==2?(void*)b->states:(void*)b->pipes),*c=(const unsigned char*)(kind==2?(void*)p->states:(void*)pipes);
const DxField *fields=kind==2?dx_state_fields:dx_pipe_fields;
int nf=kind==2?(int)(sizeof(dx_state_fields)/sizeof(*dx_state_fields)):(int)(sizeof(dx_pipe_fields)/sizeof(*dx_pipe_fields));
for(size_t i=0;i<count*size;i++)if(a[i]!=c[i]){
d.kind=kind;d.index=(int)(i/size);size_t off=i%size;d.offset=off;d.size=1;
for(int f=0;f<nf;f++)if(off>=fields[f].offset && off<fields[f].offset+fields[f].size){d.field=f;d.offset=fields[f].offset;d.size=fields[f].size;break;}
memcpy(&d.baseline,a+d.index*size+d.offset,d.size);memcpy(&d.trial,c+d.index*size+d.offset,d.size);return d;
}
}
return d;
}
static void dx_failure(int r,NativePropertyCache *p,NativePipeCache *pipes){
if(dx_mode==5 && !dx_trace)return;
DxDifference d=dx_difference(p,pipes,&saved->snapshots[lp_before[r]]);
if(!d.kind){fprintf(stderr,"failure without difference\n");abort();}
int first=-999,origin=dx_trace?dx_origin(d,dx_version[dx_start_pos[r]],&first):-999;
fprintf(dx_fail_file,"{\"jac\":%llu,\"t\":%.17g,\"group\":%d,\"region\":%d,\"kind\":%d,\"index\":%d,\"field\":%d,\"offset\":%llu,\"baseline\":\"%016llx\",\"trial\":\"%016llx\",\"firstOrigin\":%d,\"persistentOrigin\":%d,\"firstContextOrigin\":%d}\n",
(unsigned long long)(dx_jac-1),dx_time,lp_color,r,d.kind,d.index,d.field,(unsigned long long)d.offset,(unsigned long long)d.baseline,(unsigned long long)d.trial,first,origin,dx_first_context);
}
void dx_initialize(void){
const char *m=getenv("CONTEXT_DIAG_MODE"),*s=getenv("CONTEXT_DIAG_STRIDE"),*seed=getenv("CONTEXT_DIAG_SEED"),*mat=getenv("CONTEXT_DIAG_MATRICES");
dx_mode=m?atoi(m):1;dx_stride=s?(unsigned)atoi(s):16;dx_seed=seed?(unsigned)atoi(seed):1;
dx_q0=lp_tick();dx_tick0=dx_clock();
if(dx_mode==1 || dx_mode==5){dx_fail_file=fopen("failures.jsonl","wb");if(!dx_fail_file)abort();setvbuf(dx_fail_file,NULL,_IOFBF,1024*1024);}
if(dx_mode==5){dx_input_file=fopen("trace-inputs.jsonl","wb");if(!dx_input_file)abort();}
if(dx_mode==5){dx_create_file=fopen("trace-created.jsonl","wb");if(!dx_create_file)abort();}
if(mat && atoi(mat)){dx_matrix_file=fopen("jacobians.bin","wb");if(!dx_matrix_file)abort();setvbuf(dx_matrix_file,NULL,_IOFBF,1024*1024);}
}
void dx_jacobian(void){
unsigned pos=(unsigned)(dx_jac++%(dx_stride?dx_stride:1));
if(!pos){dx_seed^=dx_seed<<13;dx_seed^=dx_seed>>17;dx_seed^=dx_seed<<5;dx_pick=dx_seed%(dx_stride?dx_stride:1);}
dx_selected=dx_mode==1 || dx_mode==5 || (dx_stride && pos==dx_pick);
dx_trace=dx_mode==5;
dx_details=dx_mode==5 && (dx_jac==1 || dx_jac==201 || dx_jac==451 || dx_jac==701 || dx_jac==896);
if(dx_selected)dx_sampled++;
}
void dx_eval_begin(double t,const double *y){(void)y;dx_time=t;dx_region=-1;dx_position=-1;dx_first_context=-999;memset(dx_seen,0,sizeof(dx_seen));}
void dx_eval_end(void){if(dx_region>=0){fprintf(stderr,"unclosed fallback region\n");abort();}dx_position=-1;}
void dx_schedule(NativePropertyCache *p,NativePipeCache *pipes){dx_region=-1;dx_boundary(0,-2,p,pipes);}
int dx_reuse(int r,NativePropertyCache *p,NativePipeCache *pipes,double *pv,double *h,double *q,double *w,double *fb){
int ok=lp_reuse(r,p,pipes,pv,h,q,w,fb);
DxRegion *s=&dx_stats[lp_color][r];s->attempts++;if(dx_contextual[r])s->compares++;
if(!ok){
if(!dx_contextual[r])abort();
s->failures++;dx_region=r;
if(dx_fail_file)dx_failure(r,p,pipes);
if(dx_mode>=2 && dx_mode<=4)dx_region_tick=dx_clock();
}
else dx_boundary(lp_end[r],-1000-r,p,pipes);
return ok;
}
void dx_op_begin(int pos,const double *inputs){
dx_position=pos;
if(lp_color<0){if(dx_mode==1 || dx_mode==5)memcpy(dx_inputs+dx_in_offset[pos],inputs,(dx_in_offset[pos+1]-dx_in_offset[pos])*sizeof(double));return;}
if(dx_details){
int any=0;
for(int i=0;i<dx_in_offset[pos+1]-dx_in_offset[pos];i++)if(memcmp(&inputs[i],&dx_inputs[dx_in_offset[pos]+i],8)){
uint64_t a,b;memcpy(&a,&dx_inputs[dx_in_offset[pos]+i],8);memcpy(&b,&inputs[i],8);
if(!any)fprintf(dx_input_file,"{\"jac\":%llu,\"group\":%d,\"position\":%d,\"inputs\":[",(unsigned long long)(dx_jac-1),lp_color,pos);
fprintf(dx_input_file,"%s[%d,\"%016llx\",\"%016llx\"]",any++?",":"",i,(unsigned long long)a,(unsigned long long)b);
}
if(any)fprintf(dx_input_file,"]}\n");
}
if(dx_region<0)return;
dx_stats[lp_color][dx_region].ops++;dx_op_count[lp_color][pos]++;
if(dx_mode==1 || dx_mode==5){
if(memcmp(dx_inputs+dx_in_offset[pos],inputs,(dx_in_offset[pos+1]-dx_in_offset[pos])*sizeof(double)))dx_stats[lp_color][dx_region].input_diff_ops++;
}
if(dx_mode==3 || dx_mode==4)dx_op_tick=dx_clock();
}
void dx_op_end(int pos,const double *outputs,NativePropertyCache *p,NativePipeCache *pipes){
if(dx_mutates[pos])dx_boundary(pos+1,pos,p,pipes);
if(lp_color<0){if(dx_mode==1 || dx_mode==5)memcpy(dx_outputs+dx_out_offset[pos],outputs,(dx_out_offset[pos+1]-dx_out_offset[pos])*sizeof(double));return;}
if(dx_region<0)return;
if(dx_mode==3 || dx_mode==4){uint64_t elapsed=dx_clock()-dx_op_tick;dx_op_ticks[lp_color][pos]+=elapsed;dx_stats[lp_color][dx_region].op_ticks+=elapsed;}
if((dx_mode==1 || dx_mode==5) && memcmp(dx_outputs+dx_out_offset[pos],outputs,(dx_out_offset[pos+1]-dx_out_offset[pos])*sizeof(double)))dx_stats[lp_color][dx_region].output_diff_ops++;
}
void dx_region_end(double *p,double *h,double *q,double *w,double *fb,NativePropertyCache *properties,NativePipeCache *pipes){
if(dx_region<0)return;
int r=dx_region;
if(dx_mode>=2 && dx_mode<=4)dx_stats[lp_color][r].region_ticks+=dx_clock()-dx_region_tick;
if(dx_mode==1 || dx_mode==5){
double *dst[]={p,h,q,w,fb};double *src[]={saved->p,saved->h,saved->q,saved->w,saved->fb};int changed=0;
for(int i=lp_first_output[r];i<lp_last_output[r];i++)changed|=memcmp(&dst[lp_output_map[i][0]][lp_output_map[i][1]],&src[lp_output_map[i][0]][lp_output_map[i][1]],sizeof(double))!=0;
dx_stats[lp_color][r].exit_output_diff+=changed;
dx_stats[lp_color][r].exit_context_diff+=dx_difference(properties,pipes,&saved->snapshots[lp_after[r]]).kind!=0;
}
dx_region=-1;
}
uint64_t dx_kernel_begin(int kind){
if(dx_mode==5 && dx_details && !dx_shadow && dx_position>=0){
if(dx_region>=0 && lp_color>=0)dx_kernel_count[dx_region][kind]++;
if(dx_depth>=64)abort();
dx_stack[dx_depth].kind=kind;dx_depth++;return 1;
}
if(!dx_selected || dx_shadow || dx_region<0 || lp_color<0)return 0;
dx_kernel_count[dx_region][kind]++;
if(dx_mode!=4)return 0;
if(dx_depth>=64)abort();
uint64_t t=dx_clock();dx_stack[dx_depth].start=t;dx_stack[dx_depth].child=0;dx_stack[dx_depth].kind=kind;dx_stack[dx_depth].region=dx_region;dx_depth++;return t;
}
void dx_kernel_end(int kind,uint64_t start){
if(!start)return;
if(start==1 && dx_mode==5){if(--dx_depth<0 || dx_stack[dx_depth].kind!=kind)abort();return;}
uint64_t elapsed=dx_clock()-start;int i=--dx_depth;
if(i<0 || dx_stack[i].kind!=kind)abort();
int r=dx_stack[i].region;dx_kernel_inclusive[r][kind]+=elapsed;dx_kernel_exclusive[r][kind]+=elapsed-dx_stack[i].child;
if(i)dx_stack[i-1].child+=elapsed;
}
void dx_property_created(NativePropertyCache *p,NativePropertyState *s){
if(!dx_details || dx_position<0 || !p)return;
uintptr_t a=(uintptr_t)s,b=(uintptr_t)p->states;
int slot=a>=b && a<b+p->capacity*sizeof(*s)?(int)((a-b)/sizeof(*s)):-1;
fprintf(dx_create_file,"{\"jac\":%llu,\"group\":%d,\"position\":%d,\"slot\":%d,\"p\":%.17g,\"T\":%.17g,\"count\":%llu,\"path\":[",(unsigned long long)(dx_jac-1),lp_color,dx_position,slot,s->p,s->T,(unsigned long long)p->count);
for(int i=0;i<dx_depth;i++)fprintf(dx_create_file,"%s%d",i?",":"",dx_stack[i].kind);
fprintf(dx_create_file,"]}\n");
}
void dx_validate_matrix(double t,const double *y,const double *m){if(dx_matrix_file){fwrite(&t,8,1,dx_matrix_file);fwrite(y,8,NSTATES,dx_matrix_file);fwrite(m,8,NSTATES*NSTATES,dx_matrix_file);}}
void dx_finish(void){
double hz=(double)(dx_clock()-dx_tick0)/((double)(lp_tick()-dx_q0)/frequency);
if(dx_fail_file)fclose(dx_fail_file);
if(dx_matrix_file)fclose(dx_matrix_file);
if(dx_input_file)fclose(dx_input_file);
if(dx_create_file)fclose(dx_create_file);
FILE *f=fopen("context.json","wb");if(!f)abort();
fprintf(f,"{\"mode\":%d,\"frequency\":%.9f,\"jacobians\":%llu,\"sampled\":%llu,\"regions\":[",dx_mode,hz,(unsigned long long)dx_jac,(unsigned long long)dx_sampled);
int comma=0;
for(int g=0;g<LP_NC;g++)for(int r=0;r<LP_NR;r++){
DxRegion *s=&dx_stats[g][r];if(!s->attempts)continue;
fprintf(f,"%s{\"group\":%d,\"region\":%d,\"attempts\":%llu,\"compares\":%llu,\"failures\":%llu,\"regionTicks\":%llu,\"opTicks\":%llu,\"ops\":%llu,\"inputDiffOps\":%llu,\"outputDiffOps\":%llu,\"exitOutputDiff\":%llu,\"exitContextDiff\":%llu}",comma++?",":"",g,r,(unsigned long long)s->attempts,(unsigned long long)s->compares,(unsigned long long)s->failures,(unsigned long long)s->region_ticks,(unsigned long long)s->op_ticks,(unsigned long long)s->ops,(unsigned long long)s->input_diff_ops,(unsigned long long)s->output_diff_ops,(unsigned long long)s->exit_output_diff,(unsigned long long)s->exit_context_diff);
}
fprintf(f,"],\"operations\":[");comma=0;
for(int g=0;g<LP_NC;g++)for(int p=0;p<LP_NO;p++)if(dx_op_count[g][p])fprintf(f,"%s[%d,%d,%llu,%llu]",comma++?",":"",g,p,(unsigned long long)dx_op_count[g][p],(unsigned long long)dx_op_ticks[g][p]);
fprintf(f,"],\"kernels\":[");comma=0;
for(int r=0;r<LP_NR;r++)for(int k=0;k<DX_NK;k++)if(dx_kernel_count[r][k])fprintf(f,"%s[%d,%d,%llu,%llu,%llu]",comma++?",":"",r,k,(unsigned long long)dx_kernel_count[r][k],(unsigned long long)dx_kernel_inclusive[r][k],(unsigned long long)dx_kernel_exclusive[r][k]);
fprintf(f,"],\"fields\":{");
for(int kind=2;kind<=3;kind++){
const DxField *fields=kind==2?dx_state_fields:dx_pipe_fields;int n=kind==2?(int)(sizeof(dx_state_fields)/sizeof(*dx_state_fields)):(int)(sizeof(dx_pipe_fields)/sizeof(*dx_pipe_fields));
fprintf(f,"%s\"%d\":[",kind==2?"":",",kind);for(int i=0;i<n;i++)fprintf(f,"%s{\"name\":\"%s\",\"offset\":%llu,\"size\":%llu,\"floating\":%d}",i?",":"",fields[i].name,(unsigned long long)fields[i].offset,(unsigned long long)fields[i].size,fields[i].floating);fprintf(f,"]");
}
fprintf(f,"}}\n");fclose(f);
}
+24
View File
@@ -0,0 +1,24 @@
/* Independent diagnostic worker. Never linked by the production builder. */
#ifndef CONTEXT_FALLBACK_DIAG_H
#define CONTEXT_FALLBACK_DIAG_H
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#include <windows.h>
#include <stdint.h>
#include "local_probe.h"
extern int dx_mode,dx_selected,dx_region,dx_position,dx_shadow;
extern const int dx_start_pos[LP_NR],dx_contextual[LP_NR],dx_version[LP_NO+1],dx_mutates[LP_NO];
extern const int dx_in_offset[LP_NO+1],dx_out_offset[LP_NO+1];
void dx_initialize(void);void dx_finish(void);void dx_jacobian(void);
int dx_reuse(int,NativePropertyCache*,NativePipeCache*,double*,double*,double*,double*,double*);
void dx_eval_begin(double,const double*);void dx_eval_end(void);
void dx_schedule(NativePropertyCache*,NativePipeCache*);
void dx_op_begin(int,const double*);void dx_op_end(int,const double*,NativePropertyCache*,NativePipeCache*);
void dx_region_end(double*,double*,double*,double*,double*,NativePropertyCache*,NativePipeCache*);
int dx_eval(double,const double*,double*,double*,ModelJacobianWorkspace*);
uint64_t dx_kernel_begin(int);void dx_kernel_end(int,uint64_t);
void dx_validate_matrix(double,const double*,const double*);
void dx_property_created(NativePropertyCache*,NativePropertyState*);
static inline uint64_t dx_clock(void){unsigned lo,hi;__asm__ __volatile__("lfence\n\trdtsc\n\tlfence":"=a"(lo),"=d"(hi)::"memory");return ((uint64_t)hi<<32)|lo;}
#endif
+578
View File
@@ -0,0 +1,578 @@
/* Diagnostic only. The semantic interpreter never calls a physics kernel. */
#include "context_access_diag.h"
#include "context_shadow_replay.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <fenv.h>
#if defined(__SSE__)
#include <xmmintrin.h>
#endif
typedef struct {fenv_t standard;int rounding;unsigned sse;} SREnvironment;
static unsigned sse_control(void){
#if defined(__SSE__)
return _mm_getcsr();
#else
return 0;
#endif
}
static void save_environment(SREnvironment *env){
fegetenv(&env->standard);env->rounding=fegetround();env->sse=0;
#if defined(__SSE__)
env->sse=_mm_getcsr();
#endif
}
static void restore_environment(const SREnvironment *env){
fesetenv(&env->standard);fesetround(env->rounding);
#if defined(__SSE__)
/* MinGW's fesetenv does not restore the complete SSE control register. */
_mm_setcsr(env->sse);
#endif
}
enum {OFF,RECORD,REFERENCE,CANDIDATE,TAIL};
enum {READ_STATE=1,WRITE_STATE,WRITE_OR,READ_PIPE,WRITE_PIPE,QUERY,ALLOCATE,VALID,SCALAR};
enum {OK,NO_RECORD,INPUTS,FIRST_MATCH,QUERY_PATH,CONSUMED,VALID_BITS,CAPACITY,OBSERVER,
EXISTING_UPDATE,PIPE_BRANCH,UNKNOWN_EFFECT,NONFINITE,MEMO_BINDING,MEMO_MISS,MEMO_VALUE,OVERFLOW,NREASONS};
static const char *reasons[]={"accepted","no_current_record","inputs","first_match_relation","query_hit_miss_path",
"consumed_field","valid_test","capacity_scratch","observer_nonnull","existing_entry_update","pipe_hit_branch",
"unknown_effect_or_schema","nonfinite_or_uncovered_branch","memo_lifetime_binding","memo_miss","memo_value","metadata_overflow"};
typedef struct {
int type,slot,offset,size,aux;
unsigned mask;
unsigned char data[sizeof(NativePropertyState)];
double result;
} Event;
typedef struct {
Event events[SR_EVENTS];
unsigned long long jac;
uintptr_t memo_binding;
double inputs[4],output;
size_t entry_count;
int position,n,ready,error,query_open,query_index,path;
int required_flags,baseline_errno,rounding;
unsigned required_sse_flags,sse_mode;
} Plan;
typedef struct {
unsigned long long total,accepted,rejected,mismatches,count_different,relocated,relocated_slots,appends;
unsigned long long paths[4],pt_hit,pt_miss,ph_hit,ph_miss,rejects[NREASONS];
unsigned long long live_contamination,rollbacks,tail_equal,live_tail_equal,memo_immutable,negative_passed;
unsigned long long untouched_equal,metadata_immutable;
size_t metadata_min,metadata_max,event_max;
} Statistics;
static Plan plan;
static Statistics stats;
static int phase,position,group,configured,pending_live,pending_status;
static unsigned long long jac;
static double sim_time,op_inputs[4];
static NativePropertyCache *bound;
static NativePipeCache *bound_pipes;
static SRContext entry,reference,candidate,overlay,before_transaction,tail_ref,tail_cand;
static SRFrame frame_ref,frame_cand;
static FILE *trials,*negative;
static unsigned long long forbidden_native_calls;
static void fatal(const char *message){fprintf(stderr,"shadow fatal: %s\n",message);abort();}
static void initialize(void){
if(configured)return;
const char *s=getenv("CONTEXT_SHADOW_POSITION");configured=s?atoi(s):16;
if(configured!=16 && configured!=52)fatal("invalid stage");
trials=fopen("shadow-trials.jsonl","wb");negative=fopen("shadow-negative.jsonl","wb");
if(!trials || !negative)fatal("open logs");
stats.metadata_min=(size_t)-1;
}
void sr_native_enter(const char *name){
if(phase==CANDIDATE){forbidden_native_calls++;fprintf(stderr,"Candidate called %s\n",name);fatal("physical call during replay");}
}
static int locate(const void *ptr,size_t width,int *domain,int *slot,int *offset){
uintptr_t p=(uintptr_t)ptr;
const void *bases[]={bound,bound?bound->states:NULL,bound_pipes};
size_t sizes[]={sizeof(NativePropertyCache),bound?bound->capacity*sizeof(NativePropertyState):0,SR_PIPES*sizeof(NativePipeCache)};
size_t units[]={sizeof(NativePropertyCache),sizeof(NativePropertyState),sizeof(NativePipeCache)};
for(int i=0;i<3;i++)if(bases[i] && p>=(uintptr_t)bases[i] && p-(uintptr_t)bases[i]+width<=sizes[i]){
size_t delta=p-(uintptr_t)bases[i];*domain=i;*slot=(int)(delta/units[i]);*offset=(int)(delta%units[i]);return 1;
}
return 0;
}
static Event *event(int type){
if(plan.n>=SR_EVENTS){plan.error=OVERFLOW;return NULL;}
Event *e=&plan.events[plan.n++];memset(e,0,sizeof(*e));e->type=type;return e;
}
static int state_pointer(int offset){
return offset==(int)offsetof(NativePropertyState,jacobian) || offset==(int)offsetof(NativePropertyState,temperatures);
}
static int floating_field(int domain,int offset,int size){
if(size!=8)return 0;
if(domain==1 && state_pointer(offset))return 0;
return offset>=(int)offsetof(NativeMedium,R) && offset<(domain==1?(int)offsetof(NativePropertyState,valid):(int)offsetof(NativePipeCache,kind));
}
void ax_access(const char *action,const void *ptr,size_t width,const char *field,const char *fn){
(void)field;
if(phase!=RECORD)return;
int domain,slot,offset;
if(!locate(ptr,width,&domain,&slot,&offset)){
/* The only recorded store to a stack local is scalar_get's output. */
if(!strcmp(action,"write") && strcmp(fn,"native_jacobian_scalar_get"))plan.error=UNKNOWN_EFFECT;
return;
}
if(domain==0){
if(!strcmp(action,"write") && offset!=(int)offsetof(NativePropertyCache,count))plan.error=UNKNOWN_EFFECT;
return; /* count/capacity and pointer bindings are semantic guards. */
}
if(plan.query_open && !strcmp(action,"read"))return; /* Re-run query, not baseline's scan slots. */
if(width>sizeof(NativePropertyState)){plan.error=UNKNOWN_EFFECT;return;}
int write=!strcmp(action,"write");
Event *e=event(domain==1?(write?WRITE_STATE:READ_STATE):(write?WRITE_PIPE:READ_PIPE));
if(!e)return;
e->slot=slot;e->offset=offset;e->size=(int)width;memcpy(e->data,ptr,width);
if(floating_field(domain,offset,(int)width)){double x;memcpy(&x,ptr,8);if(!isfinite(x))plan.error=NONFINITE;}
if(domain==1 && write && (size_t)slot<plan.entry_count)plan.error=EXISTING_UPDATE;
if(domain==2 && slot!=(configured==16?0:28))plan.error=UNKNOWN_EFFECT;
}
void sr_or(const void *ptr,unsigned mask,const char *field,const char *fn){
int previous=plan.n;ax_access("write",ptr,sizeof(unsigned),field,fn);
if(phase==RECORD && plan.n>previous){Event *e=&plan.events[plan.n-1];e->type=WRITE_OR;e->mask=mask;}
}
void ax_bind(NativePropertyCache *p,NativePipeCache *pipes){bound=p;bound_pipes=pipes;}
void ax_begin(unsigned long long j,int color,int pos,double t,const double *inputs,int n){
initialize();phase=OFF;jac=j;group=color;position=pos;sim_time=t;
if(pos!=configured)return;
if(n!=4 || !inputs)fatal("operation signature");
memcpy(op_inputs,inputs,sizeof(op_inputs));
if(color<0){
memset(&plan,0,sizeof(plan));plan.jac=j;plan.position=pos;plan.entry_count=bound->count;
plan.memo_binding=(uintptr_t)bound->jacobian;memcpy(plan.inputs,inputs,sizeof(plan.inputs));
plan.rounding=fegetround();
plan.sse_mode=sse_control()&~63u;
if(bound->temperatures)plan.error=OBSERVER;
phase=RECORD;
}
}
void ax_query(const char *kind,const NativeMedium *m,double p,double second){
if(phase!=RECORD)return;
if(plan.query_open){plan.error=UNKNOWN_EFFECT;return;}
Event *e=event(QUERY);if(!e)return;
e->aux=!strcmp(kind,"PH");e->offset=m->real_helium;e->slot=-2;
double key[]={p,second,m->R,m->cp,m->Tref,m->slope,m->mu,m->muT,m->S};
memcpy(e->data,key,sizeof(key));for(int i=0;i<9;i++)if(!isfinite(key[i]))plan.error=NONFINITE;
plan.query_open=1;plan.query_index=plan.n-1;
}
void ax_match(const char *kind,NativePropertyState *s){
if(phase!=RECORD)return;
if(!plan.query_open){plan.error=UNKNOWN_EFFECT;return;}
Event *e=&plan.events[plan.query_index];
if(e->aux!=(!strcmp(kind,"PH")))plan.error=UNKNOWN_EFFECT;
e->slot=s?(int)(s-bound->states):-1;plan.query_open=0;
}
void ax_new(NativePropertyCache *cache,NativePropertyState *s,int valid){
if(phase!=RECORD)return;
int domain,slot,offset;
if(!valid || !cache || !locate(s,sizeof(*s),&domain,&slot,&offset) || domain!=1 || offset || (size_t)slot+1!=cache->count){plan.error=CAPACITY;return;}
Event *e=event(ALLOCATE);if(e)e->slot=slot;
}
unsigned ax_test(NativePropertyState *s,unsigned mask,const char *fn){
(void)fn;unsigned result=s->valid&mask;
if(phase==RECORD && !plan.query_open){
int domain,slot,offset;
if(!locate(s,sizeof(*s),&domain,&slot,&offset) || domain!=1){plan.error=CAPACITY;return result;}
Event *e=event(VALID);if(e){e->slot=slot;e->mask=mask;memcpy(e->data,&result,sizeof(result));}
}
return result;
}
void ax_scalar(const char *action,NativeJacobianScalars *memo,int kind,int medium,const double *keys,size_t n,int hit,const double *value){
if(phase!=RECORD)return;
if(memo!=bound->jacobian || n>NATIVE_JACOBIAN_SCALAR_KEYS){plan.error=UNKNOWN_EFFECT;return;}
if(!strcmp(action,"get")){
Event *e=event(SCALAR);if(!e)return;e->slot=kind;e->offset=medium;e->size=(int)n;memcpy(e->data,keys,n*8);
if(hit && value){e->result=*value;e->aux=1;}
}else if(!strcmp(action,"put_attempt")){
int found=0;
for(int i=plan.n-1;i>=0;i--){Event *e=&plan.events[i];if(e->type==SCALAR && e->slot==kind && e->offset==medium && e->size==(int)n && !memcmp(e->data,keys,n*8)){
if(!value || !isfinite(*value)){plan.error=NONFINITE;break;}e->result=*value;e->aux=1;found=1;break;
}}
if(!found)plan.error=UNKNOWN_EFFECT;
}else plan.error=UNKNOWN_EFFECT;
}
static int path_of_plan(void){
int pattern[4],n=0;
for(int i=0;i<plan.n;i++)if(plan.events[i].type==QUERY){Event *e=&plan.events[i];if(n==4)return -1;pattern[n++]=e->aux*2+(e->slot>=0);}
if(n==0)return 0;
if(n==2 && pattern[0]==0 && pattern[1]==0)return 1;
if(n==4 && pattern[0]==2 && pattern[1]==0 && pattern[2]==1 && pattern[3]==0)return 2;
if(n==3 && pattern[0]==3 && pattern[1]==1 && pattern[2]==0)return 3;
return -1;
}
void ax_end(const double *outputs,int n){
if(phase!=RECORD)return;
if(n!=1 || !isfinite(outputs[0]))plan.error=NONFINITE;
plan.output=outputs[0];plan.path=path_of_plan();
plan.required_flags=fetestexcept(FE_ALL_EXCEPT);plan.baseline_errno=errno;
plan.required_sse_flags=sse_control()&63u;
if(plan.path<0 || plan.query_open)plan.error=NONFINITE;
for(int i=0;i<plan.n;i++)if(plan.events[i].type==SCALAR && !plan.events[i].aux)plan.error=UNKNOWN_EFFECT;
plan.ready=1;phase=OFF;
}
void ax_result(int result){(void)result;}
static void copy_context(SRContext *dst,const SRContext *src){
memcpy(dst,src,sizeof(*dst));dst->context.states=dst->states;dst->memo.entries=dst->entries;
if(src->context.jacobian==&src->memo)dst->context.jacobian=&dst->memo;
if(src->context.temperatures==&src->observer)dst->context.temperatures=&dst->observer;
for(size_t i=0;i<dst->context.count;i++){
if(src->states[i].jacobian==&src->memo)dst->states[i].jacobian=&dst->memo;
if(src->states[i].temperatures==&src->observer)dst->states[i].temperatures=&dst->observer;
}
}
static void from_live(SRContext *dst,NativePropertyCache *src,NativePipeCache *pipes){
if(src->count>SR_STATES || src->capacity>SR_STATES || !src->jacobian || src->jacobian->capacity>MODEL_JACOBIAN_SCALAR_COUNT)fatal("context bounds");
memset(dst,0,sizeof(*dst));memset(dst->states,0xa5,sizeof(dst->states));
dst->context=*src;memcpy(dst->states,src->states,src->count*sizeof(*src->states));memcpy(dst->pipes,pipes,sizeof(dst->pipes));
dst->memo=*src->jacobian;memcpy(dst->entries,src->jacobian->entries,src->jacobian->capacity*sizeof(*dst->entries));
dst->memo.entries=dst->entries;dst->context.states=dst->states;dst->context.jacobian=&dst->memo;
if(src->temperatures){dst->observer=*src->temperatures;dst->context.temperatures=&dst->observer;}
for(size_t i=0;i<src->count;i++){
if(dst->states[i].jacobian==src->jacobian)dst->states[i].jacobian=&dst->memo;
if(src->temperatures && dst->states[i].temperatures==src->temperatures)dst->states[i].temperatures=&dst->observer;
}
}
static int same_medium_key(const NativeMedium *m,int kind,const double *key){
return m->real_helium==kind && m->R==key[2] && m->cp==key[3] && m->Tref==key[4] && m->slope==key[5] && m->mu==key[6] && m->muT==key[7] && m->S==key[8];
}
static int first_match(SRContext *ctx,const Event *e){
double key[9];memcpy(key,e->data,sizeof(key));
for(size_t i=0;i<ctx->context.count;i++){
NativePropertyState *s=&ctx->states[i];unsigned mask=e->aux?NATIVE_PROPERTY_H:NATIVE_PROPERTY_PT;
if((s->valid&mask) && s->p==key[0] && (e->aux?s->h:s->T)==key[1] && same_medium_key(&s->medium,e->offset,key))return (int)i;
}
return -1;
}
static int map_slot(int *mapping,int logical,int actual){
if(logical<0 || logical>=SR_STATES || actual<0 || actual>=SR_STATES)return 0;
if(mapping[logical]>=0)return mapping[logical]==actual;
for(int i=0;i<SR_STATES;i++)if(i!=logical && mapping[i]==actual)return 0;
mapping[logical]=actual;return 1;
}
/* Same exact bit-key and bounded linear-probe semantics as the read-only memo.
* Only counters change. No physical fallback and no memo insertion is allowed. */
static int scalar_lookup(SRContext *ctx,const Event *e){
NativeJacobianScalars *memo=&ctx->memo;size_t capacity=memo->capacity;
if(!capacity || capacity>MODEL_JACOBIAN_SCALAR_COUNT || (capacity&(capacity-1)) || memo->recording)return MEMO_BINDING;
uint64_t hash=UINT64_C(14695981039346656037)^(unsigned)e->slot;
hash=(hash^(unsigned)e->offset)*UINT64_C(1099511628211);
for(int i=0;i<e->size;i++){uint64_t bits;memcpy(&bits,e->data+i*8,8);hash=(hash^bits)*UINT64_C(1099511628211);hash^=hash>>32;}
if(!hash)hash=1;
size_t limit=capacity<32?capacity:32;
for(size_t i=0;i<limit;i++){
NativeJacobianScalarEntry *v=&ctx->entries[(hash+i)&(capacity-1)];
if(!v->hash)return MEMO_MISS;
if(v->hash==hash && v->kind==e->slot && v->medium_kind==e->offset && v->input_count==(size_t)e->size && !memcmp(v->inputs,e->data,e->size*8)){
if(!isfinite(v->value) || memcmp(&v->value,&e->result,8))return MEMO_VALUE;
memo->reuses[e->slot]++;return OK;
}
}
return MEMO_MISS;
}
static int state_read_equal(SRContext *ctx,int slot,const Event *e){
NativePropertyState *s=&ctx->states[slot];
if(e->offset==(int)offsetof(NativePropertyState,jacobian))return s->jacobian==&ctx->memo;
if(e->offset==(int)offsetof(NativePropertyState,temperatures))return s->temperatures==NULL;
return !memcmp((unsigned char*)s+e->offset,e->data,e->size);
}
static int replay_overlay(SRContext *ctx,const double *inputs,int *mapping,int *appends){
if(!plan.ready || plan.jac!=jac || plan.position!=position)return NO_RECORD;
if(plan.error)return plan.error;
if(fegetround()!=plan.rounding || (fetestexcept(FE_ALL_EXCEPT)&plan.required_flags)!=plan.required_flags ||
(plan.baseline_errno && errno!=plan.baseline_errno) || (sse_control()&~63u)!=plan.sse_mode ||
(sse_control()&plan.required_sse_flags)!=plan.required_sse_flags)return UNKNOWN_EFFECT;
if(memcmp(inputs,plan.inputs,sizeof(plan.inputs)))return INPUTS;
for(int i=0;i<4;i++)if(!isfinite(inputs[i]))return NONFINITE;
if(ctx->context.temperatures)return OBSERVER;
if(ctx->context.capacity>SR_STATES || ctx->context.count>ctx->context.capacity)return CAPACITY;
if(ctx->context.jacobian!=&ctx->memo || ctx->memo.entries!=ctx->entries || ctx->memo.recording || (uintptr_t)bound->jacobian!=plan.memo_binding)return MEMO_BINDING;
for(size_t i=0;i<ctx->context.count;i++)if(ctx->states[i].temperatures || ctx->states[i].jacobian!=&ctx->memo)return MEMO_BINDING;
if(ctx->pipes[position==16?0:28].valid)return PIPE_BRANCH;
for(int i=0;i<SR_STATES;i++)mapping[i]=-1;
*appends=0;int pending_miss=0;
for(int i=0;i<plan.n;i++){
const Event *e=&plan.events[i];int slot=-1;
if(e->type<READ_STATE || e->type>SCALAR)return UNKNOWN_EFFECT;
if(e->type==QUERY){
int actual=first_match(ctx,e);
if((e->slot<0)!=(actual<0))return QUERY_PATH;
if(e->slot>=0 && !map_slot(mapping,e->slot,actual))return FIRST_MATCH;
pending_miss=(actual<0 && !e->aux);continue;
}
if(e->type==ALLOCATE){
if(!pending_miss)return UNKNOWN_EFFECT;
if(ctx->context.count>=ctx->context.capacity)return CAPACITY;
if(!map_slot(mapping,e->slot,(int)ctx->context.count))return FIRST_MATCH;
ctx->context.count++;(*appends)++;pending_miss=0;continue;
}
if(e->type==SCALAR){int reason=scalar_lookup(ctx,e);if(reason)return reason;continue;}
if(e->type==READ_PIPE || e->type==WRITE_PIPE){
if(e->slot!=(position==16?0:28) || e->offset<0 || e->offset+e->size>(int)sizeof(NativePipeCache))return UNKNOWN_EFFECT;
unsigned char *ptr=(unsigned char*)&ctx->pipes[e->slot]+e->offset;
if(e->type==READ_PIPE){if(memcmp(ptr,e->data,e->size))return PIPE_BRANCH;}
else memcpy(ptr,e->data,e->size);
continue;
}
if(e->slot<0 || e->slot>=SR_STATES || (slot=mapping[e->slot])<0 || (size_t)slot>=ctx->context.count)return FIRST_MATCH;
NativePropertyState *s=&ctx->states[slot];
if(e->type==VALID){unsigned expected;memcpy(&expected,e->data,sizeof(expected));if((s->valid&e->mask)!=expected)return VALID_BITS;continue;}
if(e->offset<0 || e->size<0 || e->offset+e->size>(int)sizeof(*s))return UNKNOWN_EFFECT;
if(e->type==READ_STATE){if(!state_read_equal(ctx,slot,e))return CONSUMED;continue;}
if(e->type!=WRITE_STATE && e->type!=WRITE_OR)return UNKNOWN_EFFECT;
if((size_t)e->slot<plan.entry_count || (size_t)slot<entry.context.count)return EXISTING_UPDATE;
if(e->type==WRITE_OR){if(e->offset!=(int)offsetof(NativePropertyState,valid))return UNKNOWN_EFFECT;s->valid|=e->mask;}
else if(e->offset==(int)offsetof(NativePropertyState,jacobian))s->jacobian=&ctx->memo;
else if(e->offset==(int)offsetof(NativePropertyState,temperatures))s->temperatures=NULL;
else memcpy((unsigned char*)s+e->offset,e->data,e->size);
}
if(pending_miss || !isfinite(plan.output))return NONFINITE;
return OK;
}
static void commit_patch(SRContext *dst,double *output,const int *mapping){
/* Apply only validated stores, in recorded order. Everything not addressed
* by a store stays in the original Candidate allocation, even at commit. */
for(int i=0;i<plan.n;i++){
const Event *e=&plan.events[i];
if(e->type==ALLOCATE)dst->context.count++;
else if(e->type==SCALAR)dst->memo.reuses[e->slot]++;
else if(e->type==WRITE_PIPE)memcpy((unsigned char*)&dst->pipes[e->slot]+e->offset,e->data,e->size);
else if(e->type==WRITE_STATE || e->type==WRITE_OR){
NativePropertyState *s=&dst->states[mapping[e->slot]];
if(e->type==WRITE_OR)s->valid|=e->mask;
else if(e->offset==(int)offsetof(NativePropertyState,jacobian))s->jacobian=&dst->memo;
else if(e->offset==(int)offsetof(NativePropertyState,temperatures))s->temperatures=NULL;
else memcpy((unsigned char*)s+e->offset,e->data,e->size);
}
}
*output=plan.output;
}
static int transaction(SRContext *dst,const double *inputs,double *output,int *mapping,int *appends){
copy_context(&overlay,dst);phase=CANDIDATE;
int reason=replay_overlay(&overlay,inputs,mapping,appends);
/* No stores to dst or output occur before this commit point. */
if(!reason)commit_patch(dst,output,mapping);
phase=OFF;return reason;
}
typedef struct {const char *name;size_t offset,size;} Field;
#define FIELD(T,n) {#n,offsetof(T,n),sizeof(((T*)0)->n)}
#define MEDIUM(T,n) {"medium." #n,offsetof(T,medium)+offsetof(NativeMedium,n),sizeof(((NativeMedium*)0)->n)}
#define MEDIUM_FIELDS(T) MEDIUM(T,real_helium),MEDIUM(T,R),MEDIUM(T,cp),MEDIUM(T,Tref),MEDIUM(T,slope),MEDIUM(T,mu),MEDIUM(T,muT),MEDIUM(T,S)
static const Field state_fields[]={MEDIUM_FIELDS(NativePropertyState),FIELD(NativePropertyState,p),FIELD(NativePropertyState,T),FIELD(NativePropertyState,h),FIELD(NativePropertyState,rho),FIELD(NativePropertyState,mu),FIELD(NativePropertyState,isentropic_factor),FIELD(NativePropertyState,isentropic_exponent),FIELD(NativePropertyState,valid)};
static const Field pipe_fields[]={MEDIUM_FIELDS(NativePipeCache),FIELD(NativePipeCache,p1),FIELD(NativePipeCache,p2),FIELD(NativePipeCache,T),FIELD(NativePipeCache,diameter),FIELD(NativePipeCache,length),FIELD(NativePipeCache,roughness),FIELD(NativePipeCache,flow),FIELD(NativePipeCache,kind),FIELD(NativePipeCache,valid)};
static char difference[160];
static int differ(const char *domain,int slot,const char *field){snprintf(difference,sizeof(difference),"%s[%d].%s",domain,slot,field);return 1;}
static int compare_frame(const SRFrame *a,const SRFrame *b){
const double *left[]={a->dy,a->w,a->p,a->h,a->q,a->fb,(const double*)a->g};
const double *right[]={b->dy,b->w,b->p,b->h,b->q,b->fb,(const double*)b->g};
const char *names[]={"dy","w","p","h","q","fb","gas_scalar"};
const int counts[]={NSTATES,NOUTPUTS,48,232,232,152,56*5};
for(int array=0;array<7;array++)for(int i=0;i<counts[array];i++)if(memcmp(&left[array][i],&right[array][i],8))return differ(names[array],i,"value");
return 0;
}
static int compare_contexts(const SRContext *a,const SRContext *b){
if(a->context.count!=b->context.count)return differ("property",-1,"count");
if(a->context.capacity!=b->context.capacity)return differ("property",-1,"capacity");
if(a->context.states!=a->states || b->context.states!=b->states)return differ("property",-1,"states_binding");
if(a->context.jacobian!=&a->memo || b->context.jacobian!=&b->memo)return differ("property",-1,"memo_binding");
if(!!a->context.temperatures!=!!b->context.temperatures)return differ("property",-1,"observer_binding");
for(size_t i=0;i<a->context.count;i++){
const NativePropertyState *x=&a->states[i],*y=&b->states[i];
for(size_t f=0;f<sizeof(state_fields)/sizeof(*state_fields);f++)if(memcmp((const char*)x+state_fields[f].offset,(const char*)y+state_fields[f].offset,state_fields[f].size))return differ("states",(int)i,state_fields[f].name);
if(x->jacobian!=&a->memo || y->jacobian!=&b->memo)return differ("states",(int)i,"memo_binding");
if(!!x->temperatures!=!!y->temperatures)return differ("states",(int)i,"observer_binding");
}
for(int i=0;i<SR_PIPES;i++)for(size_t f=0;f<sizeof(pipe_fields)/sizeof(*pipe_fields);f++)if(memcmp((const char*)&a->pipes[i]+pipe_fields[f].offset,(const char*)&b->pipes[i]+pipe_fields[f].offset,pipe_fields[f].size))return differ("pipes",i,pipe_fields[f].name);
if(a->memo.entries!=a->entries || b->memo.entries!=b->entries || a->memo.capacity!=b->memo.capacity || a->memo.recording!=b->memo.recording)return differ("memo",-1,"lifecycle");
if(memcmp(a->memo.evaluations,b->memo.evaluations,sizeof(a->memo.evaluations)))return differ("memo",-1,"evaluations");
if(memcmp(a->memo.reuses,b->memo.reuses,sizeof(a->memo.reuses)))return differ("memo",-1,"reuses");
for(size_t i=0;i<a->memo.capacity;i++)if(memcmp(&a->entries[i],&b->entries[i],sizeof(*a->entries))){
const Field fields[]={FIELD(NativeJacobianScalarEntry,hash),FIELD(NativeJacobianScalarEntry,inputs),FIELD(NativeJacobianScalarEntry,value),FIELD(NativeJacobianScalarEntry,kind),FIELD(NativeJacobianScalarEntry,medium_kind),FIELD(NativeJacobianScalarEntry,input_count)};
for(size_t k=0;k<sizeof(fields)/sizeof(*fields);k++)if(memcmp((const char*)&a->entries[i]+fields[k].offset,(const char*)&b->entries[i]+fields[k].offset,fields[k].size))return differ("memo_entries",(int)i,fields[k].name);
return differ("memo_entries",(int)i,"padding_byte");
}
if(memcmp(&a->observer,&b->observer,sizeof(a->observer)))return differ("warning",-1,"observer");
return 0;
}
static void dump_blob(FILE *f,const char *name,const void *ptr,size_t n){
fprintf(f,"\"%s\":\"",name);const unsigned char *b=ptr;for(size_t i=0;i<n;i++)fprintf(f,"%02x",b[i]);fputc('"',f);
}
static void mismatch(const char *where,double ref_output,double cand_output){
stats.mismatches++;
if(stats.mismatches==1){
FILE *f=fopen("first-mismatch.json","wb");if(!f)fatal("mismatch log");
fprintf(f,"{\"jac\":%llu,\"position\":%d,\"t\":%.17g,\"where\":\"%s\",\"field\":\"%s\",",jac,configured,sim_time,where,difference);
dump_blob(f,"probe_entry",&entry,sizeof(entry));fputc(',',f);dump_blob(f,"reference",&reference,sizeof(reference));fputc(',',f);dump_blob(f,"candidate",&candidate,sizeof(candidate));fputc(',',f);
dump_blob(f,"metadata",&plan,sizeof(plan));fputc(',',f);dump_blob(f,"reference_output",&ref_output,8);fputc(',',f);dump_blob(f,"candidate_output",&cand_output,8);fputc(',',f);
dump_blob(f,"reference_frame",&frame_ref,sizeof(frame_ref));fputc(',',f);dump_blob(f,"candidate_frame",&frame_cand,sizeof(frame_cand));fputc(',',f);
dump_blob(f,"reference_tail_context",&tail_ref,sizeof(tail_ref));fputc(',',f);dump_blob(f,"candidate_tail_context",&tail_cand,sizeof(tail_cand));fputs("}\n",f);fclose(f);
}
}
static void fill_frame(SRFrame *f,const double *p,const double *h,const double *q,const double *fb,const NativeGas *g,const double *w){
memset(f,0,sizeof(*f));memcpy(f->p,p,sizeof(f->p));memcpy(f->h,h,sizeof(f->h));memcpy(f->q,q,sizeof(f->q));memcpy(f->fb,fb,sizeof(f->fb));memcpy(f->g,g,sizeof(f->g));memcpy(f->w,w,sizeof(f->w));
}
static int live_equal(NativePropertyCache *p,NativePipeCache *pipes){
if(p->count!=entry.context.count || p->capacity!=entry.context.capacity || p->temperatures)return 0;
for(size_t i=0;i<p->count;i++){
NativePropertyState s=entry.states[i];s.jacobian=p->jacobian;
if(memcmp(&s,&p->states[i],sizeof(s)))return 0;
}
return !memcmp(pipes,entry.pipes,sizeof(entry.pipes)) && !memcmp(p->jacobian->entries,entry.entries,p->jacobian->capacity*sizeof(*entry.entries)) &&
!memcmp(p->jacobian->evaluations,entry.memo.evaluations,sizeof(entry.memo.evaluations)) && !memcmp(p->jacobian->reuses,entry.memo.reuses,sizeof(entry.memo.reuses));
}
static int untouched_equal(const SRContext *ctx){
for(size_t i=0;i<entry.context.count;i++){
NativePropertyState expected=entry.states[i];expected.jacobian=(NativeJacobianScalars*)&ctx->memo;
if(memcmp(&ctx->states[i],&expected,sizeof(expected)))return 0;
}
for(int i=0;i<SR_PIPES;i++)if(i!=(position==16?0:28) && memcmp(&ctx->pipes[i],&entry.pipes[i],sizeof(NativePipeCache)))return 0;
/* Unallocated tail is a poison-filled diagnostic canary. */
if(memcmp(ctx->states+ctx->context.count,entry.states+ctx->context.count,(SR_STATES-ctx->context.count)*sizeof(NativePropertyState)))return 0;
return 1;
}
static uint64_t metadata_hash(void){
const unsigned char *bytes=(const unsigned char*)&plan;uint64_t h=UINT64_C(14695981039346656037);
for(size_t i=0;i<sizeof(plan);i++)h=(h^bytes[i])*UINT64_C(1099511628211);
return h;
}
static void negative_tests(const double *inputs);
void sr_shadow(int pos,double t,const double *y,NativePropertyCache *properties,NativePipeCache *pipes,
const double *p,const double *h,const double *q,const double *fb,const NativeGas *g,const double *w,SROperation operation,SRTail tail){
initialize();
if(pos!=configured || group!=(configured==16?6:18))return;
uint64_t metadata_before=metadata_hash();
stats.total++;stats.count_different+=(plan.entry_count!=properties->count);
int saved_errno=errno;SREnvironment saved_env;save_environment(&saved_env);int entry_flags=fetestexcept(FE_ALL_EXCEPT);
from_live(&entry,properties,pipes);copy_context(&reference,&entry);copy_context(&candidate,&entry);
if(compare_contexts(&reference,&candidate))fatal("initial clones differ");
if(reference.states==candidate.states || reference.entries==candidate.entries)fatal("aliased clones");
phase=REFERENCE;double ref_output=operation(&reference.context,reference.pipes,op_inputs);phase=OFF;
int ref_errno=errno,ref_flags=fetestexcept(FE_ALL_EXCEPT);unsigned ref_sse=sse_control();
errno=saved_errno;restore_environment(&saved_env);
memcpy(&before_transaction,&candidate,sizeof(candidate));double cand_output=NAN;int mapping[SR_STATES],appends=0;
int reason=transaction(&candidate,op_inputs,&cand_output,mapping,&appends);
int cand_errno=errno,cand_flags=fetestexcept(FE_ALL_EXCEPT);unsigned cand_sse=sse_control();
if(reason){
stats.rejected++;stats.rejects[reason]++;
if(memcmp(&before_transaction,&candidate,sizeof(candidate)) || !isnan(cand_output))fatal("reject modified Candidate");
stats.rollbacks++;
}else{
stats.accepted++;stats.paths[plan.path]++;stats.appends+=appends;
int relocated=0;
for(int i=0;i<SR_STATES;i++)if(mapping[i]>=0 && mapping[i]!=i){relocated++;stats.relocated_slots++;}
stats.relocated+=(relocated>0);
for(int i=0;i<plan.n;i++)if(plan.events[i].type==QUERY){Event *e=&plan.events[i];if(e->aux){if(e->slot>=0)stats.ph_hit++;else stats.ph_miss++;}else{if(e->slot>=0)stats.pt_hit++;else stats.pt_miss++;}}
size_t bytes=sizeof(Plan)-sizeof(plan.events)+(size_t)plan.n*sizeof(Event);
if(bytes<stats.metadata_min)stats.metadata_min=bytes;
if(bytes>stats.metadata_max)stats.metadata_max=bytes;
if((size_t)plan.n>stats.event_max)stats.event_max=(size_t)plan.n;
if(memcmp(&ref_output,&cand_output,8)){differ("operation",pos,"output");mismatch("operation",ref_output,cand_output);}
else if(compare_contexts(&reference,&candidate))mismatch("operation_context",ref_output,cand_output);
if(!untouched_equal(&reference) || !untouched_equal(&candidate)){differ("probe",-1,"unwritten_data");mismatch("untouched",ref_output,cand_output);}else stats.untouched_equal++;
if(ref_errno!=cand_errno || ref_flags!=cand_flags || ref_sse!=cand_sse){differ("warning",-1,"errno_or_fenv_sse");mismatch("side_effect",ref_output,cand_output);}
if(memcmp(entry.entries,reference.entries,entry.memo.capacity*sizeof(*entry.entries)) || memcmp(entry.entries,candidate.entries,entry.memo.capacity*sizeof(*entry.entries))){differ("memo",-1,"readonly_entries");mismatch("memo",ref_output,cand_output);}else stats.memo_immutable++;
/* Both independent continuations begin at these operation exits. */
copy_context(&tail_ref,&reference);copy_context(&tail_cand,&candidate);
fill_frame(&frame_ref,p,h,q,fb,g,w);memcpy(&frame_cand,&frame_ref,sizeof(frame_ref));
int out_index=pos==16?45:152;frame_ref.q[out_index]=ref_output;frame_cand.q[out_index]=cand_output;
phase=TAIL;errno=saved_errno;restore_environment(&saved_env);
int ref_status=tail(t,y,&tail_ref,&frame_ref);
errno=saved_errno;restore_environment(&saved_env);
int cand_status=tail(t,y,&tail_cand,&frame_cand);phase=OFF;
if(ref_status!=cand_status){differ("evaluator",-1,"return");mismatch("continuation",ref_output,cand_output);}
else if(compare_frame(&frame_ref,&frame_cand))mismatch("continuation",ref_output,cand_output);
else if(compare_contexts(&tail_ref,&tail_cand))mismatch("continuation_context",ref_output,cand_output);
else stats.tail_equal++;
pending_live=1;pending_status=ref_status;
}
fprintf(trials,"{\"jac\":%llu,\"position\":%d,\"t\":%.17g,\"reason\":\"%s\",\"baselineCount\":%llu,\"probeCount\":%llu,\"referenceCount\":%llu,\"candidateCount\":%llu,\"appends\":%d,\"path\":%d,\"events\":%d,\"entryFlags\":%d,\"referenceFlags\":%d,\"candidateFlags\":%d,\"mapping\":[",jac,pos,t,reasons[reason],(unsigned long long)plan.entry_count,(unsigned long long)entry.context.count,(unsigned long long)reference.context.count,(unsigned long long)candidate.context.count,appends,plan.path,plan.n,entry_flags,ref_flags,cand_flags);
int comma=0;if(!reason)for(int i=0;i<SR_STATES;i++)if(mapping[i]>=0)fprintf(trials,"%s[%d,%d]",comma++?",":"",i,mapping[i]);
fputs("]}\n",trials);
if(!reason && (jac==200 || (configured==52 && jac==1)))negative_tests(op_inputs);
if(metadata_hash()!=metadata_before)fatal("Reference or Candidate modified metadata");
stats.metadata_immutable++;
if(!live_equal(properties,pipes))stats.live_contamination++;
phase=OFF;restore_environment(&saved_env);errno=saved_errno;
#if defined(__SSE__)
if(_mm_getcsr()!=saved_env.sse)stats.live_contamination++;
#endif
if(fegetround()!=saved_env.rounding)stats.live_contamination++;
}
void sr_eval_result(int result,const double *dy,const double *w){
if(!pending_live){return;}pending_live=0;
int different=result!=pending_status;
if(different)differ("evaluator",-1,"return");
for(int i=0;!different && i<NSTATES;i++)if(memcmp(&dy[i],&frame_ref.dy[i],8))different=differ("live_dy",i,"value");
for(int i=0;!different && i<NOUTPUTS;i++)if(memcmp(&w[i],&frame_ref.w[i],8))different=differ("live_w",i,"value");
if(different)mismatch("live_tail",0,0);
else stats.live_tail_equal++;
}
/* Adversarial rejection tests run on copies. Every failed transaction must
* leave the entire destination and explicit output sentinel byte-identical. */
static void negative_case(const char *name,int expected,const double *inputs){
SRContext before;memcpy(&before,&candidate,sizeof(before));double output=123.25;int mapping[SR_STATES],appends;
int reason=transaction(&candidate,inputs,&output,mapping,&appends);
int unchanged=!memcmp(&before,&candidate,sizeof(before)) && output==123.25;
fprintf(negative,"{\"jac\":%llu,\"test\":\"%s\",\"reason\":\"%s\",\"expected\":\"%s\",\"unchanged\":%s}\n",jac,name,reasons[reason],reasons[expected],unchanged?"true":"false");
if(reason!=expected || !unchanged)fatal("negative transaction test");
stats.negative_passed++;
}
static void negative_tests(const double *inputs){
Plan saved;memcpy(&saved,&plan,sizeof(saved));
copy_context(&candidate,&entry);candidate.context.temperatures=&candidate.observer;negative_case("observer",OBSERVER,inputs);
copy_context(&candidate,&entry);candidate.context.capacity=candidate.context.count;negative_case("capacity",CAPACITY,inputs);
int allocations=0;for(int i=0;i<plan.n;i++)allocations+=plan.events[i].type==ALLOCATE;
if(allocations>1){copy_context(&candidate,&entry);candidate.context.capacity=candidate.context.count+1;negative_case("capacity_after_first_append",CAPACITY,inputs);}
copy_context(&candidate,&entry);candidate.pipes[position==16?0:28].valid=1;negative_case("pipe_hit",PIPE_BRANCH,inputs);
copy_context(&candidate,&entry);candidate.memo.recording=1;negative_case("memo_recording",MEMO_BINDING,inputs);
copy_context(&candidate,&entry);memset(candidate.entries,0,sizeof(candidate.entries));negative_case("memo_miss",MEMO_MISS,inputs);
copy_context(&candidate,&entry);
for(size_t i=0;i<candidate.memo.capacity;i++)if(candidate.entries[i].hash)candidate.entries[i].value=nextafter(candidate.entries[i].value,INFINITY);
negative_case("memo_value",MEMO_VALUE,inputs);
copy_context(&candidate,&entry);double changed[4];memcpy(changed,inputs,sizeof(changed));changed[0]=nextafter(changed[0],INFINITY);negative_case("changed_input",INPUTS,changed);
int qi=-1,ri=-1,vi=-1,wi=-1;
for(int i=0;i<plan.n;i++){
if(qi<0 && plan.events[i].type==QUERY)qi=i;
if(ri<0 && plan.events[i].type==READ_STATE && plan.events[i].offset==(int)offsetof(NativePropertyState,rho))ri=i;
if(vi<0 && plan.events[i].type==VALID)vi=i;
if(wi<0 && plan.events[i].type==WRITE_STATE)wi=i;
}
if(qi>=0){
copy_context(&candidate,&entry);Event *e=&plan.events[qi];e->slot=e->slot<0?0:-1;negative_case("query_path",QUERY_PATH,inputs);memcpy(&plan,&saved,sizeof(plan));
}
if(ri>=0){copy_context(&candidate,&entry);plan.events[ri].data[0]^=1;negative_case("consumed_rho",CONSUMED,inputs);memcpy(&plan,&saved,sizeof(plan));}
if(vi>=0){copy_context(&candidate,&entry);plan.events[vi].data[0]^=(unsigned char)plan.events[vi].mask;negative_case("valid_test",VALID_BITS,inputs);memcpy(&plan,&saved,sizeof(plan));}
if(wi>=0){copy_context(&candidate,&entry);plan.entry_count=SR_STATES;negative_case("existing_entry_store",EXISTING_UPDATE,inputs);memcpy(&plan,&saved,sizeof(plan));}
if(allocations>1){
int logical=-1;
for(int i=0;i<plan.n;i++)if(plan.events[i].type==ALLOCATE){if(logical<0)logical=plan.events[i].slot;else{plan.events[i].slot=logical;break;}}
copy_context(&candidate,&entry);negative_case("conflicting_append_identity",FIRST_MATCH,inputs);memcpy(&plan,&saved,sizeof(plan));
}
if(qi>=0 && plan.events[qi].slot>=0){
int hit=plan.events[qi].slot;
copy_context(&candidate,&entry);candidate.states[hit].rho=nextafter(candidate.states[hit].rho,INFINITY);negative_case("probe_consumed_rho",CONSUMED,inputs);
copy_context(&candidate,&entry);candidate.states[hit].valid^=NATIVE_PROPERTY_MU;negative_case("probe_valid_bit",VALID_BITS,inputs);
for(int i=qi+1;i<plan.n;i++)if(plan.events[i].type==QUERY && plan.events[i].slot==hit){plan.events[i].slot=hit+1;break;}
copy_context(&candidate,&entry);negative_case("incompatible_first_hit_identity",FIRST_MATCH,inputs);memcpy(&plan,&saved,sizeof(plan));
}
copy_context(&candidate,&entry);plan.events[plan.n-1].type=999;negative_case("late_unknown_event",UNKNOWN_EFFECT,inputs);memcpy(&plan,&saved,sizeof(plan));
copy_context(&candidate,&entry);plan.output=NAN;negative_case("late_nonfinite_output",NONFINITE,inputs);memcpy(&plan,&saved,sizeof(plan));
copy_context(&candidate,&entry);plan.jac++;negative_case("expired_record",NO_RECORD,inputs);memcpy(&plan,&saved,sizeof(plan));
SREnvironment env;save_environment(&env);
if(plan.required_flags){copy_context(&candidate,&entry);feclearexcept(FE_ALL_EXCEPT);negative_case("uncovered_fenv",UNKNOWN_EFFECT,inputs);restore_environment(&env);}
copy_context(&candidate,&entry);fesetround(plan.rounding==FE_DOWNWARD?FE_UPWARD:FE_DOWNWARD);negative_case("rounding_mode",UNKNOWN_EFFECT,inputs);restore_environment(&env);
#if defined(__SSE__)
copy_context(&candidate,&entry);_mm_setcsr(env.sse^0x2000u);negative_case("sse_only_rounding_mode",UNKNOWN_EFFECT,inputs);restore_environment(&env);
#endif
}
void ax_finish(void){
initialize();FILE *f=fopen("shadow-summary.json","wb");if(!f)fatal("summary log");
fprintf(f,"{\"position\":%d,\"total\":%llu,\"accepted\":%llu,\"rejected\":%llu,\"mismatches\":%llu,\"countDifferent\":%llu,\"slotRelocationTrials\":%llu,\"relocatedSlots\":%llu,\"appends\":%llu,\"ptHit\":%llu,\"ptMiss\":%llu,\"phHit\":%llu,\"phMiss\":%llu,\"paths\":[%llu,%llu,%llu,%llu],\"liveContamination\":%llu,\"rejectRollbackChecks\":%llu,\"tailEqual\":%llu,\"liveTailEqual\":%llu,\"memoImmutable\":%llu,\"negativePassed\":%llu,\"forbiddenNativeCalls\":%llu,\"eventBytes\":%llu,\"eventMax\":%llu,\"metadataMin\":%llu,\"metadataMax\":%llu,\"metadataReserved\":%llu,\"contextCopyBytes\":%llu,\"frameCopyBytes\":%llu,\"rejectReasons\":{",configured,stats.total,stats.accepted,stats.rejected,stats.mismatches,stats.count_different,stats.relocated,stats.relocated_slots,stats.appends,stats.pt_hit,stats.pt_miss,stats.ph_hit,stats.ph_miss,stats.paths[0],stats.paths[1],stats.paths[2],stats.paths[3],stats.live_contamination,stats.rollbacks,stats.tail_equal,stats.live_tail_equal,stats.memo_immutable,stats.negative_passed,forbidden_native_calls,(unsigned long long)sizeof(Event),(unsigned long long)stats.event_max,(unsigned long long)stats.metadata_min,(unsigned long long)stats.metadata_max,(unsigned long long)sizeof(Plan),(unsigned long long)sizeof(SRContext),(unsigned long long)sizeof(SRFrame));
for(int i=1;i<NREASONS;i++)fprintf(f,"%s\"%s\":%llu",i>1?",":"",reasons[i],stats.rejects[i]);
fprintf(f,"},\"untouchedProbeEqual\":%llu,\"metadataImmutable\":%llu}\n",stats.untouched_equal,stats.metadata_immutable);fclose(f);fclose(trials);fclose(negative);
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef CONTEXT_SHADOW_REPLAY_H
#define CONTEXT_SHADOW_REPLAY_H
#include "model.h"
#define SR_STATES 256
#define SR_PIPES 40
#define SR_EVENTS 512
typedef struct {
NativePropertyCache context;
NativePropertyState states[SR_STATES];
NativePipeCache pipes[SR_PIPES];
NativeJacobianScalars memo;
NativeJacobianScalarEntry entries[MODEL_JACOBIAN_SCALAR_COUNT];
NativePropertyTemperatures observer;
} SRContext;
typedef struct {
double p[48],h[232],q[232],fb[152],w[NOUTPUTS],dy[NSTATES];
NativeGas g[56];
} SRFrame;
typedef int (*SRTail)(double,const double*,SRContext*,SRFrame*);
typedef double (*SROperation)(NativePropertyCache*,NativePipeCache*,const double*);
void sr_shadow(int,double,const double*,NativePropertyCache*,NativePipeCache*,
const double*,const double*,const double*,const double*,const NativeGas*,const double*,SROperation,SRTail);
void sr_eval_result(int,const double*,const double*);
void sr_native_enter(const char*);
void sr_or(const void*,unsigned,const char*,const char*);
#endif
+145
View File
@@ -0,0 +1,145 @@
# 最小 context shadow replay 验证结果
## 1. Candidate 能否逐位复现 Reference
**可以,在本轮两个 operation 的已覆盖路径和严格前置条件下,1,792 次 shadow replay 全部通过,出口不一致为 0。** R288 先完成全部 896 次;同一 worker 二进制通过该阶段后,才运行 position 52 的 896 次。
参考答案始终是**同一个当前 probe 入口的独立深拷贝,真实执行原 operation 后的出口**。没有把 baseline 出口当参考。baseline 只提供本 Jacobian 内的局部语义记录,不提供用于恢复的完整 context。
| 统计 | group 6 / R288 / position 16 | group 18 / R475 / position 52 |
|---|---:|---:|
| shadow 总次数 | 896 | 896 |
| 可重放 | 896 | 896 |
| 自然轨迹 reject | 0 | 0 |
| Reference/Candidate 不一致 | 0 | 0 |
| 入口 count 不同 | 638 | 24 |
| 发生 slot relocation 的 probe | 571 | 23 |
| 重定位逻辑 slot 数量 | 1142 | 45 |
| 追加条目总数 | 1658 | 1789 |
| PT hit | 0 | 168 |
| PT miss | 1658 | 1789 |
| PH hit | 0 | 1 |
| PH miss | 0 | 167 |
| 独立后续 evaluator 返回值/dy/w/context 一致 | 896 | 896 |
| 后续 evaluator 与主仿真返回值/dy/w 一致 | 896 | 896 |
| memo 条目只读验证 | 896 | 896 |
| 未写入的 probe 数据保持不变 | 896 | 896 |
| 局部 metadata 未被 Reference/Candidate 改写 | 896 | 896 |
| Candidate 物理 native 调用 | 0 | 0 |
| 主 context/浮点环境污染 | 0 | 0 |
### 如何保证比较有意义
1. 在目标 probe 的 operation 前,深拷贝 property states、全部 pipe 槽、scalar memo 的全部 entries 和计数,分别绑定到 Reference/Candidate 私有存储。两路没有共享可写 context。
2. Reference 调用该位置的原始表达式。Candidate 只解释 baseline 捕获的 query/read/valid/allocate/write/OR/scalar-get 语义;物理 native 入口有运行时禁入检查。Reference 执行期间不能补写 Candidate 的记录。
3. Candidate 从自己的 probe 副本建立 overlay。重新扫描当前有序 entries,采用原 PT/PH 的 exact `==` 和 medium 比较语义找 first-match;用逻辑 ID 映射结果,追加使用当前 count。
4. 所有条件成功后进入无失败分支的 commit,仅按记录顺序更新实际写入字段、valid OR、count、指定 pipe 槽、memo 命中计数及显式输出;不复制 baseline context,也不覆盖 probe 未写字段。reject 前没有向 Candidate 或输出提交任何存储。
5. 逐位比较输出、所有 active property 字段、valid bits、全部 pipe 字段、memo entries/计数、warning observer、errno 和 x87/SSE 环境。私有指针按“绑定到各自当前 owner”检查;不要求两个独立 allocation 的地址相等。数值字段没有容差或近似比较。
6. 两个独立出口继续执行同一份原 evaluator 后续代码,实测返回状态、完整 dy/w、context 和 memo 计数一致;再与主仿真的真实 evaluator 返回值/dy/w 核对。主仿真仍然执行原 operation,没有采用 Candidate 输出,没有进入真实 skip 路径。
7. 两轮主仿真的状态、输出、事件及全部 896 个 132×132 Jacobian 仍与未插桩基线逐字节一致。whole-context guard 源码哈希保持一致。
operation 本身返回一个 double,没有单独的 int 成功码;报告中的 evaluator 状态来自两条后续执行路径,不是用 `isfinite(output)` 代替。
### 两个具体入口例子(Jacobian 200)
| 项目 | R288 | position 52 |
|---|---|---|
| baseline count | `12` | `75` |
| 当前 probe count | `13` | `75` |
| Reference 出口 count | `15` | `77` |
| Candidate 出口 count | `15` | `77` |
| 逻辑 slot → 当前 slot | `[[12, 13], [13, 14]]` | `[[75, 75], [76, 76]]` |
R288 保留 probe 原 slot 12,在 13、14 追加;position 52 保留 probe slot 74 的压力/温度差异,查询重新扫描后仍 miss,再在 75、76 追加。
## 2. 哪些路径已经可以 replay
| 查询路径 | R288 次数 | position 52 次数 |
|---|---:|---:|
| 近零流量,无物性查询 | 67 | 1 |
| PT miss → PT miss | 829 | 727 |
| PH miss → PT miss → PT hit → PT miss | 0 | 167 |
| PH hit → PT hit → PT miss | 0 | 1 |
以上“可 replay”指本轮相应 operation 的完整 0–10 s 轨迹,且所有 runtime guard 同时成立。PH miss 的 h 登记与 valid OR、后续对刚追加逻辑条目的 PT hit 都保留。近零流量不追加物性条目,但仍执行 pipe 的有序写入,包括 valid=0 的同值写入。
R288 的 638 次 count 差异中,67 次属于无查询/无追加路径,因此只有 571 次发生实际 slot relocation。position 52 同理,24 次 count 差异中有一次无追加。不能把 count 差异次数当作重定位次数。
## 3. 哪些情况仍必须 reject / fallback
自然轨迹中各 reject 分类均为 0。为防止“全成功但拒绝机制无效”,另在私有副本中执行下列负例,要求 reject 且整个 Candidate 与输出 sentinel 逐字节不变;这些不计入自然轨迹的 896 次。
| reject 分类 | R288 负例通过次数 | position 52 负例通过次数 |
|---|---:|---:|
| `capacity_scratch` | 2 | 3 |
| `consumed_field` | 1 | 3 |
| `existing_entry_update` | 1 | 2 |
| `first_match_relation` | 1 | 2 |
| `inputs` | 1 | 2 |
| `memo_lifetime_binding` | 1 | 2 |
| `memo_miss` | 1 | 2 |
| `memo_value` | 1 | 2 |
| `no_current_record` | 1 | 2 |
| `nonfinite_or_uncovered_branch` | 1 | 2 |
| `observer_nonnull` | 1 | 2 |
| `pipe_hit_branch` | 1 | 2 |
| `query_hit_miss_path` | 1 | 2 |
| `unknown_effect_or_schema` | 4 | 8 |
| `valid_test` | 1 | 3 |
严格拒绝边界:
- 记录不属于当前 Jacobian/operation、源代码不再匹配已验证 worker、记录溢出或未知语义事件。
- 输入改变、first-match 逻辑映射冲突、hit/miss 路径改变、已消费字段或 valid 掩码结果不一致。
- 容量不足或 scratch、非空 observer、未覆盖的已有 entry 原地更新、pipe 命中分支变化、非有限结果或未覆盖路径。
- memo owner/lifetime/recording 不符合只读约束、所需 scalar key 未命中或值不匹配。Candidate 不调用物理 fallback 来弥补 memo miss。
- 舍入模式、SSE 控制模式或所需异常状态不满足已验证条件;已记录的非零 errno 前置条件不成立。
负例包括第一次追加完成后第二次容量检查失败、末尾未知事件、末尾非有限输出,因此覆盖了 overlay 已发生大量修改后的回滚,不只是入口早退。position 52 的 PH-hit 样本还直接改变 probe 已有条目的 rho 和 MU 位,确认消费值/valid guard 生效。
Jacobian memo 的 entries 始终只读,但 Reference 的 scalar get 会增加 reuses 计数。Candidate 在 overlay 中验证同一 bit-key 查找结果,并在 commit 中重放对应计数增量。没有将 baseline 的 recording/put 副作用照搬到 probe。
验证期间发现并修复了 Windows 诊断隔离问题:该工具链的 `fesetenv` 不完整恢复 SSE 控制寄存器。负例现在保存/恢复完整 x87/SSE 环境,并验证 SSE 单独改变时会拒绝。修复前失败证据保存在 `test/context-shadow-20260917/env-restore-investigation/`;本报告仅使用修复后的同一构建全量重跑结果。
## 4. Runtime metadata 需要多少
| 当前实现 | R288 | position 52 |
|---|---:|---:|
| 单条语义事件 | 176 B | 176 B |
| 最大事件数 | 132 | 138 |
| 每条 operation 记录实际使用范围 | 2224–23344 B | 2224–24400 B |
| 每阶段为 512 个事件预留 | 90224 B | 90224 B |
记录包括:Jacobian/operation 身份、四个显式输入、出口值、query 完整 key/逻辑命中关系、实际消费的字节值与 valid 掩码、创建顺序、字段更新/OR、scalar key/value、memo 绑定及环境前置条件。**不保存 baseline 完整 property/pipe context 用于 replay。** 只保留当前 Jacobian 的一条目标 operation 记录。
事务 scratch 另需一个 156816 B 的 overlay;本诊断为入口、Reference、Candidate、回滚检查及双路后续比较共保留 7 个 context 副本、2 个 22880 B frame。它们是 shadow 验证工作内存,不能算作未来 skip 每条记录都必须长期保存的 metadata。slot 映射临时表为 256 个 int(当前 ABI 1,024 B)。
这是当前保守事件表示的实测大小,未做去重或压缩,也没有据此评价性能。
## 5. 是否具备最小真实 skip 实验的条件
**已具备针对这两个单独 operation、上述四种已验证路径的下一阶段实验条件。** 下一步必须继续保持严格 guard、事务 commit、reject 后原执行以及独立 Reference 抽查/全量对比;先对 R288 单点实验,再单独考虑 position 52。
本轮没有实现真实 skip,也没有移除或放宽 whole-context guard。结论不适用于整个 R475、全部 reuse interval、新模型、其他 native kernel、容量耗尽/observer 非空/已有 entry 原地更新等未验证分支;Linux 尚未运行本实验。双路执行和详细检查的时长不作性能证据。
## 复现与证据
依赖上一轮保留的 access worker 和未插桩 baseline 文件。准备阶段核对 access worker 源码哈希;第二阶段核对第一阶段通过的 worker 二进制 SHA-256。
```powershell
.venv-win\Scripts\python.exe tests/manual/diagnose_context_shadow.py prepare
.venv-win\Scripts\python.exe tests/manual/diagnose_context_shadow.py run --position 16
.venv-win\Scripts\python.exe tests/manual/diagnose_context_shadow.py run --position 52
.venv-win\Scripts\python.exe tests/manual/analyze_context_shadow.py
```
生成文件位于 `test/context-shadow-20260917/`:`worker/build.json`、各阶段 `shadow-trials.jsonl`、`shadow-summary.json`、`shadow-negative.jsonl`、`measurement.json`,以及汇总的 `validation.json`。若出现不一致,会输出首个不同字段和完整入口/两路 context、frame、metadata 到 `first-mismatch.json`。最终两阶段均未生成该文件。
最终 worker SHA-256:`0491872485d3d97d6b79da556ea4de53b77e6daed9100112f1e1179a6dd3d18f`。
全量主轨迹哈希(两阶段相同):
- states: `100864d208ce5cd86d4728aa9c1a343b6252b057266c8ccb7010c7fb0bc14317`
- outputs: `46fdbd10844d72e4fd23debe3439fd8eb736567f48713950b1bb5faadbdb1ff8`
- events: `4a068d192278eb91bb78782cdcc7d635deaef8dabd074ddbde3edc1d5d080b7d`
- jacobians: `74d559c39973a609884c6bc9274ce1185d23e163b33d60443108c4519fedb448`
+134
View File
@@ -0,0 +1,134 @@
"""Independent, opt-in access recorder for positions 16 and 52 only.
Uses the existing fallback diagnostic worker as its source; never edits the
production kernels or changes the local-probe guard/replay policy.
"""
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import argparse, hashlib, json, re, shutil, time
import diagnose_context_fallback as dx
ROOT = dx.ROOT
HERE = Path(__file__).parent
BASE = ROOT / 'test/context-fallback-20260917/worker'
OUT = ROOT / 'test/context-access-20260917'
replace = dx.replace
def instrument(body):
"""Wrap reviewed lvalues first, then actual rvalues; keep short circuits."""
saved = []
def hold(text):
saved.append(text)
return f'AXHOLD{len(saved)-1}ZZ'
field = r'(?:s|up|down|cache|m|a|b)->\w+'
def reads(text):
# An address expression is not a value read (e.g. &s->rho).
text = re.sub(r'(?<![\w&])' + field, lambda m: 'AX_R(' + m[0] + ')', text)
# Taking &states[i] still consumes the states base pointer.
text = text.replace('&cache->states[', '&AX_R(cache->states)[')
return 'AX_R(*m)' if text.strip() == '*m' else text
body = re.sub(r'([su]\w*->valid)&(NATIVE_PROPERTY_\w+)',
lambda m: hold(f'ax_test({m[1].split("->")[0]},{m[2]},__func__)'), body)
body = body.replace('cache->count++', hold('AX_INC(cache->count)'))
# Struct reset is an explicit write, including equal-valued fields.
body = body.replace('*s=(NativePropertyState){0};', hold('AX_W(*s,((NativePropertyState){0}));'))
def write(m):
lhs, operator, rhs = m.groups()
return hold(('AX_OR' if operator == '|=' else 'AX_W') + f'({lhs},{reads(rhs)});')
body = re.sub(r'(' + field + r')\s*(\|=|=(?!=))\s*([^;]+);', write, body)
body = reads(body)
for i, value in enumerate(saved):
body = body.replace(f'AXHOLD{i}ZZ', value)
# Fail closed if a field write evaded the transformation.
assert not re.search(field + r'\s*(?:\|=|=(?!=)|\+\+)', body), body
return body
def change_function(source, name, transform):
a, b, e = dx.ex.function_span(source, name)
return source[:b+1] + transform(source[b+1:e-1]) + source[e-1:]
def prepare():
OUT.mkdir(exist_ok=True)
work = OUT / 'worker'
work.mkdir(exist_ok=True)
sources = {p.name: p.read_text(encoding='utf-8') for p in BASE.glob('*.c')}
assert sources, 'First prepare the existing context-fallback diagnostic worker.'
before = {k: hashlib.sha256(v.encode()).hexdigest() for k, v in sources.items()}
s = sources['properties.c']
def pt(body):
body = 'ax_query("PT",m,p,T);\n' + body
body = replace(body, 'same_medium(&s->medium,m))return s;', 'same_medium(&s->medium,m)){ax_match("PT",s);return s;}')
body = replace(body, 'return property_new(cache,m,p,T,scratch);', 'ax_match("PT",NULL);return property_new(cache,m,p,T,scratch);')
return body
s = change_function(s, 'property_pt', pt)
def ph(body):
body = 'ax_query("PH",m,p,h);\n' + body
body = replace(body, 'observe_temperature(cache->temperatures,m,s->T,3);return s->T;', 'ax_match("PH",s);observe_temperature(cache->temperatures,m,s->T,3);return s->T;')
return replace(body, 'double T;', 'ax_match("PH",NULL);double T;')
s = change_function(s, 'native_temperature_ph_context', ph)
s = change_function(s, 'property_new', lambda body: replace(body, '*s=(NativePropertyState){0};', 'ax_new(cache,s,valid);*s=(NativePropertyState){0};'))
functions = ['same_medium', 'property_new', 'property_pt', 'property_density', 'property_viscosity',
'native_temperature_ph_context', 'local_isentropic', 'isentropic', 'state_valve',
'native_density', 'native_temperature_ph', 'native_viscosity']
for name in functions:
s = change_function(s, name, instrument)
# Scalar get can write directly into s->rho through an output pointer.
s = change_function(s, 'native_jacobian_scalar_get', lambda body: replace(
replace(body, '*value=entry->value;', 'AX_W(*value,entry->value);ax_scalar("get",cache,kind,medium_kind,inputs,count,1,value);'),
'return 0;', 'ax_scalar("get",cache,kind,medium_kind,inputs,count,0,NULL);return 0;'))
s = change_function(s, 'native_jacobian_scalar_put', lambda body:
'ax_scalar("put_attempt",cache,kind,medium_kind,inputs,count,-1,&value);\n' + body)
sources['properties.c'] = s
for name in ['native_pipe_flow_context', 'native_pipe_flow_cached_context']:
sources['pipe.c'] = change_function(sources['pipe.c'], name, instrument)
s = sources['local_probe_support.c']
s = replace(s, 'dx_region=-1;dx_boundary(0,-2,p,pipes);', 'ax_bind(p,pipes);dx_region=-1;dx_boundary(0,-2,p,pipes);')
s = replace(s, 'void dx_op_begin(int pos,const double *inputs){',
'void dx_op_begin(int pos,const double *inputs){ax_begin(dx_jac-1,lp_color,pos,dx_time,inputs,dx_in_offset[pos+1]-dx_in_offset[pos]);')
s = replace(s, 'void dx_op_end(int pos,const double *outputs,NativePropertyCache *p,NativePipeCache *pipes){',
'void dx_op_end(int pos,const double *outputs,NativePropertyCache *p,NativePipeCache *pipes){ax_end(outputs,dx_out_offset[pos+1]-dx_out_offset[pos]);')
s = replace(s, 'void dx_finish(void){', 'void dx_finish(void){ax_finish();')
sources['local_probe_support.c'] = s
sources['model.c'] = replace(sources['model.c'], 'lp_active=-1;dx_eval_end();return result;', 'lp_active=-1;ax_result(result);dx_eval_end();return result;')
sources['context_access_diag.c'] = (HERE/'context_access_diag.c').read_text(encoding='utf-8')
for path in BASE.glob('*.h'):
shutil.copyfile(path, work/path.name)
shutil.copyfile(HERE/'context_access_diag.h', work/'context_access_diag.h')
cc, sun, _ = dx.ex.builder.toolchain()
flags, libs, dlls, exe = dx.ex.builder.platform_build_inputs(sun)
flags += ['-DLP_OBSERVE=0']
started = time.perf_counter()
def compile_one(item):
name, code = item
path = work/name
if '#define _WIN32_WINNT 0x0600' in code:
code = code.replace('#define _WIN32_WINNT 0x0600', '#define _WIN32_WINNT 0x0600\n#include "context_access_diag.h"', 1)
else:
code = '#include "context_access_diag.h"\n' + code
path.write_text(code, encoding='utf-8', newline='\n')
obj = path.with_suffix('.o'); log = []
dx.ex.builder._command([cc, *flags, '-I', str(work), '-I', str(dx.ex.builder.NATIVE/'include'), '-I', str(sun/'include'), '-c', str(path), '-o', str(obj)], log=log, timeout=240)
return obj, log
with ThreadPoolExecutor(max_workers=4) as pool:
objects = list(pool.map(compile_one, sources.items()))
log = []
dx.ex.builder._command([cc, *flags, *[str(o) for o, _ in objects], *dx.ex.builder.link_library_arguments(libs), '-lm', '-o', str(work/exe)], log=log)
for dll in dlls:
shutil.copyfile(dll, work/dll.name)
(work/'build.log').write_text('\n'.join(sum([v for _, v in objects], [])+log), encoding='utf-8')
dx.ex.write(work/'build.json', dict(sourceHashes=before, instrumentedHashes={p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in work.glob('*.c')}, seconds=time.perf_counter()-started, functions=functions))
print('BUILT access worker', flush=True)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('action', choices=['prepare', 'run'])
args = parser.parse_args()
if args.action == 'prepare':
prepare()
else:
dx.OUT = OUT
dx.run('audit', mode=1, matrices=True)
+126
View File
@@ -0,0 +1,126 @@
"""Build isolated context-fallback diagnostic workers; no implementation edits."""
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import argparse,hashlib,json,os,re,shutil,subprocess,time
import local_probe_experiment as ex
ROOT=ex.ROOT;SOURCE=ROOT/'test/local-probe-20260917/worker';OUT=ROOT/'test/context-fallback-20260917';HERE=Path(__file__).parent
KERNELS=[('properties','property_pt'),('properties','property_density'),('properties','property_viscosity'),
('properties','native_temperature_ph_context'),('properties','local_isentropic'),('properties','state_valve'),
('properties','native_jacobian_scalar_get'),('properties','native_temperature_ph'),('properties','native_density'),('properties','native_viscosity'),
('pipe','native_pipe_flow_cached_context'),('pipe','native_pipe_flow_context'),('pipe','native_pipe_resistance'),
('orifice','native_medium_orifice_context')]
replace=ex.replace
def function(s,name):a,b,e=ex.function_span(s,name);return s[a:e]
def vec(refs):return '(double[]){'+(','.join(refs) or '0')+'}'
def generate_model(source,ops,meta):
original=function(source,'model_eval_local_internal');start=original.index('if(lp_capture){');end=original.index('double node_energy[')
checkpoints=meta['contextCheckpointSlots'];capture=[]
for pos,op in enumerate(ops):
if str(pos) in checkpoints:capture.append(f'lp_snapshot({checkpoints[str(pos)]},properties,pipe_cache);')
capture += [f'dx_op_begin({pos},{vec(sorted(op.inputs))});',*op.code,f'dx_op_end({pos},{vec(op.outputs)},properties,pipe_cache);']
capture += [f'lp_snapshot({checkpoints[str(len(ops))]},properties,pipe_cache);','lp_save(p,h,q,w,fb);']
# Use the original capture branch in time-only runs; it is not profiled.
orig_capture=original[start+len('if(lp_capture){'):original.index('}else{',start)]
schedule=['dx_schedule(properties,pipe_cache);','if(lp_capture){','if(dx_mode==1 || dx_mode==5){',*capture,'}else{',orig_capture,'}','}else{','for(int pos=0;pos<LP_NO;){',
'int region=lp_plan[lp_color][pos];',
'if(region>=0 && dx_reuse(region,properties,pipe_cache,p,h,q,w,fb)){pos=lp_end[region];continue;}',
'switch(pos){']
for pos,op in enumerate(ops):
schedule += [f'case {pos}:{{',f'dx_op_begin({pos},(dx_mode==1 || dx_mode==5)?{vec(sorted(op.inputs))}:NULL);',*op.code,
f'dx_op_end({pos},(dx_mode==1 || dx_mode==5)?{vec(op.outputs)}:NULL,properties,pipe_cache);','break;}']
schedule += ['default:return 0;}','pos++;','if(dx_region>=0 && pos==lp_end[dx_region])dx_region_end(p,h,q,w,fb,properties,pipe_cache);','}}']
clone=(original[:start]+'\n'.join(schedule)+'\ndx_position=-1;\n'+original[end:]).replace('model_eval_local_internal(', 'dx_model_eval_local_internal(',1)
wrapper=function(source,'lp_eval').replace('int lp_eval(', 'int dx_eval(',1).replace('model_eval_local_internal(', 'dx_model_eval_local_internal(')
a=wrapper.index('{')+1;wrapper=wrapper[:a]+'\nif(!dx_selected)return lp_eval(t,y,dy,w,workspace);\ndx_eval_begin(t,y);\n'+wrapper[a:]
wrapper=replace(wrapper,'lp_active=-1;return result;','lp_active=-1;dx_eval_end();return result;')
return source+'\n'+clone+'\n'+wrapper
def kernel_wrapper(source,name,index):
a,b,e=ex.function_span(source,name);sig=source[a:b].strip();body=source[a:e]
args=sig[sig.index('(')+1:sig.rindex(')')]
params=[re.search(r'([A-Za-z_]\w*)\s*(?:\[[^]]*\])?$',x.strip())[1] for x in args.split(',')]
prefix=sig[:sig.index(name)].strip();typ=re.sub(r'^(?:static|NATIVE_COMPONENT_INTERNAL)\s+','',prefix).strip()
impl=body.replace(name+'(', 'dx_impl_'+name+'(',1)
call='dx_impl_'+name+'('+','.join(params)+')'
action=(call+';dx_kernel_end('+str(index)+',ticket);') if typ=='void' else (typ+' result='+call+';dx_kernel_end('+str(index)+',ticket);return result;')
wrapper=sig+'{uint64_t ticket=dx_kernel_begin('+str(index)+');'+action+'}'
# Forward declaration preserves recursive calls and cross-calls.
return source[:a]+sig+';\n'+impl+'\n'+wrapper+source[e:]
def prepare(kernels=False,trace=False):
OUT.mkdir(exist_ok=True);work=OUT/('trace-worker' if trace else 'kernels' if kernels else 'worker');work.mkdir(exist_ok=True)
program,saved=ex.capture(ROOT/'tests/data/test-mql-8-corrected.json')
assert program.source==(SOURCE.parent/'original-model.c').read_text(encoding='utf-8')
schedule=saved['schedule'];ops=[schedule.computations[b.members[0]] for b in schedule.blocks]
meta=json.loads((SOURCE.parent/'plan.json').read_text(encoding='utf-8'))
versions=[0];pure={'if','for','sizeof','fmax','fmin','fabs','sqrt','copysign','pow'}
for op in ops:versions.append(versions[-1]+int(bool(set(re.findall(r'\b([A-Za-z_]\w*)\s*\(', '\n'.join(op.code)))-pure)))
assert all(versions[int(pos)]==slot for pos,slot in meta['contextCheckpointSlots'].items())
ins=[0];outs=[0]
for op in ops:ins.append(ins[-1]+len(op.inputs));outs.append(outs[-1]+len(op.outputs))
arrays={'dx_start_pos':[a for a,b in meta['regions']],'dx_contextual':[int(versions[a]!=versions[b]) for a,b in meta['regions']],
'dx_version':versions,'dx_mutates':[versions[i+1]!=versions[i] for i in range(len(ops))],'dx_in_offset':ins,'dx_out_offset':outs}
tables='\n'.join('const int '+name+'[]={'+','.join(str(int(v)) for v in values)+'};' for name,values in arrays.items())
tables+=f'\n#define DX_NIN {ins[-1]}\n#define DX_NOUT {outs[-1]}\n'
sources={p.name:p.read_text(encoding='utf-8') for p in SOURCE.glob('*.c')};before={n:hashlib.sha256(s.encode()).hexdigest() for n,s in sources.items()}
sources['model.c']=generate_model(sources['model.c'],ops,meta)
sources['local_probe_support.c']+='\n'+(HERE/'context_fallback_diag.c').read_text(encoding='utf-8').replace('/* DIAG_TABLES */',tables)
sources['common.c']=replace(sources['common.c'],'lp_start();','lp_start();dx_initialize();')
sources['common.c']=replace(sources['common.c'],'lp_finish();','lp_finish();dx_finish();')
sources['cvode_solver.c']=replace(sources['cvode_solver.c'],'lp_eval(t,N_VGetArrayPointer(y),N_VGetArrayPointer(f),outputs,workspace)','dx_eval(t,N_VGetArrayPointer(y),N_VGetArrayPointer(f),outputs,workspace)')
sources['cvode_solver.c']=replace(sources['cvode_solver.c'],'{lp_color=-1;uint64_t start=lp_tick();','{lp_color=-1;dx_jacobian();uint64_t start=lp_tick();')
sources['cvode_solver.c']=replace(sources['cvode_solver.c'],'if(!result)lp_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));',
'if(!result){lp_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));dx_validate_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));}')
if kernels:
for i,(module,name) in enumerate(KERNELS):sources[module+'.c']=kernel_wrapper(sources[module+'.c'],name,i)
if trace:
a,b,e=ex.function_span(sources['properties.c'],'property_new')
fn=sources['properties.c'][a:e];fn=replace(fn,'return s;','dx_property_created(cache,s);return s;')
sources['properties.c']=sources['properties.c'][:a]+fn+sources['properties.c'][e:]
for name in ('model.h','local_probe.h'):shutil.copyfile(SOURCE/name,work/name)
(work/'context_fallback_diag.h').write_text((HERE/'context_fallback_diag.h').read_text(encoding='utf-8').replace('#include "local_probe.h"','#include "local_probe.h"\n#define DX_NK '+str(len(KERNELS))),encoding='utf-8')
cc,sun,_=ex.builder.toolchain();flags,libs,dlls,exe=ex.builder.platform_build_inputs(sun);flags+=['-DLP_OBSERVE=0'];started=time.perf_counter()
def compile_one(item):
i,(name,s)=item;path=work/name;path.write_text('#include "context_fallback_diag.h"\n'+s,encoding='utf-8',newline='\n');obj=work/f'diag-{i}.o';log=[]
ex.builder._command([cc,*flags,'-I',str(work),'-I',str(ex.builder.NATIVE/'include'),'-I',str(sun/'include'),'-c',str(path),'-o',str(obj)],log=log,timeout=240)
return obj,log
with ThreadPoolExecutor(max_workers=4) as pool:objs=list(pool.map(compile_one,enumerate(sources.items())))
log=[];ex.builder._command([cc,*flags,*[str(o) for o,_ in objs],*ex.builder.link_library_arguments(libs),'-lm','-o',str(work/exe)],log=log)
for dll in dlls:shutil.copyfile(dll,work/dll.name)
(work/'build.log').write_text('\n'.join(sum([v for _,v in objs],[])+log),encoding='utf-8')
ex.write(work/'build.json',dict(sourceHashes=before,seconds=time.perf_counter()-started,kernels=kernels))
ex.write(OUT/'plan.json',dict(**meta,versions=versions,kernels=KERNELS,code=[list(o.code) for o in ops],inputs=[sorted(o.inputs) for o in ops]))
print('BUILT',work.name,time.perf_counter()-started,flush=True)
def run(label,mode=1,stride=16,seed=1,kernels=False,matrices=False,control=False,trace=False):
work=OUT/label;work.mkdir(exist_ok=True);exe=(SOURCE if control else OUT/('trace-worker' if trace else 'kernels' if kernels else 'worker'))/'model.exe'
env=os.environ.copy();env.update(LOCAL_PROBE_MASK='0x7ffffff',CONTEXT_DIAG_MODE=str(mode),CONTEXT_DIAG_STRIDE=str(stride),CONTEXT_DIAG_SEED=str(seed),CONTEXT_DIAG_MATRICES=str(int(matrices)))
args=[str(exe),'--method','BDF','--start','0','--stop','10','--sample-step','.01','--max-step','1e30','--rtol','1e-8','--timeout','300',
'--sample-file',str(work/'states.bin'),'--output-block-file',str(work/'outputs.bin'),'--output',str(work/'result.json')]
start=time.perf_counter()
with (work/'stderr.log').open('wb') as f:p=subprocess.run(args,cwd=work,env=env,stdout=subprocess.PIPE,stderr=f,timeout=330,creationflags=subprocess.CREATE_NO_WINDOW)
elapsed=time.perf_counter()-start
if p.returncode:raise RuntimeError((label,p.returncode,(work/'stderr.log').read_text()[-5000:]))
result=json.loads((work/'result.json').read_text());diag=json.loads((work/'probe.json').read_text());ref=json.loads((SOURCE.parent/'all-run-0/measurement.json').read_text(encoding='utf-8'))
keys=['success','finalState','final','propertyWarnings','acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu']
assert all(result[k]==ref[k] for k in keys),(label,'result differs')
assert all(diag[k]==ref['diagnostic'][k] for k in ['newtonIterations','newtonConvergenceFailures','modelCalls','groups','contextCopiedBytes','contextComparedBytes']),(label,'counters differ')
hashes={}
for name in ('states','outputs','events','jacobians'):
p=work/(name+'.bin')
if p.exists():
with p.open('rb') as f:hashes[name]=hashlib.file_digest(f,'sha256').hexdigest()
if name=='jacobians':
with (SOURCE.parent/'all-audit/jacobians.bin').open('rb') as f:assert hashes[name]==hashlib.file_digest(f,'sha256').hexdigest()
else:assert hashes[name]==ref[name+'Sha256'],(label,name)
record=dict(label=label,mode=mode,stride=stride,seed=seed,kernels=kernels,control=control,processSeconds=elapsed,
solveSeconds=result['solveSeconds'],solveCpuSeconds=result['solveCpuSeconds'],jacobianSeconds=diag['jacobianSeconds'],hashes=hashes)
ex.write(work/'measurement.json',record);print('RUN',label,'Jac',diag['jacobianSeconds'],'solve',result['solveSeconds'],'exact OK',flush=True)
return record
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('action',choices=['prepare','run']);p.add_argument('--kernels',action='store_true');p.add_argument('--matrices',action='store_true');p.add_argument('--control',action='store_true');p.add_argument('--trace',action='store_true');p.add_argument('--label',default='census');p.add_argument('--mode',type=int,default=1);p.add_argument('--stride',type=int,default=16);p.add_argument('--seed',type=int,default=1);a=p.parse_args()
if a.action=='prepare':prepare(a.kernels,a.trace)
else:run(a.label,a.mode,a.stride,a.seed,a.kernels,a.matrices,a.control,a.trace)
+107
View File
@@ -0,0 +1,107 @@
"""Build/run a shadow-only replay experiment, R288 before position 52.
No Candidate result is used by the live evaluator. Baseline metadata is captured
online; Reference executes on a deep copy of the actual current probe entry.
"""
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import argparse,hashlib,json,os,shutil,time
import diagnose_context_access as access
ROOT=access.ROOT
HERE=Path(__file__).parent
BASE=ROOT/'test/context-access-20260917/worker'
OUT=ROOT/'test/context-shadow-20260917'
replace=access.replace
def generated_model(source,plan):
a,b,e=access.dx.ex.function_span(source,'dx_model_eval_local_internal')
fn=source[a:e]
tail=fn[fn.index('double node_energy['):]
generated=[]
for pos in (16,52):
operation=plan['code'][pos][0]
expected=(45,0,3,4,64) if pos==16 else (152,28,44,40,178)
qi,pipe,pi,gi,hi=expected
expr=operation.split('=',1)[1].rstrip(';')
for old,new in [(f'p[{pi}]','x[3]'),(f'g[{gi}].p','x[1]'),(f'g[{gi}].T','x[0]'),(f'h[{hi}]','x[2]')]:expr=expr.replace(old,new)
generated.append(f'static double sr_original_{pos}(NativePropertyCache *properties,NativePipeCache *pipe_cache,const double *x){{return {expr};}}')
header=f'''static int sr_tail_{pos}(double t,const double *y,SRContext *ctx,SRFrame *f){{
NativePropertyCache *properties=&ctx->context;NativePipeCache *pipe_cache=ctx->pipes;
double *p=f->p,*h=f->h,*q=f->q,*fb=f->fb,*w=f->w,*dy=f->dy;NativeGas *g=f->g;
(void)t;(void)y;(void)p;(void)h;(void)q;(void)fb;(void)w;(void)dy;(void)g;(void)properties;(void)pipe_cache;
'''
generated.append(header+'\n'.join('\n'.join(c) for c in plan['code'][pos+1:])+'\n'+tail)
hook=f'sr_shadow({pos},t,y,properties,pipe_cache,p,h,q,fb,g,w,sr_original_{pos},sr_tail_{pos});\n'
# Both baseline capture and the live probe switch still execute the
# original assignment. The hook returns void and cannot supply q.
assert fn.count(operation)==3 # detailed baseline, timed baseline, probe
fn=fn.replace(operation,hook+operation)
return source[:a]+'\n'.join(generated)+'\n'+fn+source[e:]
def prepare():
OUT.mkdir(exist_ok=True);work=OUT/'worker';work.mkdir(exist_ok=True)
sources={p.name:p.read_text(encoding='utf-8') for p in BASE.glob('*.c')}
assert sources
before={k:hashlib.sha256(v.encode()).hexdigest() for k,v in sources.items()}
certified=json.loads((BASE/'build.json').read_text(encoding='utf-8'))['instrumentedHashes']
assert before==certified, 'Access-validated source changed; revalidate it before extending the allowlist.'
plan=json.loads((ROOT/'test/context-fallback-20260917/plan.json').read_text(encoding='utf-8'))
sources['model.c']=generated_model(sources['model.c'],plan)
sources['model.c']=replace(sources['model.c'],'ax_result(result);dx_eval_end();','sr_eval_result(result,dy,w);dx_eval_end();')
# Every native entry reachable by either target is guarded during replay.
# Candidate's semantic interpreter has no calls to these physics functions.
native_functions={
'properties.c':['property_pt','property_density','property_viscosity','native_temperature_ph_context','local_isentropic','isentropic','state_valve','native_density','native_temperature_ph','native_viscosity'],
'pipe.c':['native_pipe_flow_cached_context','native_pipe_flow_context','native_pipe_resistance'],
}
for module,names in native_functions.items():
for name in names:sources[module]=access.change_function(sources[module],name,lambda body:'sr_native_enter(__func__);\n'+body)
del sources['context_access_diag.c']
sources['context_shadow_replay.c']=(HERE/'context_shadow_replay.c').read_text(encoding='utf-8')
for p in BASE.glob('*.h'):shutil.copyfile(p,work/p.name)
shutil.copyfile(HERE/'context_shadow_replay.h',work/'context_shadow_replay.h')
header=(work/'context_access_diag.h').read_text(encoding='utf-8')
header=header.replace('#include "kernels.h"','#include "kernels.h"\n#include "context_shadow_replay.h"')
header=replace(header,'(x)|=(v); ax_access("write",&(x),sizeof(x),#x,__func__);','(x)|=(v); sr_or(&(x),(v),#x,__func__);')
(work/'context_access_diag.h').write_text(header,encoding='utf-8')
cc,sun,_=access.dx.ex.builder.toolchain();flags,libs,dlls,exe=access.dx.ex.builder.platform_build_inputs(sun)
flags+=['-DLP_OBSERVE=0'];started=time.perf_counter()
def compile_one(item):
name,code=item;path=work/name;path.write_text(code,encoding='utf-8',newline='\n');obj=path.with_suffix('.o');log=[]
access.dx.ex.builder._command([cc,*flags,'-I',str(work),'-I',str(access.dx.ex.builder.NATIVE/'include'),'-I',str(sun/'include'),'-c',str(path),'-o',str(obj)],log=log,timeout=240)
return obj,log
with ThreadPoolExecutor(max_workers=4) as pool:objects=list(pool.map(compile_one,sources.items()))
log=[];access.dx.ex.builder._command([cc,*flags,*[str(o) for o,_ in objects],*access.dx.ex.builder.link_library_arguments(libs),'-lm','-o',str(work/exe)],log=log)
for dll in dlls:shutil.copyfile(dll,work/dll.name)
(work/'build.log').write_text('\n'.join(sum([v for _,v in objects],[])+log),encoding='utf-8')
guard_before=access.dx.function(sources['local_probe_support.c'],'lp_reuse')
guard_certified=access.dx.function((BASE/'local_probe_support.c').read_text(encoding='utf-8'),'lp_reuse')
assert guard_before==guard_certified
access.dx.ex.write(work/'build.json',dict(sourceHashes=before,instrumentedHashes={p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in work.glob('*.c')},seconds=time.perf_counter()-started,guardedNativeFunctions=native_functions,wholeContextGuardUnchanged=True))
print('BUILT independent shadow worker',flush=True)
def run(stage):
worker_hash=hashlib.sha256((OUT/'worker/model.exe').read_bytes()).hexdigest()
if stage==52:
gate=json.loads((OUT/'r288/shadow-summary.json').read_text(encoding='utf-8'))
assert gate['total']==896 and gate['accepted']==896 and gate['mismatches']==0 and gate['liveContamination']==0, 'R288 must pass first.'
assert gate['validatedWorkerSha256']==worker_hash, 'R288 must validate this exact worker build first.'
os.environ['CONTEXT_SHADOW_POSITION']=str(stage)
access.dx.OUT=OUT
label='r288' if stage==16 else 'position52'
access.dx.run(label,mode=1,matrices=True)
summary=json.loads((OUT/label/'shadow-summary.json').read_text(encoding='utf-8'))
assert summary['total']==896 and summary['mismatches']==0 and summary['liveContamination']==0
summary['validatedWorkerSha256']=worker_hash
access.dx.ex.write(OUT/label/'shadow-summary.json',summary)
print(json.dumps(summary,indent=2),flush=True)
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('action',choices=['prepare','run']);p.add_argument('--position',type=int,choices=[16,52],default=16);a=p.parse_args()
if a.action=='prepare':prepare()
else:run(a.position)
+328
View File
@@ -0,0 +1,328 @@
"""Verify production LSTP integration and probe force at real Amesim timestamps.
Dense trace sampling operates on an isolated runtime copy, without changing
the step schedule or time-event treatment. It never interpolates across events.
"""
from concurrent.futures import ThreadPoolExecutor
from dataclasses import replace
import argparse
import hashlib
import json
import math
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
from unittest.mock import patch
import numpy as np
ROOT=Path(__file__).resolve().parents[2]
sys.path.insert(0,str(ROOT))
from app.main import compile_system_xml_network
from app.simulation.backends import simulation_config
from app.simulation.native_codegen import build as builder,result_storage
from app.simulation.native_codegen.compiler import compile_native_program
from app.simulation.native_codegen.input import load_input
from app.simulation.native_codegen.runner import execute_native
from tests.manual import evaluate_mql8_correctness as evaluation,mql8_comparison as curves
PREVIOUS=ROOT/'test/mechanical-events-20260917/baselines'
DEFAULT=ROOT/'test/lstp-mainline-20260917'
save=evaluation.save
def model(profile):
project=ROOT/'test/output-semantics-20260917/after'/profile/'platform.json'
xml,doc=load_input(project)
net=compile_system_xml_network(doc)
return project,xml,doc,net,compile_native_program(net)
def build(program,out,native=None):
with patch.object(builder,'CACHE',out/'cache'),patch.object(builder,'NATIVE',native or ROOT/'native'), \
patch.object(builder,'ThreadPoolExecutor',lambda **kw:ThreadPoolExecutor(max_workers=1)):
return builder.build_native(program)
def baseline(out):
results={}
for profile in ('full','noncyclic'):
directory=out/'baseline'/profile
if (directory/'summary.json').exists():
results[profile]=json.loads((directory/'summary.json').read_bytes());continue
directory.mkdir(parents=True,exist_ok=True)
project,xml,doc,net,program=model(profile)
old=json.loads((PREVIOUS/profile/'lstp/event-descriptors.json').read_bytes())
# The sole generated-source addition is a constant descriptor table.
original=re.sub(r'^const NativeContact model_contacts\[[^\n]+\n','',program.source,flags=re.M)
assert hashlib.sha256(original.encode()).hexdigest()==old['originalModelSourceSha256']
assert program.jacobian_structure==old['jacobianStructure']
(directory/'platform.json').write_bytes(project.read_bytes());(directory/'platform.xml').write_bytes(xml)
reference=directory/'amesim';reference.mkdir(exist_ok=True)
for filename in ('test_mql_.var','test_mql_.results'):
if not (reference/filename).exists():os.link(PREVIOUS/profile/'reference'/filename,reference/filename)
audit=json.loads((PREVIOUS/profile/'audit/audit.json').read_bytes())
print(profile,'production build',flush=True)
compiled=build(program,out)
try:
with patch.object(result_storage,'RESULT_ROOT',out/'results'):
r=execute_native(compiled,replace(simulation_config(doc.simulation),rtol=1e-8),.01,
run_dir=directory/'native',timeout=180)
assert r['success'],r['message']
summary=evaluation.compare(directory,json.loads(project.read_bytes()),net,audit,evaluation.PROFILES[profile])
summary['nativeRun']={k:v for k,v in r.items() if k not in ('series','final','finalState')}
with np.load(directory/'curves.npz') as current,np.load(PREVIOUS/profile/'lstp/curves.npz') as expected:
summary['allComparedArraysEqualIsolatedLstp']=all(np.array_equal(current[k],expected[k],equal_nan=True) for k in current.files)
summary['generatedRhsUnchanged']=True
save(directory/'summary.json',summary);results[profile]=summary
print(profile,'done',summary['allComparedArraysEqualIsolatedLstp'],r['contactEvents'],flush=True)
finally:compiled.close()
save(out/'baseline-summary.json',results)
def trace(out):
project,xml,doc,net,program=model('full')
audit=json.loads((PREVIOUS/'full/audit/audit.json').read_bytes());curves.configure(audit,net)
ame=curves.read_ame(PREVIOUS/'full/reference')
at=np.array(ame.times)
# Query BEFORE and AFTER every forcing boundary, including the exact saved
# Amesim row. These queries never become solver stop times.
queries=set();points=[]
boundaries=evaluation.event_times(json.loads(project.read_bytes()),50)
for boundary in boundaries:
if boundary<=0:continue
ai=int(np.argmin(abs(at-boundary)))
points.append(dict(boundary=boundary,ameIndex=ai,ameTime=float(at[ai])))
queries.update((math.nextafter(boundary,-math.inf),boundary,float(at[ai])))
for delta in (-1e-6,-1e-9,-1e-12,1e-14,1e-13,5e-13,1e-12,2e-12,3e-12,1e-11,1e-10,1e-9,1e-8,1e-7,1e-6,1e-5,1e-4,.001):
queries.add(boundary+delta)
queries=sorted(t for t in queries if 0<t<50)
directory=out/'trace';directory.mkdir(exist_ok=True)
save(directory/'queries.json',dict(points=points,times=queries,stateKeys=program.state_keys))
runtime=directory/'native-source';shutil.copytree(ROOT/'native',runtime,dirs_exist_ok=True)
source=runtime/'runtime/common.c';text=source.read_text(encoding='utf-8')
hook='''
static void force_trace(double t,double end,int event,NativeDense dense,void *context) {
static const double times[]={TIMES};
static size_t cursor=0;
while(cursor<sizeof(times)/sizeof(times[0]) && (event?times[cursor]<end:times[cursor]<=end)) {
double at=times[cursor++],y[NSTATES];
if(at<t || !dense(context,at,y)) continue;
fprintf(stderr,"{\\"phase\\":\\"force-dense-trace\\",\\"time\\":%.17g,\\"state\\":[",at);
for(int i=0;i<NSTATES;i++)fprintf(stderr,"%s%.17g",i?",":"",y[i]);
fprintf(stderr,"]}\\n");
}
}
'''.replace('TIMES',','.join(repr(t) for t in queries))
text=text.replace('int native_accept(',hook+'\nint native_accept(',1)
needle=' for (int i=0;i<count;i++) stop=fmin(stop,when[i]);'
assert text.count(needle)==1
text=text.replace(needle,needle+'\n force_trace(t,stop,count,dense,context);',1)
source.write_text(text,encoding='utf-8')
print('dense trace build',flush=True);compiled=build(program,out,runtime)
try:
r=execute_native(compiled,replace(simulation_config(doc.simulation),rtol=1e-8),.01,
record_samples=False,run_dir=directory/'run',timeout=180)
assert r['success'],r['message']
base=json.loads((out/'baseline/full/summary.json').read_bytes())['nativeRun']
fields=('nfev','acceptedSteps','rejectedSteps','stateTransitions','solverStarts','njev','nlu','contactEvents')
assert all(r[k]==base[k] for k in fields),'Trace changed integration work'
states=[]
for line in (directory/'run/worker.log').read_text(encoding='utf-8').splitlines():
try:e=json.loads(line)
except ValueError:continue
if e.get('phase')=='force-dense-trace':states.append(e)
assert [s['time'] for s in states]==queries
payload='\n'.join(' '.join(format(v,'.17g') for v in (s['time'],*s['state'])) for s in states)+'\n'
probes=subprocess.run([str(compiled.executable),'--probe'],input=payload,capture_output=True,text=True,check=True,timeout=45)
output=[json.loads(line) for line in probes.stdout.splitlines()]
keys=[v.key for v in program.variables]
focused=[k for k in keys if any(word in k for word in ('amesim_mecmas21','amesim_lstp00a','amesim_ud00','amesim_forc'))]
rows=[]
for s,e in zip(states,output):
assert e['success'];outputs=dict(zip(keys,e['outputs']))
rows.append(dict(time=s['time'],values={k:outputs[k] for k in focused}))
save(directory/'dense-outputs.json',rows)
save(directory/'summary.json',dict(countersUnchanged=True,queries=len(rows),nativeRun={k:v for k,v in r.items() if k not in ('series','final','finalState')}))
print('trace done',len(rows),flush=True)
finally:compiled.close()
def rebase(out):
"""Short diagnostic continuation in a local clock, with the SAME saved
boundary state and physics. This is not a production time-handling patch.
"""
_,_,doc,net,program=model('full')
points=json.loads((out/'trace/queries.json').read_bytes())['points']
point=next(p for p in points if abs(p['boundary']-32.4)<1e-6)
origin=point['boundary'];duration=point['ameTime']-origin
summary=json.loads((out/'baseline/full/summary.json').read_bytes())
state_file=out/'results'/summary['nativeRun']['resultStorage']['id']/'states.bin'
assert not result_storage.scan_blocks(state_file)['corrupt']
initial=None
with state_file.open('rb') as stream:
while header:=stream.read(result_storage._HEADER.size):
magic,sequence,nrow,ncol,crc=result_storage._HEADER.unpack(header)
assert magic==b'SIMBLK01' and 0<nrow<=1024 and ncol==len(program.state_keys)+1
block=np.frombuffer(stream.read(nrow*ncol*8),dtype='<f8').reshape(ncol,nrow).T
assert stream.read(8)==b'COMMIT01'
matches=np.flatnonzero(block[:,0]==origin)
if len(matches):initial=block[matches[-1],1:].copy()
assert initial is not None
original=program.source
names=('model_init','model_eval','model_eval_jacobian','model_eval_jacobian_reuse',
'model_friction_drives','model_property_temperatures','model_next_break')
source=re.sub(r'\b('+ '|'.join(names)+r')\s*\(',lambda m:'absolute_'+m[1]+'(',original)
source+='\nint model_init(double *y) {const double initial[NSTATES]={'+','.join(repr(float(v)) for v in initial)+'};memcpy(y,initial,sizeof(initial));return 1;}\n'
for name,tail,args in [
('model_eval','double *dy,double *w','dy,w'),
('model_eval_jacobian','double *dy,double *w','dy,w'),
('model_eval_jacobian_reuse','double *dy,double *w,ModelJacobianWorkspace *workspace','dy,w,workspace'),
('model_friction_drives','double *drives','drives'),
('model_property_temperatures','NativePropertyTemperatures *temperatures','temperatures')]:
source+=f'int {name}(double t,const double *y,{tail}) {{return absolute_{name}(t+{origin!r},y,{args});}}\n'
# All STEP/UD00 signals are constant in this 2.2 ps continuation.
source+='double model_next_break(double t,double end) {(void)t;return end;}\n'
directory=out/'rebase';directory.mkdir(exist_ok=True)
save(directory/'initial-state.json',dict(time=origin,stateKeys=program.state_keys,state=initial.tolist()))
compiled=build(replace(program,source=source),out)
try:
records=[]
for rtol in (1e-8,1e-10):
config=replace(simulation_config(doc.simulation),t_start=0,t_stop=duration,max_step=duration/20,rtol=rtol)
with patch.object(result_storage,'RESULT_ROOT',out/'results'):
r=execute_native(compiled,config,duration/40,run_dir=directory/str(rtol),timeout=60)
assert r['success'],r['message']
record={k:v for k,v in r.items() if k not in ('series',)}
record.update(origin=origin,duration=duration,rtol=rtol)
records.append(record)
print('rebase',rtol,r['final']['amesim_lstp00a_2.force'],r['solverControl'],flush=True)
save(directory/'summary.json',records)
finally:compiled.close()
def analyze(out):
project,_,_,net,program=model('full')
info=json.loads((out/'trace/queries.json').read_bytes())
dense={row['time']:row['values'] for row in json.loads((out/'trace/dense-outputs.json').read_bytes())}
descriptors=json.loads((PREVIOUS/'full/lstp/event-descriptors.json').read_bytes())['contacts']
grid=np.load(out/'baseline/full/curves.npz')
rows=[]
for point in info['points']:
boundary=point['boundary'];at=point['ameTime'];i=int(np.argmin(abs(grid['time']-boundary)))
if not any(abs(boundary-t)<1e-6 for t in (21.6,32.4,43.2)):continue
for contact in descriptors:
name=contact['name'];_,v1,v2,gap,k,d,pdis,option=contact['values']
keys=[program.state_keys[v1],program.state_keys[v2]]
values={}
for side in ('platform','amesim'):
p=-float(grid[side+'|'+name+'.gap'][i])
velocity=float(grid[side+'|'+keys[0]][i]-grid[side+'|'+keys[1]][i])
elastic=k*p;damping=-math.expm1(-p/pdis)*d*velocity
force=float(grid[side+'|'+name+'.force'][i])
values[side]=dict(penetration=p,relativeVelocity=velocity,elastic=elastic,damping=damping,force=force,
reconstructedForce=elastic+damping,residual=force-(elastic+damping))
error=values['platform']['force']-values['amesim']['force']
same=dense[at][name+'.force'];same_error=same-values['amesim']['force']
row=dict(component=name,nominalTime=float(grid['time'][i]),boundary=boundary,ameTime=at,
elapsedAfterBoundary=at-boundary,**values,originalForceError=error,
elasticError=values['platform']['elastic']-values['amesim']['elastic'],
dampingError=values['platform']['damping']-values['amesim']['damping'],
sameTimeForce=same,sameTimeForceError=same_error,
errorReductionPercent=100*(1-abs(same_error)/abs(error)),
timeUlp=math.ulp(boundary),forceChangePerTimeUlp=d*abs(dense[at]['amesim_mecmas21_10.a'])*math.ulp(boundary))
rows.append(row)
save(out/'force-error-analysis.json',rows)
for row in rows:
if row['component']=='amesim_lstp00a_2':print(json.dumps(row,ensure_ascii=False))
# Report the absolute raw event peak separately from reference error.
raw=json.loads((out/'baseline/full/native/result.json').read_bytes())['series']
name='amesim_lstp00a_2';i=int(np.argmax(np.abs(raw[name+'.force'])))
peak=dict(time=raw['time'][i],force=raw[name+'.force'][i],gap=raw[name+'.gap'][i],
relativeVelocity=raw[name+'.port_1.v'][i]-raw[name+'.port_2.v'][i])
peak['elastic']=-peak['gap']*1e11;peak['damping']=peak['force']-peak['elastic']
keys=(name+'.force',name+'.gap',name+'.port_1.v',name+'.port_2.v',
'amesim_mecmas21_10.x','amesim_mecmas21_10.v','amesim_ud00_2.out.signal')
peak['adjacentSamples']=[dict(time=raw['time'][j],**{k:raw[k][j] for k in keys}) for j in (i-1,i,i+1)]
save(out/'raw-force-peak.json',peak);print('raw peak',peak)
grid.close()
def amesim_events(out):
"""Enable only Amesim's documented discontinuities printout on a copy."""
directory=out/'amesim-event-output'
evaluation.prepare_ame(ROOT/'tests/data/test_mql.ame',directory,50,.01,1e-8)
sim=directory/'test_mql_.sim';lines=sim.read_text(encoding='ascii').splitlines()
before=lines[:];options=lines[1].split()
# Amesim 2404 scripting/python/amesim.py: ameputsimopt maps printDiscont
# to simOptions[2]; every other solver and model setting stays unchanged.
options[2]='1';lines[1]=' '.join(options);sim.write_text('\n'.join(lines)+'\n',encoding='ascii')
save(directory/'print-option-change.json',dict(before=before,after=lines,source='Amesim 2404 ameputsimopt: simOptions[2]'))
run=evaluation.run_ame(directory,Path('F:/AMESim2404/Amesim'))
reference=curves.read_ame(directory)
audit=json.loads((PREVIOUS/'full/audit/audit.json').read_bytes())
aliases={x['component']:x['ameAlias'] for x in audit['mapping']}
keys={'force':'f1@'+aliases['amesim_lstp00a_2'],
'gap':'gap@'+aliases['amesim_lstp00a_2'],
'massVelocity':'v1@'+aliases['amesim_mecmas21_10'],
'branchVelocity':'v1@'+aliases['amesim_mecmas21_2'],
'massPosition':'x1@'+aliases['amesim_mecmas21_10'],
'drive':'output@'+aliases['amesim_ud00_2']}
times=np.array(reference.times);data={k:np.array(reference.series(v)) for k,v in keys.items()}
data['gap']*=.001
selected=np.flatnonzero((times>32.39999)&(times<32.4004))
rows=[dict(time=float(times[i]),**{k:float(v[i]) for k,v in data.items()}) for i in selected]
j=int(np.argmax(abs(data['force'])))
peak=dict(index=j,time=float(times[j]),**{k:float(v[j]) for k,v in data.items()})
save(directory/'mechanical-events.json',dict(run=run,samples=len(times),around324=rows,maximumAbsoluteForce=peak))
print('Amesim event output',len(times),'rows',rows,'peak',peak,flush=True)
def event_comparison(out):
directory=out/'event-output-comparison';directory.mkdir(exist_ok=True)
(directory/'native').mkdir(exist_ok=True);(directory/'amesim').mkdir(exist_ok=True)
for source,target in [(out/'baseline/full/native/result.json',directory/'native/result.json'),
*[(out/'amesim-event-output'/name,directory/'amesim'/name) for name in ('test_mql_.var','test_mql_.results')]]:
if not target.exists():os.link(source,target)
project,_,_,net,_=model('full')
audit=json.loads((PREVIOUS/'full/audit/audit.json').read_bytes())
original_reference=curves.read_ame(directory/'amesim')
times=np.array(original_reference.times);order=np.argsort(times,kind='stable')
# Amesim writes its .8 s discontinuity after an already-written grid row
# at .8000000000000009. Preserve every row/value and stable-sort by its
# actual timestamp for this diagnostic matcher; retain the permutation.
sorted_reference=replace(original_reference,times=tuple(times[order]),
series_by_data_path={k:tuple(np.asarray(v)[order]) for k,v in original_reference.series_by_data_path.items()})
save(directory/'ame-row-order.json',dict(originalTimeInversions=np.flatnonzero(np.diff(times)<0).tolist(),sortedToOriginal=order.tolist()))
with patch.object(curves,'read_ame',return_value=sorted_reference):
summary=evaluation.compare(directory,json.loads(project.read_bytes()),net,audit,evaluation.PROFILES['full'])
# Adding event outputs must preserve every original Amesim saved sample.
old=curves.read_ame(PREVIOUS/'full/reference');new=sorted_reference
oldtimes=np.array(old.times);newtimes=np.array(new.times)
indices=np.searchsorted(newtimes,oldtimes)
assert np.array_equal(newtimes[indices],oldtimes)
unchanged=all(np.array_equal(np.array(new.series(key))[indices],values) for key,values in old.series_by_data_path.items())
assert unchanged,'Amesim event printout changed original results'
summary['allOriginalAmesimRowsUnchanged']=unchanged
save(directory/'summary.json',summary)
print('event-output comparison',summary['groups']['force']['worstAbsolute'],
'above5',summary['above5PercentCount'],'unpaired',summary['phaseUnpairedGridCount'],flush=True)
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output',type=Path,default=DEFAULT)
parser.add_argument('--stage',choices=('baseline','trace','rebase','analyze','amesim-events','event-comparison','all'),default='all')
args=parser.parse_args();out=args.output.resolve();out.mkdir(parents=True,exist_ok=True)
if args.stage in ('baseline','all'):baseline(out)
if args.stage in ('trace','all'):trace(out)
if args.stage in ('rebase','all'):rebase(out)
if args.stage in ('analyze','all'):analyze(out)
if args.stage in ('amesim-events','all'):amesim_events(out)
if args.stage in ('event-comparison','all'):event_comparison(out)
if __name__=='__main__':main()
+90
View File
@@ -0,0 +1,90 @@
"""position379 only: diagnose the actual numerical tail before any real skip."""
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import argparse,hashlib,json,os,shutil,subprocess,time
import local_probe_experiment as ex
ROOT=ex.ROOT
HERE=Path(__file__).parent
BASE=ROOT/'test/local-probe-20260917/worker'
OUT=ROOT/'test/position379-tail-20260917'
def replace(s,a,b):
assert s.count(a)==1,(a,s.count(a))
return s.replace(a,b)
def prepare(trace=True,tail_only=False):
name='tail-only-trace' if tail_only else 'diag-trace' if trace else 'diag-perf'
work=OUT/name;work.mkdir(parents=True,exist_ok=True)
src={p.name:p.read_text(encoding='utf-8') for p in BASE.glob('*.c')}
hashes={k:hashlib.sha256(v.encode()).hexdigest() for k,v in src.items()}
plan=json.loads((ROOT/'test/context-fallback-20260917/plan.json').read_text(encoding='utf-8'))
s=src['model.c'];a,b,e=ex.function_span(s,'model_eval_local_internal');body=s[a:e];op=plan['code'][379][0]
assert body.count(op)==2
body=body.replace(op,'kd_scope=1;'+op+'kd_scope=0;')
body=replace(body,'return 1;}','kd_eval_exit(t,y,dy,w,1,properties,pipe_cache,jacobian);return 1;}')
src['model.c']=s[:a]+body+s[e:]
s=src['properties.c'];a,b,e=ex.function_span(s,'state_valve');body=s[a:e]
boundary=' double r=fmax(pd/p,0),critical=pow(2*g/(g+1),1/(1-g)),eff;'
body=replace(body,boundary,''' KdRecord kd_record;uint64_t kd_t=0;
if(kd_scope){uint64_t kd_lookup_start=kd_clock();kd_before(&kd_record,p,T,pd,g,rho);kd_record.lookup=kd_clock()-kd_lookup_start;
kd_t=kd_clock();
}
'''+boundary)
end=body.rfind('}')
body=body[:end]+' if(kd_scope){uint64_t kd_end=kd_clock();kd_after(&kd_record,*cm,*vel,kd_end-kd_t,1);}\n'+body[end:]
if tail_only:
# Cross-check K without key construction, lookup, or environment reads
# immediately before the timed numerical work. Post-tail records in this
# mode MUST NOT be used for memo/FP eligibility or G/H estimates.
before='uint64_t kd_lookup_start=kd_clock();kd_before(&kd_record,p,T,pd,g,rho);kd_record.lookup=kd_clock()-kd_lookup_start;'
body=replace(body,before,'')
body=replace(body,'uint64_t kd_end=kd_clock();kd_after(', 'uint64_t kd_end=kd_clock();kd_before(&kd_record,p,T,pd,g,rho);kd_record.lookup=0;kd_after(')
src['properties.c']=s[:a]+body+s[e:]
src['common.c']=replace(replace(src['common.c'],'lp_start();','lp_start();kd_start();'),'lp_finish();','lp_finish();kd_finish();')
src['cvode_solver.c']=replace(src['cvode_solver.c'],'{lp_color=-1;uint64_t start=lp_tick();','{lp_color=-1;kd_new_jac();uint64_t start=lp_tick();')
src['cvode_solver.c']=replace(src['cvode_solver.c'],'if(!result)lp_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));','if(!result){lp_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));kd_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));}')
src['valve_tail_diag.c']=(HERE/'valve_tail_diag.c').read_text(encoding='utf-8')
for name in ('model.h','local_probe.h'):shutil.copyfile(BASE/name,work/name)
shutil.copyfile(HERE/'valve_tail_diag.h',work/'valve_tail_diag.h')
cc,sun,_=ex.builder.toolchain();flags,libs,dlls,exe=ex.builder.platform_build_inputs(sun)
flags+=['-DLP_OBSERVE=0',f'-DKD_TRACE={int(trace)}']
def compile_one(item):
name,code=item;path=work/name;path.write_text('#include "valve_tail_diag.h"\n'+code,encoding='utf-8',newline='\n');obj=path.with_suffix('.o');log=[]
ex.builder._command([cc,*flags,'-I',str(work),'-I',str(ex.builder.NATIVE/'include'),'-I',str(sun/'include'),'-c',str(path),'-o',str(obj)],log=log,timeout=240)
return obj,log
with ThreadPoolExecutor(max_workers=4) as pool:objects=list(pool.map(compile_one,src.items()))
log=[];ex.builder._command([cc,*flags,*[str(o) for o,_ in objects],*ex.builder.link_library_arguments(libs),'-lm','-o',str(work/exe)],log=log)
for dll in dlls:shutil.copyfile(dll,work/dll.name)
(work/'build.log').write_text('\n'.join(sum([l for _,l in objects],[])+log),encoding='utf-8')
assert src['local_probe_support.c']==(BASE/'local_probe_support.c').read_text(encoding='utf-8')
ex.write(work/'build.json',dict(originalHashes=hashes,skip=False,trace=trace,tailOnlyTiming=tail_only,wholeContextGuardUnchanged=True,sourceHashes={p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in work.glob('*.c')}))
print('BUILT',work.name,flush=True)
def run(label,worker='diag-trace',matrices=False,disabled=False):
folder=OUT/label;folder.mkdir(parents=True,exist_ok=True);env=os.environ.copy();env['LOCAL_PROBE_MASK']='0x7ffffff'
if matrices:env['KD_MATRICES']='1'
if disabled:env['KD_DISABLED']='1'
args=[str(OUT/worker/'model.exe'),'--method','BDF','--start','0','--stop','10','--sample-step','.01','--max-step','1e30','--rtol','1e-8','--timeout','300','--sample-file',str(folder/'states.bin'),'--output-block-file',str(folder/'outputs.bin'),'--output',str(folder/'result.json')]
start=time.perf_counter()
with (folder/'stderr.log').open('wb') as f:r=subprocess.run(args,cwd=folder,env=env,stdout=subprocess.PIPE,stderr=f,timeout=330,creationflags=subprocess.CREATE_NO_WINDOW)
assert r.returncode==0,(label,r.returncode,(folder/'stderr.log').read_text(encoding='utf-8')[-4000:])
result=json.loads((folder/'result.json').read_text(encoding='utf-8'));d=json.loads((folder/'probe.json').read_text(encoding='utf-8'))
reference=json.loads((BASE.parent/'all-run-0/measurement.json').read_text(encoding='utf-8'))
keys=['success','finalState','final','propertyWarnings','acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu']
assert all(result[k]==reference[k] for k in keys),[(k,result[k],reference[k]) for k in keys if result[k]!=reference[k]]
for k in ['newtonIterations','newtonConvergenceFailures','modelCalls','groups','contextCopiedBytes','contextComparedBytes']:assert d[k]==reference['diagnostic'][k],k
hashes={}
for name in ['states','outputs','events']+(['jacobians'] if matrices else []):
hashes[name]=hashlib.sha256((folder/(name+'.bin')).read_bytes()).hexdigest()
expected=hashlib.sha256((BASE.parent/'all-audit/jacobians.bin').read_bytes()).hexdigest() if name=='jacobians' else reference[name+'Sha256']
assert hashes[name]==expected,name
data=json.loads((folder/'tail.json').read_text());assert data['jacobians']==896
record=dict(label=label,worker=worker,exact=True,hashes=hashes,counters={k:result[k] for k in keys},newtonIterations=d['newtonIterations'],newtonConvergenceFailures=d['newtonConvergenceFailures'],tail=data,wall=result['solveSeconds'],cpu=result['solveCpuSeconds'],jacobian=d.get('jacobianSeconds'),processSeconds=time.perf_counter()-start)
ex.write(folder/'validation.json',record);print(json.dumps(dict(label=label,exact=True,tail=data,wall=record['wall'],cpu=record['cpu']),ensure_ascii=True),flush=True)
return record
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('action',choices=['prepare','run']);p.add_argument('--label',default='diagnostic-0');p.add_argument('--worker',default='diag-trace');p.add_argument('--no-trace',action='store_true');p.add_argument('--tail-only',action='store_true');p.add_argument('--matrices',action='store_true');p.add_argument('--disabled',action='store_true');a=p.parse_args()
if a.action=='prepare':prepare(not a.no_trace,a.tail_only)
else:run(a.label,a.worker,a.matrices,a.disabled)
+140
View File
@@ -0,0 +1,140 @@
"""Run isolated off/MASS/LSTP/all experiments without altering production.
One fresh Amesim execution per profile supplies the common reference. Accuracy
runs retain full output. Repeated solve-only runs rotate variant order and
exclude compilation/output serialization from integration timing.
"""
from dataclasses import replace
from concurrent.futures import ThreadPoolExecutor
from contextlib import nullcontext
import argparse
import hashlib
import json
import os
from pathlib import Path
import statistics
import sys
import time
from unittest.mock import patch
ROOT=Path(__file__).resolve().parents[2]
sys.path.insert(0,str(ROOT))
from app.main import compile_system_xml_network
from app.simulation.backends import simulation_config
from app.simulation.native_codegen import build as builder, result_storage
from app.simulation.native_codegen.compiler import compile_native_program
from app.simulation.native_codegen.input import load_input
from app.simulation.native_codegen.runner import execute_native
from tests.manual import evaluate_mql8_correctness as evaluation
from tests.manual.mechanical_event_variant import event_program,prepare_runtime,MODES
def tree_hashes(root):
return {str(p.relative_to(root)):hashlib.sha256(p.read_bytes()).hexdigest()
for folder in ('native','app/simulation/native_codegen') for p in (root/folder).rglob('*')
if p.is_file() and '__pycache__' not in p.parts}
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output',type=Path,required=True)
parser.add_argument('--profiles',nargs='+',choices=('full','noncyclic'),default=['full','noncyclic'])
parser.add_argument('--repeats',type=int,default=5)
parser.add_argument('--resume',action='store_true',help='Resume completed accuracy/timing runs after a transient toolchain failure.')
parser.add_argument('--serial-build',action='store_true',help='Serialize experiment compilation to recover Windows compiler-launch failures; solve settings are unchanged.')
args=parser.parse_args();out=args.output.resolve();out.mkdir(parents=True,exist_ok=args.resume)
if args.repeats<3:raise ValueError('Use at least three repeated integration timings')
control=ROOT/'test/mechanical-events-20260917/source-control/native'
runtime=out/'experimental-native';prepare_runtime(control,runtime)
production=tree_hashes(ROOT)
evaluation.save(out/'production-before.json',production)
results=json.loads((out/'summary.json').read_bytes()) if args.resume and (out/'summary.json').exists() else {}
for profile in args.profiles:
if profile in results and all('timing' in r for r in results[profile].values()):continue
base=out/profile;base.mkdir(exist_ok=args.resume)
ame=ROOT/('tests/data/test_mql.ame' if profile=='full' else 'test/node-fixes-amesim-20260914/test_mql.ame')
project=ROOT/'test/output-semantics-20260917/after'/profile/'platform.json'
audit=(json.loads((base/'audit/audit.json').read_bytes()) if (base/'audit/audit.json').exists()
else evaluation.audit_input(ame,project,base/'audit'))
settings=evaluation.PROFILES[profile];stop,step,rtol=settings
if not (base/'reference').exists():evaluation.prepare_ame(ame,base/'reference',stop,step,rtol)
if args.resume and (base/'reference/run-summary.json').exists():
ame_run=json.loads((base/'reference/run-summary.json').read_bytes())
assert ame_run['normalTermination'] and ame_run['returncode']==0
else:
print(profile,'fresh Amesim reference',flush=True)
ame_run=evaluation.run_ame(base/'reference',Path('F:/AMESim2404/Amesim'))
xml,document=load_input(project);network=compile_system_xml_network(document)
raw_project=json.loads(project.read_bytes())
original=compile_native_program(network)
builds={};records=json.loads((base/'summary.json').read_bytes()) if args.resume and (base/'summary.json').exists() else {}
config=replace(simulation_config(document.simulation),rtol=rtol)
try:
for mode in MODES:
directory=base/mode;directory.mkdir(exist_ok=args.resume);(directory/'platform.json').write_bytes(project.read_bytes())
(directory/'platform.xml').write_bytes(xml)
reference=directory/'amesim';reference.mkdir(exist_ok=args.resume)
for name in ('test_mql_.results','test_mql_.var'):
if not (reference/name).exists():os.link(base/'reference'/name,reference/name)
program,metadata=event_program(original,network,mode)
evaluation.save(directory/'event-descriptors.json',metadata)
assert program.state_keys==original.state_keys
assert program.jacobian_structure==original.jacobian_structure
assert program.evaluation_schedule==original.evaluation_schedule
assert program.source.startswith(original.source)
for attempt in range(3):
cache=out/('build-cache' if not attempt else f'build-cache-retry-{profile}-{mode}-{time.time_ns()}')
try:
build_workers=(patch.object(builder,'ThreadPoolExecutor',lambda **kwargs: ThreadPoolExecutor(max_workers=1))
if args.serial_build else nullcontext())
with patch.object(builder,'NATIVE',control if mode=='off' else runtime),patch.object(builder,'CACHE',cache),build_workers:
print(profile,mode,'build',attempt+1,flush=True)
builds[mode]=builder.build_native(program)
break
except PermissionError as exc:
if getattr(exc,'winerror',None)!=5 or attempt==2:raise
print('Windows cache rename denied; retaining artifacts and using a fresh isolated cache.',flush=True)
if mode in records and not records[mode].get('failed'):
print(profile,mode,'retaining completed accuracy result',flush=True)
continue
print(profile,mode,'accuracy run',flush=True)
with patch.object(result_storage,'RESULT_ROOT',out/'result-storage'):
r=execute_native(builds[mode],config,step,run_dir=directory/'native',timeout=180)
native={k:v for k,v in r.items() if k not in ('series','final','finalState')}
evaluation.save(directory/'native-summary.json',native)
if not r['success']:
records[mode]=dict(nativeRun=native,failed=True)
evaluation.save(base/'summary.json',records)
raise RuntimeError(f'{profile}/{mode} failed: '+r['message'])
summary=evaluation.compare(directory,raw_project,network,audit,settings)
summary.update(nativeRun=native,amesimRun=ame_run,settings=dict(stop=stop,sampleStep=step,rtol=rtol))
records[mode]=summary;evaluation.save(base/'summary.json',records)
print(profile,mode,'accuracy done; force=',summary['groups']['force']['worstAbsolute']['maxAbsoluteError'],
'events=',native.get('experimentalEvents',{}),flush=True)
timings=json.loads((base/'timings.json').read_bytes()) if args.resume and (base/'timings.json').exists() else {mode:[] for mode in MODES}
for repeat in range(args.repeats):
order=MODES[repeat%4:]+MODES[:repeat%4]
for mode in order:
if len(timings[mode])>repeat:continue
print(profile,'timing',repeat+1,mode,flush=True)
r=execute_native(builds[mode],config,step,record_samples=False,
run_dir=base/mode/f'timing-{repeat+1}',timeout=180)
if not r['success']:raise RuntimeError(r['message'])
selected={k:v for k,v in r.items() if k in ('solveSeconds','solveCpuSeconds','processWallSeconds',
'nfev','acceptedSteps','rejectedSteps','solverStarts','stateTransitions','njev','nlu','experimentalEvents')}
timings[mode].append(selected)
evaluation.save(base/'timings.json',timings)
for mode in MODES:
values=[r['solveSeconds'] for r in timings[mode]]
records[mode]['timing']=dict(repeats=len(values),medianSeconds=statistics.median(values),
minimumSeconds=min(values),maximumSeconds=max(values),runs=timings[mode])
results[profile]=records;evaluation.save(out/'summary.json',results)
finally:
for build in builds.values():build.close()
after=tree_hashes(ROOT)
evaluation.save(out/'production-verification.json',dict(unchanged=production==after,before=production,after=after))
assert production==after,'Production code changed during the isolated experiment'
print('Completed; production source and existing numerical optimizations unchanged.',flush=True)
if __name__=='__main__':main()
+412
View File
@@ -0,0 +1,412 @@
"""Re-run audited eight-branch inputs against the AME archive's executable.
Use project Python 3.12; --output must be a new directory. Only copied time
settings change unless --align-cyclic-from-ame is explicitly supplied, which
permits only the audited UD00 cyclic flags to change in a new JSON copy.
Numeric differences are evidence, not a claim of physical validation.
"""
from __future__ import annotations
import argparse
from contextlib import redirect_stdout
from dataclasses import replace
import hashlib
import json
import os
from pathlib import Path
import re
import subprocess
import sys
import tarfile
import time
from unittest.mock import patch
import numpy as np
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from app.main import compile_system_xml_network
from app.simulation.backends import simulation_config
from app.simulation.native_codegen import build as builder, result_storage
from app.simulation.native_codegen.compiler import compile_native_program
from app.simulation.native_codegen.input import load_input
from app.simulation.native_codegen.runner import execute_native
from tests.manual import mql8_comparison as curves
from tests.manual.event_phase_comparison import pair_saved_phases
from tools import audit_test_mql8_model as auditor
# Report boundaries, not accepted engineering tolerances. Preserve absolute
# errors when the reference is small, and report the sensitivity to this choice.
EPS = dict(pressure=1., temperature=1e-6, enthalpy_flow=1., mass_flow=1e-6,
displacement=1e-9, velocity=1e-6, gap=1e-9, mass=1e-12,
volume=1e-12, piston_volume=1e-12, volume_rate=1e-12,
chamber_volume_rate=1e-12, volume_work=1., force=1e-6, signal=1e-12)
PROFILES = {'default': (10., .01, 1e-8), 'full': (50., .01, 1e-8),
'startup': (.15, .0001, 1e-8), 'startup-refined': (.15, .0001, 1e-10),
'volume-startup': (2e-6, 2e-9, 1e-10), 'noncyclic': (50., .01, 1e-8)}
def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def save(path, data):
path.write_text(json.dumps(data, ensure_ascii=False, indent=2, allow_nan=False) + '\n', encoding='utf-8')
def audit_input(ame, project, out, allow_cyclic_mismatch=False):
ame, project = ame.resolve(), project.resolve()
out.mkdir(parents=True)
with patch.object(auditor, 'HERE', out), patch.object(auditor, 'AME', ame), \
patch.object(auditor, 'INPUT', project), patch.object(sys, 'argv', ['audit', '--check']), \
(out / 'audit.log').open('w', encoding='utf-8') as log, redirect_stdout(log):
try:
auditor.main()
except AssertionError:
if not allow_cyclic_mismatch or not (out / 'audit.json').exists():
raise
evidence = json.loads((out / 'audit.json').read_bytes())
changes = evidence['parameterChanges']
if evidence['connectionChanges'] or not changes or not all(
row['parameter'] == 'iscyclic' and row['component'].startswith('amesim_ud00_')
for row in changes):
raise
return json.loads((out / 'audit.json').read_bytes())
def prepare_ame(archive_path, target, stop, step, rtol):
target.mkdir()
hashes = {}
with tarfile.open(archive_path) as archive:
for member in archive:
if not member.isfile() or member.name.endswith(('.results', '.ameperf')):
continue
path = (target / member.name).resolve()
if not path.is_relative_to(target.resolve()):
raise ValueError('Archive path outside target: ' + member.name)
path.parent.mkdir(parents=True, exist_ok=True)
data = archive.extractfile(member).read()
path.write_bytes(data)
hashes[member.name] = hashlib.sha256(data).hexdigest()
sim = target / 'test_mql_.sim'
lines = sim.read_text(encoding='ascii').splitlines()
original = lines[0].split()
fields = original[:]
fields[:5] = ['0', str(stop), str(step), '1e30', str(rtol)]
lines[0] = ' '.join(fields)
sim.write_text('\n'.join(lines) + '\n', encoding='ascii')
assert all(digest(target / name) == value for name, value in hashes.items() if name != sim.name)
save(target / 'source-verification.json', dict(archiveSha256=digest(archive_path),
originalSim=original, actualSim=fields, unchangedArchiveFiles=hashes))
def run_ame(target, ame_home):
env = dict(os.environ, AME=str(ame_home))
env['PATH'] = str(ame_home / 'win64') + os.pathsep + str(ame_home) + os.pathsep + env['PATH']
start = time.perf_counter()
with (target / 'run.log').open('wb') as log:
proc = subprocess.run([str(target / 'test_mql_.exe')], cwd=target, env=env,
stdout=log, stderr=subprocess.STDOUT, timeout=240,
creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0)
elapsed = time.perf_counter() - start
log = (target / 'run.log').read_text(encoding='utf-8', errors='replace')
cpu = re.search(r'Total CPU time:\s*([\d.eE+-]+)', log)
result = dict(returncode=proc.returncode, processWallSeconds=elapsed,
cpuSeconds=float(cpu.group(1)) if cpu else None,
normalTermination='terminated normally' in log)
save(target / 'run-summary.json', result)
if proc.returncode or not result['normalTermination']:
raise RuntimeError(log[-3000:])
result['resultsSha256'] = digest(target / 'test_mql_.results')
return result
def event_times(project, stop):
result = set()
for node in project['nodes']:
kind, p = node['data']['modelType'], node['data']['parameters']
if kind == 'amesim_step0':
result.add(float(p['time']))
elif kind == 'amesim_ud00':
durations = [float(p['t' + str(i)]) for i in range(1, int(p['nstages']) + 1)]
period = sum(durations)
cycles = range(int(stop / period) + 1) if int(p['iscyclic']) else range(1)
for cycle in cycles:
at = float(p['tstart']) + cycle * period
result.add(at)
for duration in durations:
at += duration
result.add(at)
return sorted(t for t in result if 0 < t <= stop)
def metric(actual, expected, grid, quantity, exact_events, quiet):
error = actual - expected
absolute = np.abs(error)
nonzero = np.abs(expected) > EPS[quantity]
relative = np.zeros_like(error)
relative[nonzero] = 100 * error[nonzero] / expected[nonzero]
bad = nonzero & (np.abs(relative) > 5)
near = ~nonzero
index = int(np.argmax(absolute))
def maximum(values, mask):
return float(np.max(values[mask])) if np.any(mask) else None
examples = []
for j in np.flatnonzero(bad)[np.argsort(absolute[bad])[-5:][::-1]]:
examples.append(dict(time=float(grid[j]), platform=float(actual[j]), amesim=float(expected[j]),
absoluteError=float(absolute[j]), relativePercent=float(relative[j]),
atSignalEvent=bool(exact_events[j])))
return dict(maxAbsoluteError=float(absolute[index]), worstTime=float(grid[index]),
platformAtWorst=float(actual[index]), amesimAtWorst=float(expected[index]),
rmse=float(np.sqrt(np.mean(error**2))), finalError=float(error[-1]),
referencePeak=float(np.max(np.abs(expected))),
maxRelativePercent=maximum(np.abs(relative), nonzero),
maxRelativeOutsideEvents=maximum(np.abs(relative), nonzero & ~exact_events),
maxAbsoluteOutsideEvents=maximum(absolute, ~exact_events),
quietMaxAbsolute=maximum(absolute, quiet),
nonzeroCount=int(nonzero.sum()), above5PercentCount=int(bad.sum()),
above5PercentOutsideEvents=int((bad & ~exact_events).sum()),
nearZeroCount=int(near.sum()), nearZeroMaxAbsolute=maximum(absolute, near),
nearZeroBeyondEpsilon=int((near & (absolute > EPS[quantity])).sum()),
epsilon=EPS[quantity], examples=examples,
relativeScreenSensitivity={str(factor): int(((np.abs(expected) > EPS[quantity] * factor)
& (absolute > .05 * np.abs(expected))).sum()) for factor in (.1, 1., 10.)})
def sample_grid(times, values, grid, step):
"""Use the actual saved value at the same nominal output-grid position.
Repeated floating-point additions move an Amesim output timestamp slightly
off its nominal grid. Interpolating 1e17 -> 49000 immediately before an
almost-equal endpoint invents a plateau error through cancellation. Match
only within 1e-7 of one output interval; retain that record's original side
of an event. Do not move event times, average duplicates, or smooth spikes.
"""
times = np.asarray(times)
right = np.clip(np.searchsorted(times, grid), 0, len(times) - 1)
left = np.maximum(right - 1, 0)
closest = np.where(np.abs(times[left] - grid) < np.abs(times[right] - grid), left, right)
same_output = np.abs(times[closest] - grid) <= step * 1e-7
result = np.interp(grid, times, values)
result[same_output] = np.asarray(values)[closest[same_output]]
return result
def compare(directory, project, network, audit, settings):
stop, step, rtol = settings
for node in project['nodes']:
if node['data']['modelType'] == 'amesim_ud00':
p = node['data']['parameters']
if any(float(p['start'+str(i)]) != float(p['end'+str(i)]) for i in range(1, int(p['nstages'])+1)):
raise ValueError('This baseline phase matcher requires piecewise-constant UD00 stages; ramps need explicit stage metadata.')
curves.configure(audit, network)
mapping = curves.curve_mapping()
save(directory / 'curve-mapping.json', mapping)
raw = json.loads((directory / 'native/result.json').read_bytes())
native = curves.native_curves(raw['series'])
ame = curves.read_ame(directory / 'amesim')
nt, at = native['time'], np.array(ame.times)
assert raw['success'] and raw['simulatedUntil'] == stop
assert nt[0] == at[0] == 0 and abs(at[-1] - stop) < 1e-9 and nt[-1] == stop
assert np.all(np.diff(nt) > 0) and np.all(np.diff(at) >= 0)
assert all(np.isfinite(v).all() for v in native.values())
assert all(np.isfinite(v).all() for v in ame.series_by_data_path.values())
grid = np.arange(round(stop / step) + 1) * step
events = event_times(project, stop)
exact_events = np.zeros(grid.shape, dtype=bool)
quiet = grid >= .1
for event in events:
exact_events |= np.abs(grid - event) <= 1e-9
quiet &= np.abs(grid - event) > .0200001
signal_mapping = [m for m in mapping if m['quantity'] == 'signal']
ni, ai, pairing = pair_saved_phases(nt, at,
np.column_stack([native[m['key']] for m in signal_mapping]),
np.column_stack([curves.ame_curve(ame, m) for m in signal_mapping]), grid, step)
valid = (ni >= 0) & (ai >= 0)
save(directory / 'phase-pairing.json', dict(
policy='Reference saved forcing phase; one shared row pair for all curves; no cross-event interpolation',
scope='Observed STEP/UD00 forcing phases; unregistered contact mode is not certified',
signalKeys=[m['key'] for m in signal_mapping], toleranceSeconds=step*1e-7,
gridCount=len(grid), pairedCount=int(valid.sum()), unpairedCount=int((~valid).sum()),
adjustedCount=sum(p['status'] == 'matched-other-event-side' for p in pairing), records=pairing))
if not np.any(valid):
raise ValueError('No matching saved physical phases; see phase-pairing.json')
rows, raw_rows, arrays = [], [], {'time': grid, 'phaseMatched': valid}
for m in mapping:
raw_actual = sample_grid(nt, native[m['key']], grid, step)
reference = curves.ame_curve(ame, m)
raw_expected = sample_grid(at, reference, grid, step)
raw_rows.append(m | metric(raw_actual, raw_expected, grid, m['quantity'], exact_events, quiet))
actual, expected = np.full(len(grid), np.nan), np.full(len(grid), np.nan)
actual[valid], expected[valid] = native[m['key']][ni[valid]], reference[ai[valid]]
rows.append(m | metric(actual[valid], expected[valid], grid[valid], m['quantity'], exact_events[valid], quiet[valid]))
arrays['platform|' + m['key']] = actual
arrays['amesim|' + m['key']] = expected
np.savez_compressed(directory / 'curves.npz', **arrays)
save(directory / 'raw-time-comparison.json', dict(curves=raw_rows,
above5PercentCount=sum(r['above5PercentCount'] for r in raw_rows)))
groups = {}
for quantity in sorted({r['quantity'] for r in rows}):
selected = [r for r in rows if r['quantity'] == quantity]
groups[quantity] = dict(curveCount=len(selected),
worstAbsolute=max(selected, key=lambda r: r['maxAbsoluteError']),
worstRelative=max(selected, key=lambda r: r['maxRelativePercent'] or 0),
nonzeroCount=sum(r['nonzeroCount'] for r in selected),
above5PercentCount=sum(r['above5PercentCount'] for r in selected),
above5PercentOutsideEvents=sum(r['above5PercentOutsideEvents'] for r in selected),
nearZeroBeyondEpsilon=sum(r['nearZeroBeyondEpsilon'] for r in selected))
mass_keys = [k for k in raw['series'] if k.rsplit('.', 1)[-1] in ('m', 'm1', 'm2')]
mass = sum(native[k] for k in mass_keys)
pressures = [v for k, v in native.items() if k.rsplit('.', 1)[-1] in ('p', 'p1', 'p2')]
temperatures = [v for k, v in native.items() if k.rsplit('.', 1)[-1] in ('T', 'T1', 'T2')]
physical = dict(massKeys=mass_keys, initialMassKg=float(mass[0]),
maxTotalMassDriftKg=float(np.max(np.abs(mass - mass[0]))),
relativeMassDrift=float(np.max(np.abs(mass - mass[0])) / mass[0]),
minimumGasMassKg=float(min(np.min(native[k]) for k in mass_keys)),
minimumAbsolutePressurePa=float(min(np.min(v) for v in pressures)),
minimumTemperatureK=float(min(np.min(v) for v in temperatures)),
maximumTemperatureK=float(max(np.max(v) for v in temperatures)))
event_samples = []
for event in events:
for m in mapping:
if m['quantity'] not in ('signal', 'force'):
continue
entry = dict(eventTime=event, key=m['key'])
for label, times, values in [('platform', nt, native[m['key']]),
('amesim', at, curves.ame_curve(ame, m))]:
idx = int(np.searchsorted(times, event))
entry[label] = [dict(time=float(times[i]), timeHex=float(times[i]).hex(), value=float(values[i]))
for i in range(max(0, idx-2), min(len(times), idx+3))]
event_samples.append(entry)
save(directory / 'event-samples.json', event_samples)
summary = dict(curveCount=len(rows), gridCount=len(grid), nativeSampleCount=len(nt),
phaseMatchedGridCount=int(valid.sum()), phaseUnpairedGridCount=int((~valid).sum()),
phaseAdjustedGridCount=sum(p['status'] == 'matched-other-event-side' for p in pairing),
rawTimeAbove5PercentCount=sum(r['above5PercentCount'] for r in raw_rows),
amesimSampleCount=len(at), platformOutputCount=len(raw['series']) - 1, allFinite=True,
signalEvents=events, physical=physical, groups=groups, curves=rows,
extraEventPointMaxContactForce=max(float(np.max(np.abs(native[m['key']])))
for m in mapping if m['quantity'] == 'force'),
above5PercentCurveCount=sum(r['above5PercentCount'] > 0 for r in rows),
above5PercentCount=sum(r['above5PercentCount'] for r in rows),
above5PercentOutsideEvents=sum(r['above5PercentOutsideEvents'] for r in rows))
save(directory / 'comparison.json', summary)
return {k: v for k, v in summary.items() if k != 'curves'}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', type=Path, required=True)
parser.add_argument('--ame', type=Path, default=ROOT / 'tests/data/test_mql.ame')
parser.add_argument('--project', type=Path, default=ROOT / 'tests/data/test-mql-8-corrected.json')
parser.add_argument('--ame-home', type=Path, required=True)
parser.add_argument('--noncyclic-ame', type=Path)
parser.add_argument('--noncyclic-project', type=Path)
parser.add_argument('--align-cyclic-from-ame', action='store_true')
parser.add_argument('--analyze-only', action='store_true', help='Reanalyze existing raw results without solving.')
parser.add_argument('--profiles', nargs='+', choices=tuple(PROFILES), default=list(PROFILES))
parser.add_argument('--native-source', type=Path, help='Frozen native source tree for a controlled before/after run.')
args = parser.parse_args()
if args.native_source:
builder.NATIVE = args.native_source.resolve()
out = args.output.resolve()
if args.analyze_only:
summaries = json.loads((out / 'summary.json').read_bytes())
for name, old in list(summaries.items()):
directory = out / name
project = json.loads((directory / 'platform.json').read_bytes())
_, document = load_input(directory / 'platform.json')
audit_name = 'audit-noncyclic' if name == 'noncyclic' else 'audit-aligned'
audit = json.loads((out / audit_name / 'audit.json').read_bytes())
summary = compare(directory, project, compile_system_xml_network(document), audit, PROFILES[name])
summary.update({key: old[key] for key in ('settings', 'amesimRun', 'nativeRun')})
summaries[name] = summary
print(name, 'reanalyzed', summary['above5PercentCount'], flush=True)
save(out / 'summary.json', summaries)
manifest = json.loads((out / 'manifest.json').read_bytes())
manifest['interpolation'] = 'Same saved forcing phase within 1e-7 output intervals. Unpaired points explicitly reported; raw time-only metrics retained.'
save(out / 'manifest.json', manifest)
return
out.mkdir(parents=True, exist_ok=False)
if bool(args.noncyclic_ame) != bool(args.noncyclic_project):
parser.error('Both noncyclic input paths are required together.')
inputs = [args.ame, args.project] + ([args.noncyclic_ame, args.noncyclic_project] if args.noncyclic_ame else [])
tracked = subprocess.check_output(['git', 'ls-files', 'app', 'native', 'frontend/src'], cwd=ROOT, text=True).splitlines()
sources = {str(p.resolve()): digest(p) for p in inputs}
code = {p: digest(ROOT / p) for p in tracked if (ROOT / p).is_file()}
manifest = dict(gitCommit=subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=ROOT, text=True).strip(),
python=sys.version, sources=sources, productionSources=code, epsilons=EPS,
criterion='5% is a diagnostic screen, not an approved engineering tolerance',
freshAmesimExecution=True, amesimExecutable='Extracted from source AME; not recompiled',
nativeSource=str(builder.NATIVE),
interpolation='Same saved forcing phase within 1e-7 output intervals. Unpaired points explicitly reported; raw time-only metrics retained.')
save(out / 'manifest.json', manifest)
original_audit = audit_input(args.ame, args.project, out / 'audit-original', args.align_cyclic_from_ame)
if original_audit['parameterChanges']:
aligned = json.loads(args.project.read_bytes())
nodes = {node['id']: node for node in aligned['nodes']}
for row in original_audit['parameterChanges']:
nodes[row['component']]['data']['parameters'][row['parameter']] = row['expected']
aligned_path = out / 'aligned-source.json'
save(aligned_path, aligned)
save(out / 'alignment-changes.json', original_audit['parameterChanges'])
args.project = aligned_path
audits = {'standard': audit_input(args.ame, args.project, out / 'audit-aligned')}
if args.noncyclic_ame:
audits['noncyclic'] = audit_input(args.noncyclic_ame, args.noncyclic_project, out / 'audit-noncyclic')
summaries = {}
for name, settings in PROFILES.items():
if name not in args.profiles:
continue
if name == 'noncyclic' and not args.noncyclic_ame:
continue
stop, step, rtol = settings
ame_path = args.noncyclic_ame if name == 'noncyclic' else args.ame
project_path = args.noncyclic_project if name == 'noncyclic' else args.project
audit = audits['noncyclic' if name == 'noncyclic' else 'standard']
directory = out / name
directory.mkdir()
project = json.loads(project_path.read_bytes())
project['simulation'].update(t_start=0., t_stop=stop, step=step, max_step=1e30, method='BDF')
save(directory / 'platform.json', project)
prepare_ame(ame_path, directory / 'amesim', stop, step, rtol)
print(name, 'Amesim running', flush=True)
ame_summary = run_ame(directory / 'amesim', args.ame_home)
xml, document = load_input(directory / 'platform.json')
(directory / 'platform.xml').write_bytes(xml)
network = compile_system_xml_network(document)
program = compile_native_program(network)
print(name, 'current platform running', flush=True)
started = time.perf_counter()
# Isolate cache and result quotas from the user's live application.
with patch.object(builder, 'CACHE', out / 'build-cache'), \
patch.object(result_storage, 'RESULT_ROOT', out / 'result-storage'):
build = builder.build_native(program)
try:
result = execute_native(build, replace(simulation_config(document.simulation), rtol=rtol),
step, run_dir=directory / 'native', timeout=240)
native_summary = {k: v for k, v in result.items() if k not in ('series', 'final', 'finalState')}
native_summary.update(buildSeconds=build.seconds, buildCacheHit=build.cache_hit,
buildKey=build.manifest['buildKey'], pipelineWallSeconds=time.perf_counter()-started,
stateKeys=program.state_keys)
save(directory / 'native-summary.json', native_summary)
finally:
build.close()
summary = compare(directory, project, network, audit, settings)
summary.update(settings=dict(stop=stop, sampleStep=step, rtol=rtol),
amesimRun=ame_summary, nativeRun=native_summary)
summaries[name] = summary
save(out / 'summary.json', summaries)
print(name, 'completed', 'pressure=', summary['groups']['pressure']['worstAbsolute']['maxAbsoluteError'],
'temperature=', summary['groups']['temperature']['worstAbsolute']['maxAbsoluteError'],
'above5%=', summary['above5PercentCount'], flush=True)
unchanged = all(digest(Path(p)) == h for p, h in sources.items())
unchanged_code = all(digest(ROOT / p) == h for p, h in code.items())
save(out / 'source-verification.json', dict(inputsUnchanged=unchanged, productionUnchanged=unchanged_code,
checkedProductionFiles=len(code)))
assert unchanged and unchanged_code
print('All profiles completed; original inputs and production sources unchanged.', flush=True)
if __name__ == '__main__':
main()
+49
View File
@@ -0,0 +1,49 @@
"""Pair saved samples by forcing phase, never by the size of output errors.
All curves share ONE pair of row indices. No interpolation across jumps, no
time shifting, and no use of force/pressure agreement to select a sample.
The reference's nearest saved grid row is authoritative; a different native
row is allowed only in the tiny output-timestamp roundoff window.
"""
from __future__ import annotations
import numpy as np
def pair_saved_phases(native_times, reference_times, native_signals, reference_signals, grid, step):
nt, rt, grid = map(np.asarray, (native_times, reference_times, grid))
ns, rs = map(np.asarray, (native_signals, reference_signals))
if ns.shape != (len(nt), rs.shape[1]) or len(rs) != len(rt):
raise ValueError('Phase signatures must be rows by the same signal columns.')
tolerance = step * 1e-7
ni = np.full(len(grid), -1, dtype=int)
ri = np.full(len(grid), -1, dtype=int)
records = []
for j, t in enumerate(grid):
candidates = np.arange(np.searchsorted(nt, t-tolerance, side='left'),
np.searchsorted(nt, t+tolerance, side='right'))
references = np.arange(np.searchsorted(rt, t-tolerance, side='left'),
np.searchsorted(rt, t+tolerance, side='right'))
if not len(candidates) or not len(references):
records.append(dict(gridIndex=j, time=float(t), status='missing-saved-sample'))
continue
# Prefer the later row on an exact tie / duplicate timestamp, matching
# the comparison's existing right-side duplicate policy.
ref = min(references, key=lambda i: (abs(rt[i]-t), -int(i)))
closest = min(candidates, key=lambda i: (abs(nt[i]-t), -int(i)))
matches = candidates[np.all(np.isclose(ns[candidates], rs[ref], rtol=1e-12, atol=1e-12), axis=1)]
ri[j] = ref
if not len(matches):
records.append(dict(gridIndex=j, time=float(t), status='unmatched-forcing-phase',
referenceTime=float(rt[ref]), referenceSignals=rs[ref].tolist(),
nativeCandidateTimes=nt[candidates].tolist(),
nativeCandidateSignals=ns[candidates].tolist()))
continue
chosen = min(matches, key=lambda i: (abs(nt[i]-t), -int(i)))
ni[j] = chosen
if chosen != closest:
records.append(dict(gridIndex=j, time=float(t), status='matched-other-event-side',
platformTime=float(nt[chosen]), referenceTime=float(rt[ref]),
originalPlatformTime=float(nt[closest]),
signals=ns[chosen].tolist(), referenceSignals=rs[ref].tolist()))
return ni, ri, records
@@ -0,0 +1,618 @@
# 全部 fallback 区间及预算
由 `analyze_fallback_profitability.py` 从历史数据生成。ID、group、position 为0基,范围为[start,end)。ms为完整896个Jacobian折算累计;µs为每次fallback。按累计原计算时间排序。
P=probe总开销,H=每个Jacobian新增baseline捕获,m=结构上最多共享的失败组数。盈利要求 P+H/m<C;Hmax=m(C-P)。所有预算是严格上限,不代表实际可达到;负数表示不可能。m不是已验证成功次数,若只能组内独立捕获则m=1。
原区间时间、op时间和function时间来自不同抽样运行,不相加;纯代数是完全不含native调用的operation计时,native内部代数耗时未知。完整JSON另含全部5157条group/position映射、三轮原始折算值及函数表。
## 115个失败区间:成本和组成
| R / 范围 | group | 次数 | 累计ms | 均值µs / 三轮min–max | ops/native | op累计ms / 纯代数ms | 主要native | 主要function(exclusive排序) | m / 共享 | 选择 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| R490 [49,184) | 24,25 | 1792 | 33.8066 | 18.865 / 18.618–19.236 | 135/15 | 34.5129 / 4.1388 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A;D仅融合区间的待证假设 |
| R485 [50,181) | 22,23 | 1792 | 31.6665 | 17.671 / 17.360–18.206 | 131/14 | 32.1117 / 4.0718 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A;D仅融合区间的待证假设 |
| R480 [51,178) | 20,21 | 1792 | 30.2693 | 16.891 / 16.279–17.565 | 127/13 | 30.2065 / 3.9538 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A;D仅融合区间的待证假设 |
| R475 [52,175) | 18,19 | 1792 | 27.2575 | 15.211 / 15.121–15.350 | 123/12 | 28.1366 / 3.8413 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R470 [53,172) | 16,17 | 1792 | 25.3815 | 14.164 / 14.078–14.304 | 119/11 | 25.6504 / 3.7122 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R465 [54,169) | 14,15 | 1792 | 22.9364 | 12.799 / 12.606–13.072 | 115/10 | 23.6220 / 3.6083 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R460 [55,166) | 12,13 | 1792 | 22.1173 | 12.342 / 11.636–13.553 | 111/9 | 21.8736 / 3.5983 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R3 [30,48) | 0 | 896 | 21.3933 | 23.876 / 23.438–24.421 | 18/17 | 18.3418 / 0.0327 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A;D仅融合区间的待证假设 |
| R455 [56,161) | 10,11 | 1792 | 20.7353 | 11.571 / 11.279–12.104 | 105/8 | 20.2934 / 3.6539 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R457 [297,444) | 10,11 | 1792 | 15.8804 | 8.862 / 8.421–9.626 | 147/4 | 14.8738 / 5.6530 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R49 [373,444) | 0 | 896 | 15.7818 | 17.614 / 16.941–18.203 | 71/4 | 6.5255 / 1.8142 | native_medium_orifice_context | state_valve,property_pt,native_temperature_ph_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R5 [86,111) | 0,1 | 1792 | 14.7128 | 8.210 / 7.906–8.560 | 25/5 | 11.3355 / 0.8709 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R58 [46,80) | 1 | 896 | 13.5515 | 15.124 / 14.891–15.414 | 34/10 | 10.9583 / 0.5253 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R487 [321,456) | 22,23 | 1792 | 13.2545 | 7.396 / 6.833–8.073 | 135/4 | 13.2774 / 4.6048 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R477 [313,452) | 18,19 | 1792 | 13.1702 | 7.349 / 7.103–7.826 | 139/4 | 13.5595 / 4.7114 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R462 [301,446) | 12,13 | 1792 | 13.0502 | 7.282 / 7.100–7.603 | 145/4 | 13.7101 / 4.9268 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R492 [325,458) | 24,25 | 1792 | 12.9209 | 7.210 / 7.078–7.329 | 133/4 | 13.3904 / 4.6050 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R467 [305,448) | 14,15 | 1792 | 12.8537 | 7.173 / 6.967–7.424 | 143/4 | 13.8231 / 4.9066 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R472 [309,450) | 16,17 | 1792 | 12.7963 | 7.141 / 7.027–7.212 | 141/4 | 13.6274 / 4.7731 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R482 [317,454) | 20,21 | 1792 | 12.7099 | 7.093 / 6.945–7.229 | 137/4 | 13.5151 / 4.7408 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R2 [20,26) | 0,1 | 1792 | 11.0505 | 6.167 / 6.118–6.228 | 6/5 | 9.9409 / 0.0493 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_jacobian_scalar_get | 2 / 条件式 | C/E预算筛选;native默认A |
| R424 [43,53) | 9 | 896 | 10.5664 | 11.793 / 11.673–11.977 | 10/10 | 10.8827 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R385 [56,88) | 8,9 | 1792 | 9.5786 | 5.345 / 5.320–5.379 | 32/4 | 9.5279 / 0.9872 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R96 [88,108) | 2,3 | 1792 | 9.2071 | 5.138 / 4.932–5.448 | 20/4 | 8.6807 / 0.5910 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R423 [32,41) | 9 | 896 | 9.2056 | 10.274 / 10.058–10.521 | 9/9 | 9.2194 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R296 [54,89) | 6 | 896 | 8.0238 | 8.955 / 8.928–8.994 | 35/7 | 8.0117 / 0.5007 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R139 [24,35) | 3 | 896 | 7.0459 | 7.864 / 7.746–8.008 | 11/7 | 7.2081 / 0.0871 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R94 [50,80) | 2 | 896 | 6.7602 | 7.545 / 7.516–7.592 | 30/6 | 6.6132 / 0.4611 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R192 [47,50) | 4,8 | 1792 | 6.5404 | 3.650 / 3.604–3.731 | 3/3 | 6.4293 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R144 [51,80) | 3 | 896 | 6.0869 | 6.793 / 6.430–7.421 | 29/5 | 5.5535 / 0.4702 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R342 [55,88) | 7 | 896 | 5.8420 | 6.520 / 6.371–6.627 | 33/5 | 5.7814 / 0.4929 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R89 [8,18) | 2 | 896 | 5.6644 | 6.322 / 6.209–6.498 | 10/6 | 5.4385 / 0.1128 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_jacobian_scalar_get | 1 / 条件式 | C/E预算筛选;native默认A |
| R74 [389,397) | 1,4,6,7 | 3584 | 5.5372 | 1.545 / 1.515–1.593 | 8/1 | 5.0806 / 0.5075 | native_medium_orifice_context | state_valve,property_pt,native_temperature_ph_context | 4 / 条件式 | C/E预算筛选;native默认A |
| R136 [6,17) | 3 | 896 | 5.4670 | 6.102 / 5.978–6.196 | 11/6 | 5.2067 / 0.1092 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R77 [402,410) | 1,2,6,7 | 3584 | 5.3570 | 1.495 / 1.473–1.534 | 8/1 | 5.4196 / 0.5316 | native_medium_orifice_context | state_valve,property_pt,native_temperature_ph_context | 4 / 条件式 | C/E预算筛选;native默认A |
| R420 [12,20) | 9 | 896 | 5.3023 | 5.918 / 5.827–6.073 | 8/6 | 5.4184 / 0.0345 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_jacobian_scalar_get | 1 / 条件式 | C/E预算筛选;native默认A |
| R336 [14,21) | 7 | 896 | 5.2899 | 5.904 / 5.798–5.958 | 7/6 | 5.2751 / 0.0181 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_jacobian_scalar_get | 1 / 条件式 | C/E预算筛选;native默认A |
| R381 [28,35) | 8 | 896 | 5.0225 | 5.605 / 5.552–5.662 | 7/5 | 5.0696 / 0.0357 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R141 [40,42) | 3,4 | 1792 | 4.9690 | 2.773 / 2.347–3.588 | 2/2 | 4.4399 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 2 / 条件式 | C/E预算筛选;native默认A |
| R295 [49,51) | 6,7 | 1792 | 4.5347 | 2.531 / 2.468–2.568 | 2/2 | 4.3244 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R57 [42,44) | 1,2 | 1792 | 4.4972 | 2.510 / 2.475–2.531 | 2/2 | 4.5279 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R379 [14,20) | 8 | 896 | 4.3643 | 4.871 / 4.820–4.952 | 6/5 | 4.2811 / 0.0170 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_jacobian_scalar_get | 1 / 条件式 | C/E预算筛选;native默认A |
| R56 [38,40) | 1,2 | 1792 | 4.3340 | 2.419 / 2.349–2.529 | 2/2 | 4.1204 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 2 / 条件式 | C/E预算筛选;native默认A |
| R193 [51,55) | 4 | 896 | 4.2716 | 4.767 / 4.536–5.218 | 4/4 | 4.1530 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R294 [43,47) | 6 | 896 | 4.2505 | 4.744 / 4.720–4.772 | 4/4 | 4.2438 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R91 [26,33) | 2 | 896 | 4.1915 | 4.678 / 4.591–4.850 | 7/4 | 4.0233 / 0.0780 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_jacobian_scalar_get | 1 / 条件式 | C/E预算筛选;native默认A |
| R383 [41,45) | 8 | 896 | 4.1668 | 4.650 / 4.609–4.716 | 4/4 | 4.1610 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R339 [39,43) | 7 | 896 | 4.1581 | 4.641 / 4.603–4.712 | 4/4 | 4.1346 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R237 [35,39) | 5 | 896 | 3.9920 | 4.455 / 4.437–4.471 | 4/4 | 4.0420 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R190 [34,38) | 4 | 896 | 3.9683 | 4.429 / 4.385–4.486 | 4/4 | 3.9788 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R234 [23,26) | 5,8 | 1792 | 3.9382 | 2.198 / 2.182–2.222 | 3/2 | 3.8424 / 0.0353 | native_medium_orifice_context,native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 2 / 条件式 | C/E预算筛选;native默认A |
| R80 [415,423) | 1,2,3 | 2688 | 3.8738 | 1.441 / 1.365–1.567 | 8/1 | 3.5162 / 0.4025 | native_medium_orifice_context | state_valve,property_pt,native_medium_orifice_context | 3 / 条件式 | C/E预算筛选;native默认A |
| R242 [55,84) | 5 | 896 | 3.7439 | 4.178 / 4.171–4.190 | 29/3 | 3.7042 / 0.4658 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R233 [17,21) | 5 | 896 | 3.5485 | 3.960 / 3.946–3.979 | 4/4 | 3.5745 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_pipe_flow_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R232 [8,16) | 5 | 896 | 3.4362 | 3.835 / 3.790–3.892 | 8/4 | 3.3525 / 0.0677 | native_medium_orifice_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R195 [88,91) | 4 | 896 | 3.0152 | 3.365 / 3.330–3.416 | 3/3 | 2.9500 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R421 [21,24) | 9 | 896 | 2.9554 | 3.298 / 3.220–3.414 | 3/3 | 2.8324 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R189 [26,32) | 4 | 896 | 2.9367 | 3.278 / 3.224–3.347 | 6/3 | 2.8788 / 0.0538 | native_medium_orifice_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R186 [8,14) | 4 | 896 | 2.6459 | 2.953 / 2.901–3.055 | 6/3 | 2.5282 / 0.0518 | native_medium_orifice_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R225 [402,423) | 4 | 896 | 2.5916 | 2.892 / 2.854–2.922 | 21/2 | 2.5976 / 0.3629 | native_medium_orifice_context | state_valve,property_pt,native_temperature_ph_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R1 [18,19) | 0,1 | 1792 | 2.3721 | 1.324 / 1.290–1.377 | 1/1 | 1.8634 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,native_pipe_flow_context,local_isentropic | 2 / 条件式 | A |
| R291 [33,34) | 6,7 | 1792 | 2.3257 | 1.298 / 1.249–1.363 | 1/1 | 2.1592 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 2 / 条件式 | A |
| R287 [10,14) | 6 | 896 | 2.2976 | 2.564 / 2.004–3.618 | 4/2 | 1.7574 / 0.0339 | native_medium_orifice_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R95 [84,86) | 2,4 | 1792 | 2.2118 | 1.234 / 1.205–1.274 | 2/1 | 2.0616 / 0.0426 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | C/E预算筛选;native默认A |
| R238 [41,43) | 5 | 896 | 2.2003 | 2.456 / 2.370–2.522 | 2/2 | 2.0701 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R142 [44,46) | 3 | 896 | 2.1845 | 2.438 / 2.411–2.455 | 2/2 | 2.3678 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R93 [46,48) | 2 | 896 | 2.1591 | 2.410 / 2.387–2.439 | 2/2 | 2.1160 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R293 [39,41) | 6 | 896 | 2.1526 | 2.402 / 2.387–2.423 | 2/2 | 2.0647 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R292 [36,37) | 6,7 | 1792 | 2.1458 | 1.197 / 1.193–1.200 | 1/1 | 2.1295 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | A |
| R340 [45,47) | 7 | 896 | 2.1315 | 2.379 / 2.367–2.400 | 2/2 | 2.1093 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R382 [37,39) | 8 | 896 | 2.0939 | 2.337 / 2.313–2.352 | 2/2 | 2.0737 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R241 [53,54) | 5,8 | 1792 | 2.0765 | 1.159 / 1.156–1.161 | 1/1 | 2.0091 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 2 / 条件式 | A |
| R290 [24,28) | 6 | 896 | 2.0487 | 2.286 / 2.234–2.331 | 4/2 | 1.9819 / 0.0359 | native_medium_orifice_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R143 [48,49) | 3,5 | 1792 | 2.0442 | 1.141 / 1.133–1.150 | 1/1 | 1.9807 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 2 / 条件式 | A |
| R138 [22,23) | 3,6 | 1792 | 2.0032 | 1.118 / 1.057–1.229 | 1/1 | 1.8615 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 2 / 条件式 | A |
| R338 [26,30) | 7 | 896 | 1.9554 | 2.182 / 2.170–2.199 | 4/2 | 1.9396 / 0.0353 | native_medium_orifice_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R55 [34,36) | 1 | 896 | 1.9532 | 2.180 / 2.159–2.204 | 2/2 | 1.9671 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R426 [91,123) | 9 | 896 | 1.9529 | 2.180 / 2.141–2.225 | 32/1 | 1.6566 / 0.6333 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R90 [21,23) | 2 | 896 | 1.9157 | 2.138 / 2.095–2.207 | 2/2 | 1.9115 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_jacobian_scalar_get | 1 / 条件式 | C/E预算筛选;native默认A |
| R188 [20,22) | 4 | 896 | 1.8858 | 2.105 / 2.069–2.152 | 2/2 | 1.8282 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_pipe_flow_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R178 [362,384) | 3 | 896 | 1.8337 | 2.047 / 1.982–2.093 | 22/1 | 1.6246 / 0.4219 | native_medium_orifice_context | state_valve,native_temperature_ph_context,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R289 [19,21) | 6 | 896 | 1.8062 | 2.016 / 1.985–2.041 | 2/2 | 1.7858 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_pipe_flow_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R129 [369,389) | 2 | 896 | 1.7436 | 1.946 / 1.904–1.983 | 20/1 | 1.5722 / 0.4017 | native_medium_orifice_context | state_valve,native_temperature_ph_context,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R185 [2,6) | 4 | 896 | 1.7273 | 1.928 / 1.917–1.936 | 4/2 | 1.6485 / 0.0434 | native_medium_orifice_context | state_valve,native_medium_orifice_context,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R194 [56,82) | 4 | 896 | 1.6724 | 1.866 / 1.840–1.880 | 26/1 | 1.6574 / 0.4726 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R224 [369,384) | 4 | 896 | 1.6049 | 1.791 / 1.720–1.827 | 15/1 | 1.4050 / 0.2447 | native_medium_orifice_context | state_valve,native_temperature_ph_context,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R71 [373,384) | 1 | 896 | 1.5497 | 1.730 / 1.685–1.788 | 11/1 | 1.4107 / 0.1898 | native_medium_orifice_context | state_valve,native_temperature_ph_context,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R243 [91,109) | 5 | 896 | 1.5234 | 1.700 / 1.626–1.806 | 18/1 | 1.3512 / 0.3231 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R179 [389,402) | 3 | 896 | 1.4651 | 1.635 / 1.605–1.660 | 13/1 | 1.4121 / 0.2499 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R279 [375,384) | 5 | 896 | 1.4309 | 1.597 / 1.582–1.614 | 9/1 | 1.3503 / 0.1494 | native_medium_orifice_context | state_valve,native_temperature_ph_context,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R281 [415,428) | 5 | 896 | 1.4079 | 1.571 / 1.522–1.650 | 13/1 | 1.3219 / 0.2408 | native_medium_orifice_context | state_valve,native_temperature_ph_context,native_medium_orifice_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R239 [45,46) | 5 | 896 | 1.2200 | 1.362 / 1.319–1.447 | 1/1 | 1.1263 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | A |
| R145 [82,84) | 3 | 896 | 1.2033 | 1.343 / 1.329–1.361 | 2/1 | 1.1604 / 0.0232 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R384 [51,52) | 8 | 896 | 1.1141 | 1.243 / 1.219–1.280 | 1/1 | 1.0539 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | A |
| R240 [50,51) | 5 | 896 | 1.1029 | 1.231 / 1.222–1.241 | 1/1 | 1.0630 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | A |
| R88 [4,6) | 2 | 896 | 1.1024 | 1.230 / 1.206–1.246 | 2/1 | 0.9525 / 0.0296 | native_medium_orifice_context | state_valve,native_temperature_ph_context,native_medium_orifice_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R341 [52,53) | 7 | 896 | 1.0995 | 1.227 / 1.187–1.302 | 1/1 | 1.0267 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | A |
| R140 [37,38) | 3 | 896 | 1.0528 | 1.175 / 1.165–1.184 | 1/1 | 1.0092 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_pipe_flow_context | 1 / 条件式 | A |
| R54 [30,32) | 1 | 896 | 1.0525 | 1.175 / 1.161–1.194 | 2/1 | 1.3184 / 0.0187 | native_medium_orifice_context | state_valve,native_temperature_ph_context,native_medium_orifice_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R386 [90,91) | 8 | 896 | 1.0474 | 1.169 / 1.142–1.208 | 1/1 | 1.0043 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | A |
| R337 [23,24) | 7 | 896 | 1.0466 | 1.168 / 1.107–1.288 | 1/1 | 0.9678 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,native_temperature_ph_context,local_isentropic | 1 / 条件式 | A |
| R191 [44,45) | 4 | 896 | 1.0381 | 1.159 / 1.153–1.167 | 1/1 | 0.9958 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,native_pipe_flow_context,local_isentropic | 1 / 条件式 | A |
| R425 [54,55) | 9 | 896 | 1.0337 | 1.154 / 1.142–1.164 | 1/1 | 0.9993 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | A |
| R343 [89,90) | 7 | 896 | 1.0302 | 1.150 / 1.139–1.157 | 1/1 | 1.0029 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | A |
| R236 [32,33) | 5 | 896 | 1.0186 | 1.137 / 1.127–1.145 | 1/1 | 0.9803 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | A |
| R422 [26,28) | 9 | 896 | 0.9927 | 1.108 / 1.101–1.117 | 2/1 | 0.9591 / 0.0169 | native_medium_orifice_context | state_valve,property_pt,local_isentropic | 1 / 条件式 | C/E预算筛选;native默认A |
| R235 [28,30) | 5 | 896 | 0.9892 | 1.104 / 1.098–1.112 | 2/1 | 0.9939 / 0.0166 | native_medium_orifice_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | C/E预算筛选;native默认A |
| R92 [35,36) | 2 | 896 | 0.9890 | 1.104 / 1.091–1.116 | 1/1 | 0.9631 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | A |
| R380 [21,22) | 8 | 896 | 0.9846 | 1.099 / 1.082–1.117 | 1/1 | 0.9415 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,property_pt | 1 / 条件式 | A |
| R137 [19,20) | 3 | 896 | 0.9560 | 1.067 / 1.057–1.081 | 1/1 | 0.9219 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_pipe_flow_context | 1 / 条件式 | A |
| R187 [17,18) | 4 | 896 | 0.9394 | 1.048 / 1.024–1.061 | 1/1 | 0.8927 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_pipe_flow_context | 1 / 条件式 | A |
| R335 [10,12) | 7 | 896 | 0.9204 | 1.027 / 1.021–1.032 | 2/1 | 0.8715 / 0.0167 | native_medium_orifice_context | state_valve,native_temperature_ph_context,native_medium_orifice_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R288 [16,17) | 6 | 896 | 0.9177 | 1.024 / 1.010–1.041 | 1/1 | 0.8781 / 0.0000 | native_pipe_flow_cached_context,native_temperature_ph_context | state_valve,local_isentropic,native_pipe_flow_context | 1 / 条件式 | A |
| R286 [6,8) | 6 | 896 | 0.9080 | 1.013 / 1.005–1.021 | 2/1 | 0.8447 / 0.0194 | native_medium_orifice_context | state_valve,native_medium_orifice_context,native_temperature_ph_context | 1 / 条件式 | C/E预算筛选;native默认A |
| R231 [2,4) | 5 | 896 | 0.8181 | 0.913 / 0.893–0.949 | 2/1 | 0.7453 / 0.0184 | native_medium_orifice_context | state_valve,native_medium_orifice_context,property_density | 1 / 条件式 | C/E预算筛选;native默认A |
## 每个区间的break-even边界
Ptyped、Htyped取R288最新批次中位数,仅作成本量级情景。区间预算假设一套融合机制处理整个区间,不能按每个native重复付P后仍使用本预算。
| R | Cµs | m | Hmax(P=0)µs | Hmax(P=2/5/10)µs | Pmax(Htyped)µs | Ptyped,H=0可行 | Ptyped,Htyped可行 | op模式也支持该情景 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 490 | 18.865 | 2 | 37.731 | 33.731/27.731/17.731 | 9.736 | True | True | True |
| 485 | 17.671 | 2 | 35.342 | 31.342/25.342/15.342 | 8.542 | True | True | True |
| 480 | 16.891 | 2 | 33.783 | 29.783/23.783/13.783 | 7.763 | True | True | True |
| 475 | 15.211 | 2 | 30.421 | 26.421/20.421/10.421 | 6.082 | True | False | False |
| 470 | 14.164 | 2 | 28.328 | 24.328/18.328/8.328 | 5.035 | True | False | False |
| 465 | 12.799 | 2 | 25.599 | 21.599/15.599/5.599 | 3.671 | True | False | False |
| 460 | 12.342 | 2 | 24.685 | 20.685/14.685/4.685 | 3.213 | True | False | False |
| 3 | 23.876 | 1 | 23.876 | 21.876/18.876/13.876 | 5.619 | True | False | False |
| 455 | 11.571 | 2 | 23.142 | 19.142/13.142/3.142 | 2.442 | True | False | False |
| 457 | 8.862 | 2 | 17.724 | 13.724/7.724/-2.276 | -0.267 | True | False | False |
| 49 | 17.614 | 1 | 17.614 | 15.614/12.614/7.614 | -0.644 | True | False | False |
| 5 | 8.210 | 2 | 16.420 | 12.420/6.420/-3.580 | -0.919 | True | False | False |
| 58 | 15.124 | 1 | 15.124 | 13.124/10.124/5.124 | -3.133 | True | False | False |
| 487 | 7.396 | 2 | 14.793 | 10.793/4.793/-5.207 | -1.732 | True | False | False |
| 477 | 7.349 | 2 | 14.699 | 10.699/4.699/-5.301 | -1.779 | True | False | False |
| 462 | 7.282 | 2 | 14.565 | 10.565/4.565/-5.435 | -1.846 | True | False | False |
| 492 | 7.210 | 2 | 14.421 | 10.421/4.421/-5.579 | -1.919 | True | False | False |
| 467 | 7.173 | 2 | 14.346 | 10.346/4.346/-5.654 | -1.956 | True | False | False |
| 472 | 7.141 | 2 | 14.282 | 10.282/4.282/-5.718 | -1.988 | True | False | False |
| 482 | 7.093 | 2 | 14.185 | 10.185/4.185/-5.815 | -2.036 | False | False | False |
| 2 | 6.167 | 2 | 12.333 | 8.333/2.333/-7.667 | -2.962 | False | False | False |
| 424 | 11.793 | 1 | 11.793 | 9.793/6.793/1.793 | -6.465 | True | False | False |
| 385 | 5.345 | 2 | 10.690 | 6.690/0.690/-9.310 | -3.784 | False | False | False |
| 96 | 5.138 | 2 | 10.276 | 6.276/0.276/-9.724 | -3.991 | False | False | False |
| 423 | 10.274 | 1 | 10.274 | 8.274/5.274/0.274 | -7.984 | True | False | False |
| 296 | 8.955 | 1 | 8.955 | 6.955/3.955/-1.045 | -9.303 | True | False | False |
| 139 | 7.864 | 1 | 7.864 | 5.864/2.864/-2.136 | -10.394 | True | False | False |
| 94 | 7.545 | 1 | 7.545 | 5.545/2.545/-2.455 | -10.713 | True | False | False |
| 192 | 3.650 | 2 | 7.300 | 3.300/-2.700/-12.700 | -5.479 | False | False | False |
| 144 | 6.793 | 1 | 6.793 | 4.793/1.793/-3.207 | -11.464 | False | False | False |
| 342 | 6.520 | 1 | 6.520 | 4.520/1.520/-3.480 | -11.738 | False | False | False |
| 89 | 6.322 | 1 | 6.322 | 4.322/1.322/-3.678 | -11.936 | False | False | False |
| 74 | 1.545 | 4 | 6.180 | -1.820/-13.820/-33.820 | -3.019 | False | False | False |
| 136 | 6.102 | 1 | 6.102 | 4.102/1.102/-3.898 | -12.156 | False | False | False |
| 77 | 1.495 | 4 | 5.979 | -2.021/-14.021/-34.021 | -3.070 | False | False | False |
| 420 | 5.918 | 1 | 5.918 | 3.918/0.918/-4.082 | -12.340 | False | False | False |
| 336 | 5.904 | 1 | 5.904 | 3.904/0.904/-4.096 | -12.354 | False | False | False |
| 381 | 5.605 | 1 | 5.605 | 3.605/0.605/-4.395 | -12.652 | False | False | False |
| 141 | 2.773 | 2 | 5.546 | 1.546/-4.454/-14.454 | -6.356 | False | False | False |
| 295 | 2.531 | 2 | 5.061 | 1.061/-4.939/-14.939 | -6.598 | False | False | False |
| 57 | 2.510 | 2 | 5.019 | 1.019/-4.981/-14.981 | -6.619 | False | False | False |
| 379 | 4.871 | 1 | 4.871 | 2.871/-0.129/-5.129 | -13.387 | False | False | False |
| 56 | 2.419 | 2 | 4.837 | 0.837/-5.163/-15.163 | -6.710 | False | False | False |
| 193 | 4.767 | 1 | 4.767 | 2.767/-0.233/-5.233 | -13.490 | False | False | False |
| 294 | 4.744 | 1 | 4.744 | 2.744/-0.256/-5.256 | -13.514 | False | False | False |
| 91 | 4.678 | 1 | 4.678 | 2.678/-0.322/-5.322 | -13.580 | False | False | False |
| 383 | 4.650 | 1 | 4.650 | 2.650/-0.350/-5.350 | -13.607 | False | False | False |
| 339 | 4.641 | 1 | 4.641 | 2.641/-0.359/-5.359 | -13.617 | False | False | False |
| 237 | 4.455 | 1 | 4.455 | 2.455/-0.545/-5.545 | -13.802 | False | False | False |
| 190 | 4.429 | 1 | 4.429 | 2.429/-0.571/-5.571 | -13.829 | False | False | False |
| 234 | 2.198 | 2 | 4.395 | 0.395/-5.605/-15.605 | -6.931 | False | False | False |
| 80 | 1.441 | 3 | 4.323 | -1.677/-10.677/-25.677 | -4.645 | False | False | False |
| 242 | 4.178 | 1 | 4.178 | 2.178/-0.822/-5.822 | -14.079 | False | False | False |
| 233 | 3.960 | 1 | 3.960 | 1.960/-1.040/-6.040 | -14.297 | False | False | False |
| 232 | 3.835 | 1 | 3.835 | 1.835/-1.165/-6.165 | -14.423 | False | False | False |
| 195 | 3.365 | 1 | 3.365 | 1.365/-1.635/-6.635 | -14.893 | False | False | False |
| 421 | 3.298 | 1 | 3.298 | 1.298/-1.702/-6.702 | -14.959 | False | False | False |
| 189 | 3.278 | 1 | 3.278 | 1.278/-1.722/-6.722 | -14.980 | False | False | False |
| 186 | 2.953 | 1 | 2.953 | 0.953/-2.047/-7.047 | -15.305 | False | False | False |
| 225 | 2.892 | 1 | 2.892 | 0.892/-2.108/-7.108 | -15.365 | False | False | False |
| 1 | 1.324 | 2 | 2.647 | -1.353/-7.353/-17.353 | -7.805 | False | False | False |
| 291 | 1.298 | 2 | 2.596 | -1.404/-7.404/-17.404 | -7.831 | False | False | False |
| 287 | 2.564 | 1 | 2.564 | 0.564/-2.436/-7.436 | -15.693 | False | False | False |
| 95 | 1.234 | 2 | 2.469 | -1.531/-7.531/-17.531 | -7.895 | False | False | False |
| 238 | 2.456 | 1 | 2.456 | 0.456/-2.544/-7.544 | -15.802 | False | False | False |
| 142 | 2.438 | 1 | 2.438 | 0.438/-2.562/-7.562 | -15.820 | False | False | False |
| 93 | 2.410 | 1 | 2.410 | 0.410/-2.590/-7.590 | -15.848 | False | False | False |
| 293 | 2.402 | 1 | 2.402 | 0.402/-2.598/-7.598 | -15.855 | False | False | False |
| 292 | 1.197 | 2 | 2.395 | -1.605/-7.605/-17.605 | -7.931 | False | False | False |
| 340 | 2.379 | 1 | 2.379 | 0.379/-2.621/-7.621 | -15.879 | False | False | False |
| 382 | 2.337 | 1 | 2.337 | 0.337/-2.663/-7.663 | -15.921 | False | False | False |
| 241 | 1.159 | 2 | 2.318 | -1.682/-7.682/-17.682 | -7.970 | False | False | False |
| 290 | 2.286 | 1 | 2.286 | 0.286/-2.714/-7.714 | -15.971 | False | False | False |
| 143 | 1.141 | 2 | 2.282 | -1.718/-7.718/-17.718 | -7.988 | False | False | False |
| 138 | 1.118 | 2 | 2.236 | -1.764/-7.764/-17.764 | -8.011 | False | False | False |
| 338 | 2.182 | 1 | 2.182 | 0.182/-2.818/-7.818 | -16.075 | False | False | False |
| 55 | 2.180 | 1 | 2.180 | 0.180/-2.820/-7.820 | -16.078 | False | False | False |
| 426 | 2.180 | 1 | 2.180 | 0.180/-2.820/-7.820 | -16.078 | False | False | False |
| 90 | 2.138 | 1 | 2.138 | 0.138/-2.862/-7.862 | -16.120 | False | False | False |
| 188 | 2.105 | 1 | 2.105 | 0.105/-2.895/-7.895 | -16.153 | False | False | False |
| 178 | 2.047 | 1 | 2.047 | 0.047/-2.953/-7.953 | -16.211 | False | False | False |
| 289 | 2.016 | 1 | 2.016 | 0.016/-2.984/-7.984 | -16.242 | False | False | False |
| 129 | 1.946 | 1 | 1.946 | -0.054/-3.054/-8.054 | -16.312 | False | False | False |
| 185 | 1.928 | 1 | 1.928 | -0.072/-3.072/-8.072 | -16.330 | False | False | False |
| 194 | 1.866 | 1 | 1.866 | -0.134/-3.134/-8.134 | -16.391 | False | False | False |
| 224 | 1.791 | 1 | 1.791 | -0.209/-3.209/-8.209 | -16.466 | False | False | False |
| 71 | 1.730 | 1 | 1.730 | -0.270/-3.270/-8.270 | -16.528 | False | False | False |
| 243 | 1.700 | 1 | 1.700 | -0.300/-3.300/-8.300 | -16.558 | False | False | False |
| 179 | 1.635 | 1 | 1.635 | -0.365/-3.365/-8.365 | -16.623 | False | False | False |
| 279 | 1.597 | 1 | 1.597 | -0.403/-3.403/-8.403 | -16.661 | False | False | False |
| 281 | 1.571 | 1 | 1.571 | -0.429/-3.429/-8.429 | -16.686 | False | False | False |
| 239 | 1.362 | 1 | 1.362 | -0.638/-3.638/-8.638 | -16.896 | False | False | False |
| 145 | 1.343 | 1 | 1.343 | -0.657/-3.657/-8.657 | -16.915 | False | False | False |
| 384 | 1.243 | 1 | 1.243 | -0.757/-3.757/-8.757 | -17.014 | False | False | False |
| 240 | 1.231 | 1 | 1.231 | -0.769/-3.769/-8.769 | -17.027 | False | False | False |
| 88 | 1.230 | 1 | 1.230 | -0.770/-3.770/-8.770 | -17.027 | False | False | False |
| 341 | 1.227 | 1 | 1.227 | -0.773/-3.773/-8.773 | -17.031 | False | False | False |
| 140 | 1.175 | 1 | 1.175 | -0.825/-3.825/-8.825 | -17.083 | False | False | False |
| 54 | 1.175 | 1 | 1.175 | -0.825/-3.825/-8.825 | -17.083 | False | False | False |
| 386 | 1.169 | 1 | 1.169 | -0.831/-3.831/-8.831 | -17.089 | False | False | False |
| 337 | 1.168 | 1 | 1.168 | -0.832/-3.832/-8.832 | -17.090 | False | False | False |
| 191 | 1.159 | 1 | 1.159 | -0.841/-3.841/-8.841 | -17.099 | False | False | False |
| 425 | 1.154 | 1 | 1.154 | -0.846/-3.846/-8.846 | -17.104 | False | False | False |
| 343 | 1.150 | 1 | 1.150 | -0.850/-3.850/-8.850 | -17.108 | False | False | False |
| 236 | 1.137 | 1 | 1.137 | -0.863/-3.863/-8.863 | -17.121 | False | False | False |
| 422 | 1.108 | 1 | 1.108 | -0.892/-3.892/-8.892 | -17.150 | False | False | False |
| 235 | 1.104 | 1 | 1.104 | -0.896/-3.896/-8.896 | -17.154 | False | False | False |
| 92 | 1.104 | 1 | 1.104 | -0.896/-3.896/-8.896 | -17.154 | False | False | False |
| 380 | 1.099 | 1 | 1.099 | -0.901/-3.901/-8.901 | -17.159 | False | False | False |
| 137 | 1.067 | 1 | 1.067 | -0.933/-3.933/-8.933 | -17.191 | False | False | False |
| 187 | 1.048 | 1 | 1.048 | -0.952/-3.952/-8.952 | -17.209 | False | False | False |
| 335 | 1.027 | 1 | 1.027 | -0.973/-3.973/-8.973 | -17.230 | False | False | False |
| 288 | 1.024 | 1 | 1.024 | -0.976/-3.976/-8.976 | -17.233 | False | False | False |
| 286 | 1.013 | 1 | 1.013 | -0.987/-3.987/-8.987 | -17.244 | False | False | False |
| 231 | 0.913 | 1 | 0.913 | -1.087/-4.087/-9.087 | -17.345 | False | False | False |
## 全部156条group/interval成本
| group | R | 次数 | 原计算ms | µs/次 | ops/native | 纯代数ms | m上限 | Pmax(Htyped/m)µs |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 0 | 3 | 896 | 21.3933 | 23.876 | 18/17 | 0.0327 | 1 | 5.619 |
| 24 | 490 | 896 | 16.9520 | 18.920 | 135/15 | 2.0761 | 2 | 9.791 |
| 25 | 490 | 896 | 16.8546 | 18.811 | 135/15 | 2.0627 | 2 | 9.682 |
| 22 | 485 | 896 | 15.8714 | 17.714 | 131/14 | 2.0171 | 2 | 8.585 |
| 23 | 485 | 896 | 15.7951 | 17.628 | 131/14 | 2.0546 | 2 | 8.500 |
| 0 | 49 | 896 | 15.7818 | 17.614 | 71/4 | 1.8142 | 1 | -0.644 |
| 21 | 480 | 896 | 15.1847 | 16.947 | 127/13 | 1.9563 | 2 | 7.818 |
| 20 | 480 | 896 | 15.0846 | 16.835 | 127/13 | 1.9974 | 2 | 7.707 |
| 18 | 475 | 896 | 13.7850 | 15.385 | 123/12 | 1.9185 | 2 | 6.256 |
| 1 | 58 | 896 | 13.5515 | 15.124 | 34/10 | 0.5253 | 1 | -3.133 |
| 19 | 475 | 896 | 13.4725 | 15.036 | 123/12 | 1.9228 | 2 | 5.907 |
| 16 | 470 | 896 | 12.8265 | 14.315 | 119/11 | 1.8549 | 2 | 5.186 |
| 17 | 470 | 896 | 12.5550 | 14.012 | 119/11 | 1.8573 | 2 | 4.883 |
| 15 | 465 | 896 | 11.5035 | 12.839 | 115/10 | 1.8024 | 2 | 3.710 |
| 14 | 465 | 896 | 11.4329 | 12.760 | 115/10 | 1.8059 | 2 | 3.631 |
| 13 | 460 | 896 | 11.2592 | 12.566 | 111/9 | 1.7614 | 2 | 3.437 |
| 12 | 460 | 896 | 10.8581 | 12.118 | 111/9 | 1.8369 | 2 | 2.990 |
| 10 | 455 | 896 | 10.6493 | 11.885 | 105/8 | 1.9083 | 2 | 2.757 |
| 9 | 424 | 896 | 10.5664 | 11.793 | 10/10 | 0.0000 | 1 | -6.465 |
| 11 | 455 | 896 | 10.0860 | 11.257 | 105/8 | 1.7455 | 2 | 2.128 |
| 9 | 423 | 896 | 9.2056 | 10.274 | 9/9 | 0.0000 | 1 | -7.984 |
| 0 | 5 | 896 | 8.9984 | 10.043 | 25/5 | 0.4921 | 2 | 0.914 |
| 10 | 457 | 896 | 8.7589 | 9.776 | 147/4 | 2.9745 | 2 | 0.647 |
| 6 | 296 | 896 | 8.0238 | 8.955 | 35/7 | 0.5007 | 1 | -9.303 |
| 11 | 457 | 896 | 7.1215 | 7.948 | 147/4 | 2.6785 | 2 | -1.181 |
| 3 | 139 | 896 | 7.0459 | 7.864 | 11/7 | 0.0871 | 1 | -10.394 |
| 23 | 487 | 896 | 6.7934 | 7.582 | 135/4 | 2.3194 | 2 | -1.547 |
| 2 | 94 | 896 | 6.7602 | 7.545 | 30/6 | 0.4611 | 1 | -10.713 |
| 19 | 477 | 896 | 6.6828 | 7.459 | 139/4 | 2.3443 | 2 | -1.670 |
| 12 | 462 | 896 | 6.6416 | 7.413 | 145/4 | 2.4650 | 2 | -1.716 |
| 15 | 467 | 896 | 6.5325 | 7.291 | 143/4 | 2.4717 | 2 | -1.838 |
| 25 | 492 | 896 | 6.5022 | 7.257 | 133/4 | 2.3466 | 2 | -1.872 |
| 16 | 472 | 896 | 6.4933 | 7.247 | 141/4 | 2.3687 | 2 | -1.882 |
| 18 | 477 | 896 | 6.4874 | 7.240 | 139/4 | 2.3671 | 2 | -1.888 |
| 22 | 487 | 896 | 6.4611 | 7.211 | 135/4 | 2.2854 | 2 | -1.918 |
| 24 | 492 | 896 | 6.4187 | 7.164 | 133/4 | 2.2585 | 2 | -1.965 |
| 13 | 462 | 896 | 6.4086 | 7.152 | 145/4 | 2.4618 | 2 | -1.976 |
| 20 | 482 | 896 | 6.3635 | 7.102 | 137/4 | 2.4323 | 2 | -2.027 |
| 21 | 482 | 896 | 6.3464 | 7.083 | 137/4 | 2.3085 | 2 | -2.046 |
| 14 | 467 | 896 | 6.3212 | 7.055 | 143/4 | 2.4350 | 2 | -2.074 |
| 17 | 472 | 896 | 6.3031 | 7.035 | 141/4 | 2.4044 | 2 | -2.094 |
| 0 | 2 | 896 | 6.1449 | 6.858 | 6/5 | 0.0297 | 2 | -2.271 |
| 3 | 144 | 896 | 6.0869 | 6.793 | 29/5 | 0.4702 | 1 | -11.464 |
| 7 | 342 | 896 | 5.8420 | 6.520 | 33/5 | 0.4929 | 1 | -11.738 |
| 1 | 5 | 896 | 5.7144 | 6.378 | 25/5 | 0.3788 | 2 | -2.751 |
| 2 | 89 | 896 | 5.6644 | 6.322 | 10/6 | 0.1128 | 1 | -11.936 |
| 3 | 136 | 896 | 5.4670 | 6.102 | 11/6 | 0.1092 | 1 | -12.156 |
| 9 | 420 | 896 | 5.3023 | 5.918 | 8/6 | 0.0345 | 1 | -12.340 |
| 7 | 336 | 896 | 5.2899 | 5.904 | 7/6 | 0.0181 | 1 | -12.354 |
| 8 | 381 | 896 | 5.0225 | 5.605 | 7/5 | 0.0357 | 1 | -12.652 |
| 1 | 2 | 896 | 4.9056 | 5.475 | 6/5 | 0.0195 | 2 | -3.654 |
| 8 | 385 | 896 | 4.8364 | 5.398 | 32/4 | 0.5041 | 2 | -3.731 |
| 2 | 96 | 896 | 4.8297 | 5.390 | 20/4 | 0.2967 | 2 | -3.739 |
| 9 | 385 | 896 | 4.7423 | 5.293 | 32/4 | 0.4831 | 2 | -3.836 |
| 3 | 96 | 896 | 4.3775 | 4.886 | 20/4 | 0.2943 | 2 | -4.243 |
| 8 | 379 | 896 | 4.3643 | 4.871 | 6/5 | 0.0170 | 1 | -13.387 |
| 4 | 193 | 896 | 4.2716 | 4.767 | 4/4 | 0.0000 | 1 | -13.490 |
| 6 | 294 | 896 | 4.2505 | 4.744 | 4/4 | 0.0000 | 1 | -13.514 |
| 2 | 91 | 896 | 4.1915 | 4.678 | 7/4 | 0.0780 | 1 | -13.580 |
| 8 | 383 | 896 | 4.1668 | 4.650 | 4/4 | 0.0000 | 1 | -13.607 |
| 7 | 339 | 896 | 4.1581 | 4.641 | 4/4 | 0.0000 | 1 | -13.617 |
| 5 | 237 | 896 | 3.9920 | 4.455 | 4/4 | 0.0000 | 1 | -13.802 |
| 4 | 190 | 896 | 3.9683 | 4.429 | 4/4 | 0.0000 | 1 | -13.829 |
| 5 | 242 | 896 | 3.7439 | 4.178 | 29/3 | 0.4658 | 1 | -14.079 |
| 5 | 233 | 896 | 3.5485 | 3.960 | 4/4 | 0.0000 | 1 | -14.297 |
| 5 | 232 | 896 | 3.4362 | 3.835 | 8/4 | 0.0677 | 1 | -14.423 |
| 4 | 192 | 896 | 3.2727 | 3.653 | 3/3 | 0.0000 | 2 | -5.476 |
| 8 | 192 | 896 | 3.2677 | 3.647 | 3/3 | 0.0000 | 2 | -5.482 |
| 4 | 195 | 896 | 3.0152 | 3.365 | 3/3 | 0.0000 | 1 | -14.893 |
| 9 | 421 | 896 | 2.9554 | 3.298 | 3/3 | 0.0000 | 1 | -14.959 |
| 4 | 189 | 896 | 2.9367 | 3.278 | 6/3 | 0.0538 | 1 | -14.980 |
| 3 | 141 | 896 | 2.8715 | 3.205 | 2/2 | 0.0000 | 2 | -5.924 |
| 4 | 186 | 896 | 2.6459 | 2.953 | 6/3 | 0.0518 | 1 | -15.305 |
| 4 | 225 | 896 | 2.5916 | 2.892 | 21/2 | 0.3629 | 1 | -15.365 |
| 7 | 295 | 896 | 2.3380 | 2.609 | 2/2 | 0.0000 | 2 | -6.519 |
| 6 | 287 | 896 | 2.2976 | 2.564 | 4/2 | 0.0339 | 1 | -15.693 |
| 1 | 57 | 896 | 2.2575 | 2.520 | 2/2 | 0.0000 | 2 | -6.609 |
| 2 | 57 | 896 | 2.2397 | 2.500 | 2/2 | 0.0000 | 2 | -6.629 |
| 5 | 238 | 896 | 2.2003 | 2.456 | 2/2 | 0.0000 | 1 | -15.802 |
| 6 | 295 | 896 | 2.1968 | 2.452 | 2/2 | 0.0000 | 2 | -6.677 |
| 3 | 142 | 896 | 2.1845 | 2.438 | 2/2 | 0.0000 | 1 | -15.820 |
| 2 | 56 | 896 | 2.1837 | 2.437 | 2/2 | 0.0000 | 2 | -6.692 |
| 2 | 93 | 896 | 2.1591 | 2.410 | 2/2 | 0.0000 | 1 | -15.848 |
| 6 | 293 | 896 | 2.1526 | 2.402 | 2/2 | 0.0000 | 1 | -15.855 |
| 1 | 56 | 896 | 2.1503 | 2.400 | 2/2 | 0.0000 | 2 | -6.729 |
| 7 | 340 | 896 | 2.1315 | 2.379 | 2/2 | 0.0000 | 1 | -15.879 |
| 4 | 141 | 896 | 2.0975 | 2.341 | 2/2 | 0.0000 | 2 | -6.788 |
| 8 | 382 | 896 | 2.0939 | 2.337 | 2/2 | 0.0000 | 1 | -15.921 |
| 6 | 290 | 896 | 2.0487 | 2.286 | 4/2 | 0.0359 | 1 | -15.971 |
| 5 | 234 | 896 | 1.9742 | 2.203 | 3/2 | 0.0180 | 2 | -6.926 |
| 8 | 234 | 896 | 1.9641 | 2.192 | 3/2 | 0.0173 | 2 | -6.937 |
| 7 | 338 | 896 | 1.9554 | 2.182 | 4/2 | 0.0353 | 1 | -16.075 |
| 1 | 55 | 896 | 1.9532 | 2.180 | 2/2 | 0.0000 | 1 | -16.078 |
| 9 | 426 | 896 | 1.9529 | 2.180 | 32/1 | 0.6333 | 1 | -16.078 |
| 2 | 90 | 896 | 1.9157 | 2.138 | 2/2 | 0.0000 | 1 | -16.120 |
| 4 | 188 | 896 | 1.8858 | 2.105 | 2/2 | 0.0000 | 1 | -16.153 |
| 3 | 178 | 896 | 1.8337 | 2.047 | 22/1 | 0.4219 | 1 | -16.211 |
| 6 | 289 | 896 | 1.8062 | 2.016 | 2/2 | 0.0000 | 1 | -16.242 |
| 2 | 129 | 896 | 1.7436 | 1.946 | 20/1 | 0.4017 | 1 | -16.312 |
| 4 | 185 | 896 | 1.7273 | 1.928 | 4/2 | 0.0434 | 1 | -16.330 |
| 4 | 194 | 896 | 1.6724 | 1.866 | 26/1 | 0.4726 | 1 | -16.391 |
| 4 | 224 | 896 | 1.6049 | 1.791 | 15/1 | 0.2447 | 1 | -16.466 |
| 1 | 71 | 896 | 1.5497 | 1.730 | 11/1 | 0.1898 | 1 | -16.528 |
| 5 | 243 | 896 | 1.5234 | 1.700 | 18/1 | 0.3231 | 1 | -16.558 |
| 3 | 179 | 896 | 1.4651 | 1.635 | 13/1 | 0.2499 | 1 | -16.623 |
| 6 | 74 | 896 | 1.4566 | 1.626 | 8/1 | 0.1271 | 4 | -2.939 |
| 7 | 74 | 896 | 1.4445 | 1.612 | 8/1 | 0.1211 | 4 | -2.952 |
| 5 | 279 | 896 | 1.4309 | 1.597 | 9/1 | 0.1494 | 1 | -16.661 |
| 2 | 77 | 896 | 1.4098 | 1.573 | 8/1 | 0.1430 | 4 | -2.991 |
| 5 | 281 | 896 | 1.4079 | 1.571 | 13/1 | 0.2408 | 1 | -16.686 |
| 1 | 74 | 896 | 1.3865 | 1.547 | 8/1 | 0.1382 | 4 | -3.017 |
| 1 | 77 | 896 | 1.3560 | 1.513 | 8/1 | 0.1433 | 4 | -3.051 |
| 1 | 80 | 896 | 1.3495 | 1.506 | 8/1 | 0.1368 | 3 | -4.580 |
| 0 | 1 | 896 | 1.3383 | 1.494 | 1/1 | 0.0000 | 2 | -7.635 |
| 6 | 77 | 896 | 1.3075 | 1.459 | 8/1 | 0.1253 | 4 | -3.105 |
| 3 | 80 | 896 | 1.2856 | 1.435 | 8/1 | 0.1277 | 3 | -4.651 |
| 7 | 77 | 896 | 1.2837 | 1.433 | 8/1 | 0.1201 | 4 | -3.132 |
| 4 | 74 | 896 | 1.2496 | 1.395 | 8/1 | 0.1212 | 4 | -3.170 |
| 2 | 80 | 896 | 1.2388 | 1.383 | 8/1 | 0.1380 | 3 | -4.703 |
| 5 | 239 | 896 | 1.2200 | 1.362 | 1/1 | 0.0000 | 1 | -16.896 |
| 7 | 291 | 896 | 1.2061 | 1.346 | 1/1 | 0.0000 | 2 | -7.783 |
| 3 | 145 | 896 | 1.2033 | 1.343 | 2/1 | 0.0232 | 1 | -16.915 |
| 2 | 95 | 896 | 1.1244 | 1.255 | 2/1 | 0.0246 | 2 | -7.874 |
| 6 | 291 | 896 | 1.1197 | 1.250 | 1/1 | 0.0000 | 2 | -7.879 |
| 8 | 384 | 896 | 1.1141 | 1.243 | 1/1 | 0.0000 | 1 | -17.014 |
| 5 | 240 | 896 | 1.1029 | 1.231 | 1/1 | 0.0000 | 1 | -17.027 |
| 2 | 88 | 896 | 1.1024 | 1.230 | 2/1 | 0.0296 | 1 | -17.027 |
| 7 | 341 | 896 | 1.0995 | 1.227 | 1/1 | 0.0000 | 1 | -17.031 |
| 4 | 95 | 896 | 1.0874 | 1.214 | 2/1 | 0.0180 | 2 | -7.915 |
| 6 | 292 | 896 | 1.0769 | 1.202 | 1/1 | 0.0000 | 2 | -7.927 |
| 7 | 292 | 896 | 1.0689 | 1.193 | 1/1 | 0.0000 | 2 | -7.936 |
| 3 | 140 | 896 | 1.0528 | 1.175 | 1/1 | 0.0000 | 1 | -17.083 |
| 1 | 54 | 896 | 1.0525 | 1.175 | 2/1 | 0.0187 | 1 | -17.083 |
| 8 | 386 | 896 | 1.0474 | 1.169 | 1/1 | 0.0000 | 1 | -17.089 |
| 7 | 337 | 896 | 1.0466 | 1.168 | 1/1 | 0.0000 | 1 | -17.090 |
| 8 | 241 | 896 | 1.0383 | 1.159 | 1/1 | 0.0000 | 2 | -7.970 |
| 5 | 241 | 896 | 1.0383 | 1.159 | 1/1 | 0.0000 | 2 | -7.970 |
| 4 | 191 | 896 | 1.0381 | 1.159 | 1/1 | 0.0000 | 1 | -17.099 |
| 1 | 1 | 896 | 1.0338 | 1.154 | 1/1 | 0.0000 | 2 | -7.975 |
| 9 | 425 | 896 | 1.0337 | 1.154 | 1/1 | 0.0000 | 1 | -17.104 |
| 7 | 343 | 896 | 1.0302 | 1.150 | 1/1 | 0.0000 | 1 | -17.108 |
| 3 | 143 | 896 | 1.0268 | 1.146 | 1/1 | 0.0000 | 2 | -7.983 |
| 3 | 138 | 896 | 1.0242 | 1.143 | 1/1 | 0.0000 | 2 | -7.986 |
| 5 | 236 | 896 | 1.0186 | 1.137 | 1/1 | 0.0000 | 1 | -17.121 |
| 5 | 143 | 896 | 1.0174 | 1.135 | 1/1 | 0.0000 | 2 | -7.993 |
| 9 | 422 | 896 | 0.9927 | 1.108 | 2/1 | 0.0169 | 1 | -17.150 |
| 5 | 235 | 896 | 0.9892 | 1.104 | 2/1 | 0.0166 | 1 | -17.154 |
| 2 | 92 | 896 | 0.9890 | 1.104 | 1/1 | 0.0000 | 1 | -17.154 |
| 8 | 380 | 896 | 0.9846 | 1.099 | 1/1 | 0.0000 | 1 | -17.159 |
| 6 | 138 | 896 | 0.9791 | 1.093 | 1/1 | 0.0000 | 2 | -8.036 |
| 3 | 137 | 896 | 0.9560 | 1.067 | 1/1 | 0.0000 | 1 | -17.191 |
| 4 | 187 | 896 | 0.9394 | 1.048 | 1/1 | 0.0000 | 1 | -17.209 |
| 7 | 335 | 896 | 0.9204 | 1.027 | 2/1 | 0.0167 | 1 | -17.230 |
| 6 | 288 | 896 | 0.9177 | 1.024 | 1/1 | 0.0000 | 1 | -17.233 |
| 6 | 286 | 896 | 0.9080 | 1.013 | 2/1 | 0.0194 | 1 | -17.244 |
| 5 | 231 | 896 | 0.8181 | 0.913 | 2/1 | 0.0184 | 1 | -17.345 |
## 全部纯代数连续段(区间内切分候选)
| R | group | 范围 | ops | 执行次数 | 累计ms | 每段µs / 允许的最大新增成本 |
| --- | --- | --- | --- | --- | --- | --- |
| 457 | 10,11 | [297,379) | 82 | 1792 | 3.3077 | 1.846 |
| 490 | 24,25 | [92,184) | 92 | 1792 | 3.2223 | 1.798 |
| 485 | 22,23 | [92,181) | 89 | 1792 | 3.1546 | 1.760 |
| 480 | 20,21 | [92,178) | 86 | 1792 | 3.0110 | 1.680 |
| 475 | 18,19 | [92,175) | 83 | 1792 | 2.9102 | 1.624 |
| 470 | 16,17 | [92,172) | 80 | 1792 | 2.7958 | 1.560 |
| 465 | 14,15 | [92,169) | 77 | 1792 | 2.6935 | 1.503 |
| 455 | 10,11 | [92,161) | 69 | 1792 | 2.6903 | 1.501 |
| 460 | 12,13 | [92,166) | 74 | 1792 | 2.6698 | 1.490 |
| 462 | 12,13 | [301,379) | 78 | 1792 | 2.6357 | 1.471 |
| 467 | 14,15 | [305,379) | 74 | 1792 | 2.4664 | 1.376 |
| 472 | 16,17 | [309,379) | 70 | 1792 | 2.3281 | 1.299 |
| 477 | 18,19 | [313,379) | 66 | 1792 | 2.1989 | 1.227 |
| 482 | 20,21 | [317,379) | 62 | 1792 | 2.0955 | 1.169 |
| 487 | 22,23 | [321,379) | 58 | 1792 | 1.9230 | 1.073 |
| 492 | 24,25 | [325,379) | 54 | 1792 | 1.7984 | 1.004 |
| 492 | 24,25 | [419,458) | 39 | 1792 | 1.4920 | 0.833 |
| 487 | 22,23 | [419,456) | 37 | 1792 | 1.3685 | 0.764 |
| 482 | 20,21 | [419,454) | 35 | 1792 | 1.3025 | 0.727 |
| 477 | 18,19 | [419,452) | 33 | 1792 | 1.2131 | 0.677 |
| 472 | 16,17 | [419,450) | 31 | 1792 | 1.1501 | 0.642 |
| 467 | 14,15 | [419,448) | 29 | 1792 | 1.0842 | 0.605 |
| 457 | 10,11 | [419,444) | 25 | 1792 | 0.9992 | 0.558 |
| 462 | 12,13 | [419,446) | 27 | 1792 | 0.9883 | 0.552 |
| 385 | 8,9 | [56,80) | 24 | 1792 | 0.8584 | 0.479 |
| 455 | 10,11 | [56,80) | 24 | 1792 | 0.8358 | 0.466 |
| 5 | 0,1 | [92,111) | 19 | 1792 | 0.8188 | 0.457 |
| 480 | 20,21 | [56,80) | 24 | 1792 | 0.8166 | 0.456 |
| 475 | 18,19 | [56,80) | 24 | 1792 | 0.8055 | 0.449 |
| 460 | 12,13 | [56,80) | 24 | 1792 | 0.8024 | 0.448 |
| 485 | 22,23 | [56,80) | 24 | 1792 | 0.7914 | 0.442 |
| 470 | 16,17 | [56,80) | 24 | 1792 | 0.7909 | 0.441 |
| 490 | 24,25 | [56,80) | 24 | 1792 | 0.7907 | 0.441 |
| 465 | 14,15 | [56,80) | 24 | 1792 | 0.7892 | 0.440 |
| 49 | 0 | [419,444) | 25 | 896 | 0.7564 | 0.844 |
| 426 | 9 | [92,123) | 31 | 896 | 0.6333 | 0.707 |
| 96 | 2,3 | [92,108) | 16 | 1792 | 0.5910 | 0.330 |
| 58 | 1 | [56,80) | 24 | 896 | 0.5253 | 0.586 |
| 482 | 20,21 | [406,418) | 12 | 1792 | 0.4822 | 0.269 |
| 144 | 3 | [56,80) | 24 | 896 | 0.4702 | 0.525 |
| 94 | 2 | [56,80) | 24 | 896 | 0.4611 | 0.515 |
| 467 | 14,15 | [393,405) | 12 | 1792 | 0.4566 | 0.255 |
| 492 | 24,25 | [380,392) | 12 | 1792 | 0.4551 | 0.254 |
| 467 | 14,15 | [380,392) | 12 | 1792 | 0.4548 | 0.254 |
| 194 | 4 | [56,80) | 24 | 896 | 0.4518 | 0.504 |
| 457 | 10,11 | [393,405) | 12 | 1792 | 0.4509 | 0.252 |
| 457 | 10,11 | [380,392) | 12 | 1792 | 0.4505 | 0.251 |
| 487 | 22,23 | [380,392) | 12 | 1792 | 0.4502 | 0.251 |
| 467 | 14,15 | [406,418) | 12 | 1792 | 0.4447 | 0.248 |
| 457 | 10,11 | [406,418) | 12 | 1792 | 0.4447 | 0.248 |
| 477 | 18,19 | [406,418) | 12 | 1792 | 0.4357 | 0.243 |
| 462 | 12,13 | [406,418) | 12 | 1792 | 0.4355 | 0.243 |
| 472 | 16,17 | [406,418) | 12 | 1792 | 0.4352 | 0.243 |
| 487 | 22,23 | [406,418) | 12 | 1792 | 0.4341 | 0.242 |
| 462 | 12,13 | [380,392) | 12 | 1792 | 0.4337 | 0.242 |
| 462 | 12,13 | [393,405) | 12 | 1792 | 0.4336 | 0.242 |
| 477 | 18,19 | [393,405) | 12 | 1792 | 0.4319 | 0.241 |
| 477 | 18,19 | [380,392) | 12 | 1792 | 0.4318 | 0.241 |
| 482 | 20,21 | [380,392) | 12 | 1792 | 0.4309 | 0.240 |
| 472 | 16,17 | [380,392) | 12 | 1792 | 0.4309 | 0.240 |
| 492 | 24,25 | [406,418) | 12 | 1792 | 0.4302 | 0.240 |
| 296 | 6 | [56,80) | 24 | 896 | 0.4301 | 0.480 |
| 242 | 5 | [56,80) | 24 | 896 | 0.4301 | 0.480 |
| 482 | 20,21 | [393,405) | 12 | 1792 | 0.4297 | 0.240 |
| 492 | 24,25 | [393,405) | 12 | 1792 | 0.4293 | 0.240 |
| 487 | 22,23 | [393,405) | 12 | 1792 | 0.4289 | 0.239 |
| 472 | 16,17 | [393,405) | 12 | 1792 | 0.4288 | 0.239 |
| 342 | 7 | [56,80) | 24 | 896 | 0.4261 | 0.476 |
| 178 | 3 | [362,379) | 17 | 896 | 0.3506 | 0.391 |
| 243 | 5 | [92,109) | 17 | 896 | 0.3231 | 0.361 |
| 49 | 0 | [393,405) | 12 | 896 | 0.3148 | 0.351 |
| 49 | 0 | [380,392) | 12 | 896 | 0.3013 | 0.336 |
| 77 | 1,2,6,7 | [406,410) | 4 | 3584 | 0.3006 | 0.084 |
| 49 | 0 | [406,418) | 12 | 896 | 0.2995 | 0.334 |
| 74 | 1,4,6,7 | [393,397) | 4 | 3584 | 0.2893 | 0.081 |
| 225 | 4 | [406,418) | 12 | 896 | 0.2406 | 0.269 |
| 77 | 1,2,6,7 | [402,405) | 3 | 3584 | 0.2310 | 0.064 |
| 80 | 1,2,3 | [419,423) | 4 | 2688 | 0.2248 | 0.084 |
| 74 | 1,4,6,7 | [389,392) | 3 | 3584 | 0.2182 | 0.061 |
| 129 | 2 | [369,379) | 10 | 896 | 0.2139 | 0.239 |
| 179 | 3 | [393,402) | 9 | 896 | 0.1948 | 0.217 |
| 281 | 5 | [419,428) | 9 | 896 | 0.1882 | 0.210 |
| 129 | 2 | [380,389) | 9 | 896 | 0.1878 | 0.210 |
| 80 | 1,2,3 | [415,418) | 3 | 2688 | 0.1777 | 0.066 |
| 224 | 4 | [369,379) | 10 | 896 | 0.1752 | 0.196 |
| 49 | 0 | [373,379) | 6 | 896 | 0.1422 | 0.159 |
| 71 | 1 | [373,379) | 6 | 896 | 0.1153 | 0.129 |
| 279 | 5 | [375,379) | 4 | 896 | 0.0785 | 0.088 |
| 71 | 1 | [380,384) | 4 | 896 | 0.0746 | 0.083 |
| 178 | 3 | [380,384) | 4 | 896 | 0.0713 | 0.080 |
| 279 | 5 | [380,384) | 4 | 896 | 0.0709 | 0.079 |
| 224 | 4 | [380,384) | 4 | 896 | 0.0694 | 0.077 |
| 225 | 4 | [419,423) | 4 | 896 | 0.0693 | 0.077 |
| 179 | 3 | [389,392) | 3 | 896 | 0.0551 | 0.062 |
| 225 | 4 | [402,405) | 3 | 896 | 0.0530 | 0.059 |
| 281 | 5 | [415,418) | 3 | 896 | 0.0526 | 0.059 |
| 5 | 0,1 | [87,88) | 1 | 1792 | 0.0521 | 0.029 |
| 2 | 0,1 | [25,26) | 1 | 1792 | 0.0493 | 0.027 |
| 95 | 2,4 | [85,86) | 1 | 1792 | 0.0426 | 0.024 |
| 234 | 5,8 | [25,26) | 1 | 1792 | 0.0353 | 0.020 |
| 91 | 2 | [27,28) | 1 | 896 | 0.0333 | 0.037 |
| 3 | 0 | [31,32) | 1 | 896 | 0.0327 | 0.036 |
| 385 | 8,9 | [83,84) | 1 | 1792 | 0.0324 | 0.018 |
| 385 | 8,9 | [87,88) | 1 | 1792 | 0.0323 | 0.018 |
| 89 | 2 | [13,14) | 1 | 896 | 0.0323 | 0.036 |
| 455 | 10,11 | [81,82) | 1 | 1792 | 0.0322 | 0.018 |
| 455 | 10,11 | [83,84) | 1 | 1792 | 0.0321 | 0.018 |
| 385 | 8,9 | [81,82) | 1 | 1792 | 0.0321 | 0.018 |
| 385 | 8,9 | [85,86) | 1 | 1792 | 0.0320 | 0.018 |
| 455 | 10,11 | [85,86) | 1 | 1792 | 0.0319 | 0.018 |
| 480 | 20,21 | [83,84) | 1 | 1792 | 0.0319 | 0.018 |
| 490 | 24,25 | [83,84) | 1 | 1792 | 0.0318 | 0.018 |
| 485 | 22,23 | [83,84) | 1 | 1792 | 0.0317 | 0.018 |
| 460 | 12,13 | [83,84) | 1 | 1792 | 0.0317 | 0.018 |
| 465 | 14,15 | [87,88) | 1 | 1792 | 0.0317 | 0.018 |
| 480 | 20,21 | [81,82) | 1 | 1792 | 0.0316 | 0.018 |
| 475 | 18,19 | [83,84) | 1 | 1792 | 0.0316 | 0.018 |
| 490 | 24,25 | [81,82) | 1 | 1792 | 0.0315 | 0.018 |
| 455 | 10,11 | [87,88) | 1 | 1792 | 0.0315 | 0.018 |
| 465 | 14,15 | [83,84) | 1 | 1792 | 0.0315 | 0.018 |
| 485 | 22,23 | [81,82) | 1 | 1792 | 0.0315 | 0.018 |
| 460 | 12,13 | [85,86) | 1 | 1792 | 0.0315 | 0.018 |
| 460 | 12,13 | [81,82) | 1 | 1792 | 0.0315 | 0.018 |
| 470 | 16,17 | [83,84) | 1 | 1792 | 0.0315 | 0.018 |
| 460 | 12,13 | [87,88) | 1 | 1792 | 0.0315 | 0.018 |
| 475 | 18,19 | [87,88) | 1 | 1792 | 0.0314 | 0.018 |
| 480 | 20,21 | [87,88) | 1 | 1792 | 0.0314 | 0.018 |
| 470 | 16,17 | [81,82) | 1 | 1792 | 0.0314 | 0.018 |
| 490 | 24,25 | [85,86) | 1 | 1792 | 0.0314 | 0.018 |
| 475 | 18,19 | [85,86) | 1 | 1792 | 0.0314 | 0.018 |
| 470 | 16,17 | [87,88) | 1 | 1792 | 0.0313 | 0.017 |
| 485 | 22,23 | [85,86) | 1 | 1792 | 0.0313 | 0.017 |
| 480 | 20,21 | [85,86) | 1 | 1792 | 0.0313 | 0.017 |
| 475 | 18,19 | [81,82) | 1 | 1792 | 0.0313 | 0.017 |
| 465 | 14,15 | [81,82) | 1 | 1792 | 0.0313 | 0.017 |
| 485 | 22,23 | [87,88) | 1 | 1792 | 0.0312 | 0.017 |
| 470 | 16,17 | [85,86) | 1 | 1792 | 0.0312 | 0.017 |
| 465 | 14,15 | [85,86) | 1 | 1792 | 0.0311 | 0.017 |
| 490 | 24,25 | [87,88) | 1 | 1792 | 0.0311 | 0.017 |
| 88 | 2 | [5,6) | 1 | 896 | 0.0296 | 0.033 |
| 89 | 2 | [9,10) | 1 | 896 | 0.0283 | 0.032 |
| 89 | 2 | [11,12) | 1 | 896 | 0.0268 | 0.030 |
| 91 | 2 | [29,30) | 1 | 896 | 0.0268 | 0.030 |
| 136 | 3 | [13,14) | 1 | 896 | 0.0255 | 0.028 |
| 89 | 2 | [15,16) | 1 | 896 | 0.0254 | 0.028 |
| 136 | 3 | [7,8) | 1 | 896 | 0.0253 | 0.028 |
| 139 | 3 | [29,30) | 1 | 896 | 0.0242 | 0.027 |
| 185 | 4 | [3,4) | 1 | 896 | 0.0237 | 0.027 |
| 139 | 3 | [27,28) | 1 | 896 | 0.0236 | 0.026 |
| 145 | 3 | [83,84) | 1 | 896 | 0.0232 | 0.026 |
| 139 | 3 | [25,26) | 1 | 896 | 0.0216 | 0.024 |
| 194 | 4 | [81,82) | 1 | 896 | 0.0208 | 0.023 |
| 136 | 3 | [9,10) | 1 | 896 | 0.0200 | 0.022 |
| 185 | 4 | [5,6) | 1 | 896 | 0.0196 | 0.022 |
| 286 | 6 | [7,8) | 1 | 896 | 0.0194 | 0.022 |
| 136 | 3 | [11,12) | 1 | 896 | 0.0192 | 0.021 |
| 136 | 3 | [15,16) | 1 | 896 | 0.0191 | 0.021 |
| 296 | 6 | [87,88) | 1 | 896 | 0.0188 | 0.021 |
| 54 | 1 | [31,32) | 1 | 896 | 0.0187 | 0.021 |
| 381 | 8 | [31,32) | 1 | 896 | 0.0185 | 0.021 |
| 242 | 5 | [83,84) | 1 | 896 | 0.0185 | 0.021 |
| 231 | 5 | [3,4) | 1 | 896 | 0.0184 | 0.020 |
| 186 | 4 | [13,14) | 1 | 896 | 0.0182 | 0.020 |
| 420 | 9 | [13,14) | 1 | 896 | 0.0182 | 0.020 |
| 189 | 4 | [29,30) | 1 | 896 | 0.0182 | 0.020 |
| 336 | 7 | [15,16) | 1 | 896 | 0.0181 | 0.020 |
| 189 | 4 | [27,28) | 1 | 896 | 0.0181 | 0.020 |
| 290 | 6 | [27,28) | 1 | 896 | 0.0181 | 0.020 |
| 338 | 7 | [29,30) | 1 | 896 | 0.0181 | 0.020 |
| 91 | 2 | [31,32) | 1 | 896 | 0.0179 | 0.020 |
| 290 | 6 | [25,26) | 1 | 896 | 0.0178 | 0.020 |
| 139 | 3 | [31,32) | 1 | 896 | 0.0177 | 0.020 |
| 232 | 5 | [15,16) | 1 | 896 | 0.0176 | 0.020 |
| 189 | 4 | [31,32) | 1 | 896 | 0.0175 | 0.020 |
| 296 | 6 | [85,86) | 1 | 896 | 0.0175 | 0.020 |
| 232 | 5 | [13,14) | 1 | 896 | 0.0173 | 0.019 |
| 296 | 6 | [81,82) | 1 | 896 | 0.0172 | 0.019 |
| 242 | 5 | [81,82) | 1 | 896 | 0.0172 | 0.019 |
| 287 | 6 | [13,14) | 1 | 896 | 0.0172 | 0.019 |
| 338 | 7 | [27,28) | 1 | 896 | 0.0172 | 0.019 |
| 381 | 8 | [29,30) | 1 | 896 | 0.0172 | 0.019 |
| 342 | 7 | [87,88) | 1 | 896 | 0.0171 | 0.019 |
| 296 | 6 | [83,84) | 1 | 896 | 0.0171 | 0.019 |
| 379 | 8 | [15,16) | 1 | 896 | 0.0170 | 0.019 |
| 186 | 4 | [11,12) | 1 | 896 | 0.0170 | 0.019 |
| 422 | 9 | [27,28) | 1 | 896 | 0.0169 | 0.019 |
| 342 | 7 | [83,84) | 1 | 896 | 0.0167 | 0.019 |
| 287 | 6 | [11,12) | 1 | 896 | 0.0167 | 0.019 |
| 342 | 7 | [85,86) | 1 | 896 | 0.0167 | 0.019 |
| 335 | 7 | [11,12) | 1 | 896 | 0.0167 | 0.019 |
| 186 | 4 | [9,10) | 1 | 896 | 0.0166 | 0.019 |
| 235 | 5 | [29,30) | 1 | 896 | 0.0166 | 0.018 |
| 232 | 5 | [9,10) | 1 | 896 | 0.0164 | 0.018 |
| 232 | 5 | [11,12) | 1 | 896 | 0.0164 | 0.018 |
| 420 | 9 | [15,16) | 1 | 896 | 0.0164 | 0.018 |
| 342 | 7 | [81,82) | 1 | 896 | 0.0163 | 0.018 |
@@ -0,0 +1,352 @@
# 全部343个 fallback operation
按跨group累计原计算时间排序。一个position计作一个operation,native=1表示原语句含native调用(不等于动态native调用总次数)。纯代数operation的纯代数耗时等于其累计时间;native语句内部的代数/函数耗时没有独立position级测量。具体(group,position)到R的关联见JSON groupOperations,不将各列group和R做笛卡尔积。
所有m都是每Jacobian结构可共享上限,非语义证明。Hmax/Pmax使用与区间表相同的严格盈亏公式。逐operation的7.109µs typed量级,即使H=0也全部不盈利。A=原计算;C=批量代数段切分;E=只评估纯kernel memo,绝非跳过native的context副作用。
| position / 原ID | operation | R | group | 次数 | 累计ms | 每次µs | native | 主要native | m | Hmax(P=0)µs | Hmax(P=2/5/10)µs | Pmax(Htyped)µs | 选择 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 379/24 | flow:amesim_pnvo001_1.port_2 | R49,R71,R129,R178,R224,R279,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 25.9256 | 1.315 | 1 | native_medium_orifice_context | 22 | 28.935 | -15.065/-81.065/-191.065 | 0.485 | A;E只测纯数值尾部 |
| 405/28 | flow:amesim_pnvo001_3.port_2 | R49,R77,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 24.9072 | 1.264 | 1 | native_medium_orifice_context | 22 | 27.798 | -16.202/-82.202/-192.202 | 0.434 | A;E只测纯数值尾部 |
| 392/26 | flow:amesim_pnvo001_2.port_2 | R49,R74,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,3,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 24.7206 | 1.254 | 1 | native_medium_orifice_context | 22 | 27.590 | -16.410/-82.410/-192.410 | 0.424 | A;E只测纯数值尾部 |
| 80/88 | flow:amesim_pnl00r_4.port_1 | R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 24.4096 | 1.238 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 22 | 27.243 | -16.757/-82.757/-192.757 | 0.408 | A;E只测纯数值尾部 |
| 82/90 | flow:amesim_pnl00r_5.port_1 | R145,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 23.7212 | 1.203 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 22 | 26.475 | -17.525/-83.525/-193.525 | 0.373 | A;E只测纯数值尾部 |
| 418/30 | flow:amesim_pnvo001_4.port_2 | R49,R80,R225,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 23.0211 | 1.168 | 1 | native_medium_orifice_context | 22 | 25.693 | -18.307/-84.307/-194.307 | 0.338 | A;E只测纯数值尾部 |
| 86/94 | flow:amesim_pnl00r_7.port_1 | R5,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 22.8168 | 1.158 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 22 | 25.465 | -18.535/-84.535/-194.535 | 0.328 | A;E只测纯数值尾部 |
| 88/96 | flow:amesim_pnl0001_21.port_1 | R5,R96,R195,R296,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,4,6,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 22.3970 | 1.136 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 22 | 24.997 | -19.003/-85.003/-195.003 | 0.306 | A;E只测纯数值尾部 |
| 91/99 | flow:amesim_pnl0001_27.port_1 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 22.3067 | 1.132 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 22 | 24.896 | -19.104/-85.104/-195.104 | 0.302 | A;E只测纯数值尾部 |
| 84/92 | flow:amesim_pnl00r_6.port_1 | R95,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 2,4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 21.5297 | 1.092 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 22 | 24.029 | -19.971/-85.971/-195.971 | 0.262 | A;E只测纯数值尾部 |
| 89/97 | flow:amesim_pnl0001_25.port_1 | R5,R96,R195,R343,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,4,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 21.2854 | 1.080 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 22 | 23.756 | -20.244/-86.244/-196.244 | 0.250 | A;E只测纯数值尾部 |
| 90/98 | flow:amesim_pnl0001_26.port_1 | R5,R96,R195,R386,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,4,8,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 21.2271 | 1.077 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 22 | 23.691 | -20.309/-86.309/-196.309 | 0.247 | A;E只测纯数值尾部 |
| 55/63 | flow:amesim_pnl0001_20.port_1 | R58,R94,R144,R242,R296,R342,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,5,6,7,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 17920 | 19.7423 | 1.102 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 20 | 22.034 | -17.966/-77.966/-177.966 | 0.189 | A;E只测纯数值尾部 |
| 54/62 | flow:amesim_pnl0001_19.port_1 | R58,R94,R144,R193,R296,R425,R465,R470,R475,R480,R485,R490 | 1,2,3,4,6,9,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 17.8718 | 1.108 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 18 | 19.946 | -16.054/-70.054/-160.054 | 0.094 | A;E只测纯数值尾部 |
| 53/61 | flow:amesim_pnl0001_18.port_1 | R58,R94,R144,R193,R241,R470,R475,R480,R485,R490 | 1,2,3,4,5,8,16,17,18,19,20,21,22,23,24,25 | 14336 | 16.1206 | 1.124 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 16 | 17.992 | -14.008/-62.008/-142.008 | -0.017 | A;E只测纯数值尾部 |
| 52/60 | flow:amesim_pnl0001_17.port_1 | R58,R94,R144,R193,R341,R424,R475,R480,R485,R490 | 1,2,3,4,7,9,18,19,20,21,22,23,24,25 | 12544 | 14.2730 | 1.138 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 14 | 15.930 | -12.070/-54.070/-124.070 | -0.166 | A;E只测纯数值尾部 |
| 51/59 | flow:amesim_pnl0001_16.port_1 | R58,R94,R144,R193,R384,R424,R480,R485,R490 | 1,2,3,4,8,9,20,21,22,23,24,25 | 10752 | 12.6662 | 1.178 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 12 | 14.136 | -9.864/-45.864/-105.864 | -0.343 | A;E只测纯数值尾部 |
| 50/58 | flow:amesim_pnl0001_15.port_1 | R58,R94,R240,R295,R424,R485,R490 | 1,2,5,6,7,9,22,23,24,25 | 8960 | 10.6532 | 1.189 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 10 | 11.890 | -8.110/-38.110/-88.110 | -0.637 | A;E只测纯数值尾部 |
| 49/57 | flow:amesim_pnl0001_14.port_1 | R58,R192,R295,R424,R490 | 1,4,6,7,8,9,24,25 | 7168 | 9.1021 | 1.270 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 8 | 10.159 | -5.841/-29.841/-69.841 | -1.012 | A;E只测纯数值尾部 |
| 40/48 | flow:amesim_pnl0002_5.port_1 | R3,R141,R293,R339,R423 | 0,3,4,6,7,9 | 5376 | 6.9862 | 1.300 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 7.797 | -4.203/-22.203/-52.203 | -1.743 | A;E只测纯数值尾部 |
| 47/55 | flow:amesim_pnl0002_8.port_2 | R3,R58,R93,R192,R424 | 0,1,2,4,8,9 | 5376 | 6.8524 | 1.275 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 7.648 | -4.352/-22.352/-52.352 | -1.768 | A;E只测纯数值尾部 |
| 43/51 | flow:amesim_pnl0002_6.port_2 | R3,R57,R294,R383,R424 | 0,1,2,6,8,9 | 5376 | 6.8264 | 1.270 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 7.619 | -4.381/-22.381/-52.381 | -1.773 | A;E只测纯数值尾部 |
| 45/53 | flow:amesim_pnl0002_7.port_2 | R3,R142,R239,R294,R340,R424 | 0,3,5,6,7,9 | 5376 | 6.7114 | 1.248 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 7.490 | -4.510/-22.510/-52.510 | -1.795 | A;E只测纯数值尾部 |
| 42/50 | flow:amesim_pnl0002_6.port_1 | R3,R57,R238,R339,R383 | 0,1,2,5,7,8 | 5376 | 6.6735 | 1.241 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 7.448 | -4.552/-22.552/-52.552 | -1.802 | A;E只测纯数值尾部 |
| 33/41 | flow:amesim_pnl0002_1.port_2 | R3,R139,R291,R381,R423 | 0,3,6,7,8,9 | 5376 | 6.4783 | 1.205 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 7.230 | -4.770/-22.770/-52.770 | -1.838 | A;E只测纯数值尾部 |
| 38/46 | flow:amesim_pnl0002_4.port_1 | R3,R56,R237,R382,R423 | 0,1,2,5,8,9 | 5376 | 6.4745 | 1.204 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 7.226 | -4.774/-22.774/-52.774 | -1.839 | A;E只测纯数值尾部 |
| 36/44 | flow:amesim_pnl0002_3.port_1 | R3,R190,R237,R292,R423 | 0,4,5,6,7,9 | 5376 | 6.3731 | 1.185 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 7.113 | -4.887/-22.887/-52.887 | -1.857 | A;E只测纯数值尾部 |
| 44/52 | flow:amesim_pnl0002_7.port_1 | R3,R142,R191,R294,R383,R424 | 0,3,4,6,8,9 | 5376 | 6.2368 | 1.160 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.961 | -5.039/-23.039/-53.039 | -1.883 | A;E只测纯数值尾部 |
| 24/32 | flow:amesim_pnvo001_5.port_2 | R2,R139,R234,R290 | 0,1,3,5,6,8 | 5376 | 6.2069 | 1.155 | 1 | native_medium_orifice_context | 6 | 6.927 | -5.073/-23.073/-53.073 | -1.888 | A;E只测纯数值尾部 |
| 30/38 | flow:amesim_pnvo001_8.port_2 | R3,R54,R91,R139,R189,R381 | 0,1,2,3,4,8 | 5376 | 6.1412 | 1.142 | 1 | native_medium_orifice_context | 6 | 6.854 | -5.146/-23.146/-53.146 | -1.901 | A;E只测纯数值尾部 |
| 37/45 | flow:amesim_pnl0002_3.port_2 | R3,R140,R190,R237,R382,R423 | 0,3,4,5,8,9 | 5376 | 6.0932 | 1.133 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.800 | -5.200/-23.200/-53.200 | -1.910 | A;E只测纯数值尾部 |
| 32/40 | flow:amesim_pnl0002_1.port_1 | R3,R91,R139,R236,R381,R423 | 0,2,3,5,8,9 | 5376 | 6.0223 | 1.120 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.721 | -5.279/-23.279/-53.279 | -1.923 | A;E只测纯数值尾部 |
| 46/54 | flow:amesim_pnl0002_8.port_1 | R3,R58,R93,R294,R340,R424 | 0,1,2,6,7,9 | 5376 | 5.9758 | 1.112 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.669 | -5.331/-23.331/-53.331 | -1.931 | A;E只测纯数值尾部 |
| 48/56 | flow:amesim_pnl0001_13.port_1 | R58,R143,R192,R424 | 1,3,4,5,8,9 | 5376 | 5.9419 | 1.105 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.632 | -5.368/-23.368/-53.368 | -1.938 | A;E只测纯数值尾部 |
| 39/47 | flow:amesim_pnl0002_4.port_2 | R3,R56,R293,R339,R423 | 0,1,2,6,7,9 | 5376 | 5.9384 | 1.105 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.628 | -5.372/-23.372/-53.372 | -1.938 | A;E只测纯数值尾部 |
| 41/49 | flow:amesim_pnl0002_5.port_2 | R3,R141,R238,R339,R383 | 0,3,4,5,7,8 | 5376 | 5.9276 | 1.103 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.616 | -5.384/-23.384/-53.384 | -1.940 | A;E只测纯数值尾部 |
| 23/23 | flow:amesim_pnl0001_11.port_1 | R2,R234,R337,R421 | 0,1,5,7,8,9 | 5376 | 5.8836 | 1.094 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.566 | -5.434/-23.434/-53.434 | -1.949 | A;E只测纯数值尾部 |
| 28/36 | flow:amesim_pnvo001_7.port_2 | R91,R139,R189,R235,R338,R381 | 2,3,4,5,7,8 | 5376 | 5.8624 | 1.090 | 1 | native_medium_orifice_context | 6 | 6.543 | -5.457/-23.457/-53.457 | -1.952 | A;E只测纯数值尾部 |
| 34/42 | flow:amesim_pnl0002_2.port_1 | R3,R55,R139,R190,R381,R423 | 0,1,3,4,8,9 | 5376 | 5.8478 | 1.088 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.527 | -5.473/-23.473/-53.473 | -1.955 | A;E只测纯数值尾部 |
| 26/34 | flow:amesim_pnvo001_6.port_2 | R91,R139,R189,R290,R338,R422 | 2,3,4,6,7,9 | 5376 | 5.7655 | 1.072 | 1 | native_medium_orifice_context | 6 | 6.435 | -5.565/-23.565/-53.565 | -1.970 | A;E只测纯数值尾部 |
| 21/21 | flow:amesim_pnl0001_9.port_1 | R2,R90,R188,R380,R421 | 0,1,2,4,8,9 | 5376 | 5.7596 | 1.071 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.428 | -5.572/-23.572/-53.572 | -1.972 | A;E只测纯数值尾部 |
| 35/43 | flow:amesim_pnl0002_2.port_2 | R3,R55,R92,R190,R237,R423 | 0,1,2,4,5,9 | 5376 | 5.6729 | 1.055 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.331 | -5.669/-23.669/-53.669 | -1.988 | A;E只测纯数值尾部 |
| 22/22 | flow:amesim_pnl0001_10.port_1 | R2,R90,R138,R421 | 0,1,2,3,6,9 | 5376 | 5.6316 | 1.048 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.285 | -5.715/-23.715/-53.715 | -1.995 | A;E只测纯数值尾部 |
| 18/18 | flow:amesim_pnl0001_4.port_1 | R1,R233,R336,R379,R420 | 0,1,5,7,8,9 | 5376 | 5.6088 | 1.043 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.260 | -5.740/-23.740/-53.740 | -2.000 | A;E只测纯数值尾部 |
| 20/20 | flow:amesim_pnl0001_7.port_1 | R2,R188,R233,R289,R336 | 0,1,4,5,6,7 | 5376 | 5.5929 | 1.040 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 6.242 | -5.758/-23.758/-53.758 | -2.003 | A;E只测纯数值尾部 |
| 19/19 | flow:amesim_pnl0001_5.port_1 | R137,R233,R289,R336,R379,R420 | 3,5,6,7,8,9 | 5376 | 5.3133 | 0.988 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 5.930 | -6.070/-24.070/-54.070 | -2.055 | A;E只测纯数值尾部 |
| 17/17 | flow:amesim_pnl0001_2.port_1 | R89,R187,R233,R336,R379,R420 | 2,4,5,7,8,9 | 5376 | 5.2407 | 0.975 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 5.849 | -6.151/-24.151/-54.151 | -2.068 | A;E只测纯数值尾部 |
| 16/16 | flow:amesim_pnl0001_1.port_1 | R89,R136,R288,R336,R379,R420 | 2,3,6,7,8,9 | 5376 | 5.1746 | 0.963 | 1 | native_pipe_flow_cached_context,native_temperature_ph_context | 6 | 5.775 | -6.225/-24.225/-54.225 | -2.080 | A;停止R288后续优化 |
| 12/12 | flow:amesim_pnor001_7.port_1 | R89,R136,R186,R232,R287,R420 | 2,3,4,5,6,9 | 5376 | 5.1265 | 0.954 | 1 | native_medium_orifice_context | 6 | 5.722 | -6.278/-24.278/-54.278 | -2.089 | A;E只测纯数值尾部 |
| 10/10 | flow:amesim_pnor001_6.port_1 | R89,R136,R186,R232,R287,R335 | 2,3,4,5,6,7 | 5376 | 5.0914 | 0.947 | 1 | native_medium_orifice_context | 6 | 5.682 | -6.318/-24.318/-54.318 | -2.096 | A;E只测纯数值尾部 |
| 14/14 | flow:amesim_pnor001_8.port_1 | R89,R136,R232,R336,R379,R420 | 2,3,5,7,8,9 | 5376 | 5.0226 | 0.934 | 1 | native_medium_orifice_context | 6 | 5.606 | -6.394/-24.394/-54.394 | -2.109 | A;E只测纯数值尾部 |
| 8/8 | flow:amesim_pnor001_5.port_1 | R89,R136,R186,R232 | 2,3,4,5 | 3584 | 3.3942 | 0.947 | 1 | native_medium_orifice_context | 4 | 3.788 | -4.212/-16.212/-36.212 | -3.617 | A;E只测纯数值尾部 |
| 4/4 | flow:amesim_pnor001_3.port_1 | R88,R185 | 2,4 | 1792 | 1.7647 | 0.985 | 1 | native_medium_orifice_context | 2 | 1.970 | -2.030/-8.030/-18.030 | -8.144 | A;E只测纯数值尾部 |
| 6/6 | flow:amesim_pnor001_4.port_1 | R136,R286 | 3,6 | 1792 | 1.6856 | 0.941 | 1 | native_medium_orifice_context | 2 | 1.881 | -2.119/-8.119/-18.119 | -8.188 | A;E只测纯数值尾部 |
| 2/2 | flow:amesim_pnor001_2.port_1 | R185,R231 | 4,5 | 1792 | 1.4902 | 0.832 | 1 | native_medium_orifice_context | 2 | 1.663 | -2.337/-8.337/-18.337 | -8.297 | A;E只测纯数值尾部 |
| 401/427 | stream:amesim_p4node2_2 | R49,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.5801 | 0.036 | 0 | 纯代数/alias | 18 | 0.647 | -35.353/-89.353/-179.353 | -0.978 | A;C合并代数段 |
| 388/423 | stream:amesim_p4node2_1 | R49,R129,R457,R462,R467,R472,R477,R482,R487,R492 | 0,2,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.5655 | 0.035 | 0 | 纯代数/alias | 18 | 0.631 | -35.369/-89.369/-179.369 | -0.979 | A;C合并代数段 |
| 431/439 | stream:amesim_p4node2_5 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.5407 | 0.035 | 0 | 纯代数/alias | 17 | 0.603 | -33.397/-84.397/-169.397 | -1.038 | A;C合并代数段 |
| 427/435 | stream:amesim_p4node2_4 | R49,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.5360 | 0.033 | 0 | 纯代数/alias | 18 | 0.598 | -35.402/-89.402/-179.402 | -0.981 | A;C合并代数段 |
| 414/431 | stream:amesim_p4node2_3 | R49,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.5268 | 0.033 | 0 | 纯代数/alias | 18 | 0.588 | -35.412/-89.412/-179.412 | -0.982 | A;C合并代数段 |
| 439/447 | stream:amesim_p4node2_7 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.4841 | 0.032 | 0 | 纯代数/alias | 17 | 0.540 | -33.460/-84.460/-169.460 | -1.042 | A;C合并代数段 |
| 435/443 | stream:amesim_p4node2_6 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.4747 | 0.031 | 0 | 纯代数/alias | 17 | 0.530 | -33.470/-84.470/-169.470 | -1.043 | A;C合并代数段 |
| 443/451 | stream:amesim_p4node2_8 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.4713 | 0.031 | 0 | 纯代数/alias | 17 | 0.526 | -33.474/-84.474/-169.474 | -1.043 | A;C合并代数段 |
| 59/67 | flow:amesim_pnpl01_4.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4703 | 0.021 | 0 | 纯代数/alias | 25 | 0.525 | -49.475/-124.475/-249.475 | -0.709 | A;C合并代数段 |
| 71/79 | flow:amesim_pnpl01_16.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4669 | 0.021 | 0 | 纯代数/alias | 25 | 0.521 | -49.479/-124.479/-249.479 | -0.709 | A;C合并代数段 |
| 67/75 | flow:amesim_pnpl01_12.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4554 | 0.020 | 0 | 纯代数/alias | 25 | 0.508 | -49.492/-124.492/-249.492 | -0.710 | A;C合并代数段 |
| 56/64 | flow:amesim_pnpl01_1.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4544 | 0.020 | 0 | 纯代数/alias | 25 | 0.507 | -49.493/-124.493/-249.493 | -0.710 | A;C合并代数段 |
| 65/73 | flow:amesim_pnpl01_10.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4439 | 0.020 | 0 | 纯代数/alias | 25 | 0.495 | -49.505/-124.505/-249.505 | -0.710 | A;C合并代数段 |
| 64/72 | flow:amesim_pnpl01_9.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4390 | 0.020 | 0 | 纯代数/alias | 25 | 0.490 | -49.510/-124.510/-249.510 | -0.711 | A;C合并代数段 |
| 62/70 | flow:amesim_pnpl01_7.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4385 | 0.020 | 0 | 纯代数/alias | 25 | 0.489 | -49.511/-124.511/-249.511 | -0.711 | A;C合并代数段 |
| 58/66 | flow:amesim_pnpl01_3.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4369 | 0.020 | 0 | 纯代数/alias | 25 | 0.488 | -49.512/-124.512/-249.512 | -0.711 | A;C合并代数段 |
| 61/69 | flow:amesim_pnpl01_6.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4363 | 0.019 | 0 | 纯代数/alias | 25 | 0.487 | -49.513/-124.513/-249.513 | -0.711 | A;C合并代数段 |
| 77/85 | flow:amesim_pnrp17_6.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4347 | 0.019 | 0 | 纯代数/alias | 25 | 0.485 | -49.515/-124.515/-249.515 | -0.711 | A;C合并代数段 |
| 57/65 | flow:amesim_pnpl01_2.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4344 | 0.019 | 0 | 纯代数/alias | 25 | 0.485 | -49.515/-124.515/-249.515 | -0.711 | A;C合并代数段 |
| 63/71 | flow:amesim_pnpl01_8.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4332 | 0.019 | 0 | 纯代数/alias | 25 | 0.483 | -49.517/-124.517/-249.517 | -0.711 | A;C合并代数段 |
| 72/80 | flow:amesim_pnrp17_1.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4331 | 0.019 | 0 | 纯代数/alias | 25 | 0.483 | -49.517/-124.517/-249.517 | -0.711 | A;C合并代数段 |
| 78/86 | flow:amesim_pnrp17_7.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4327 | 0.019 | 0 | 纯代数/alias | 25 | 0.483 | -49.517/-124.517/-249.517 | -0.711 | A;C合并代数段 |
| 79/87 | flow:amesim_pnrp17_8.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4323 | 0.019 | 0 | 纯代数/alias | 25 | 0.482 | -49.518/-124.518/-249.518 | -0.711 | A;C合并代数段 |
| 70/78 | flow:amesim_pnpl01_15.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4309 | 0.019 | 0 | 纯代数/alias | 25 | 0.481 | -49.519/-124.519/-249.519 | -0.711 | A;C合并代数段 |
| 68/76 | flow:amesim_pnpl01_13.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4289 | 0.019 | 0 | 纯代数/alias | 25 | 0.479 | -49.521/-124.521/-249.521 | -0.711 | A;C合并代数段 |
| 60/68 | flow:amesim_pnpl01_5.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4274 | 0.019 | 0 | 纯代数/alias | 25 | 0.477 | -49.523/-124.523/-249.523 | -0.711 | A;C合并代数段 |
| 66/74 | flow:amesim_pnpl01_11.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4270 | 0.019 | 0 | 纯代数/alias | 25 | 0.477 | -49.523/-124.523/-249.523 | -0.711 | A;C合并代数段 |
| 417/434 | alias:amesim_p4node2_4.port_4 | R49,R80,R225,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.4268 | 0.022 | 0 | 纯代数/alias | 22 | 0.476 | -43.524/-109.524/-219.524 | -0.808 | A;C合并代数段 |
| 75/83 | flow:amesim_pnrp17_4.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4265 | 0.019 | 0 | 纯代数/alias | 25 | 0.476 | -49.524/-124.524/-249.524 | -0.711 | A;C合并代数段 |
| 76/84 | flow:amesim_pnrp17_5.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4255 | 0.019 | 0 | 纯代数/alias | 25 | 0.475 | -49.525/-124.525/-249.525 | -0.711 | A;C合并代数段 |
| 73/81 | flow:amesim_pnrp17_2.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4249 | 0.019 | 0 | 纯代数/alias | 25 | 0.474 | -49.526/-124.526/-249.526 | -0.711 | A;C合并代数段 |
| 69/77 | flow:amesim_pnpl01_14.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4246 | 0.019 | 0 | 纯代数/alias | 25 | 0.474 | -49.526/-124.526/-249.526 | -0.711 | A;C合并代数段 |
| 99/107 | connection:b15 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.4185 | 0.021 | 0 | 纯代数/alias | 22 | 0.467 | -43.533/-109.533/-219.533 | -0.809 | A;C合并代数段 |
| 74/82 | flow:amesim_pnrp17_3.port_1 | R58,R94,R144,R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 22400 | 0.4179 | 0.019 | 0 | 纯代数/alias | 25 | 0.466 | -49.534/-124.534/-249.534 | -0.712 | A;C合并代数段 |
| 96/104 | connection:b8 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.4146 | 0.021 | 0 | 纯代数/alias | 22 | 0.463 | -43.537/-109.537/-219.537 | -0.809 | A;C合并代数段 |
| 415/432 | alias:amesim_p4node2_4.port_1 | R49,R80,R225,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.4138 | 0.021 | 0 | 纯代数/alias | 22 | 0.462 | -43.538/-109.538/-219.538 | -0.809 | A;C合并代数段 |
| 376/420 | alias:amesim_p4node2_1.port_1 | R49,R71,R129,R178,R224,R279,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.4046 | 0.021 | 0 | 纯代数/alias | 22 | 0.452 | -43.548/-109.548/-219.548 | -0.809 | A;C合并代数段 |
| 381/146 | connection:b66 | R49,R71,R129,R178,R224,R279,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3991 | 0.020 | 0 | 纯代数/alias | 22 | 0.445 | -43.555/-109.555/-219.555 | -0.810 | A;C合并代数段 |
| 386/320 | connection:q[145] | R49,R129,R457,R462,R467,R472,R477,R482,R487,R492 | 0,2,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3989 | 0.025 | 0 | 纯代数/alias | 18 | 0.445 | -35.555/-89.555/-179.555 | -0.990 | A;C合并代数段 |
| 408/156 | connection:b76 | R49,R77,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3982 | 0.020 | 0 | 纯代数/alias | 22 | 0.444 | -43.556/-109.556/-219.556 | -0.810 | A;C合并代数段 |
| 407/148 | connection:b68 | R49,R77,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3967 | 0.020 | 0 | 纯代数/alias | 22 | 0.443 | -43.557/-109.557/-219.557 | -0.810 | A;C合并代数段 |
| 92/100 | connection:b2 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3951 | 0.020 | 0 | 纯代数/alias | 22 | 0.441 | -43.559/-109.559/-219.559 | -0.810 | A;C合并代数段 |
| 382/154 | connection:b74 | R49,R71,R129,R178,R224,R279,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3944 | 0.020 | 0 | 纯代数/alias | 22 | 0.440 | -43.560/-109.560/-219.560 | -0.810 | A;C合并代数段 |
| 420/149 | connection:b69 | R49,R80,R225,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3939 | 0.020 | 0 | 纯代数/alias | 22 | 0.440 | -43.560/-109.560/-219.560 | -0.810 | A;C合并代数段 |
| 395/155 | connection:b75 | R49,R74,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,3,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3929 | 0.020 | 0 | 纯代数/alias | 22 | 0.439 | -43.561/-109.561/-219.561 | -0.810 | A;C合并代数段 |
| 421/157 | connection:b77 | R49,R80,R225,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3919 | 0.020 | 0 | 纯代数/alias | 22 | 0.437 | -43.563/-109.563/-219.563 | -0.810 | A;C合并代数段 |
| 375/419 | alias:amesim_pnvo001_8.port_3 | R49,R71,R129,R178,R224,R279,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3911 | 0.020 | 0 | 纯代数/alias | 22 | 0.436 | -43.564/-109.564/-219.564 | -0.810 | A;C合并代数段 |
| 97/105 | connection:b11 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3892 | 0.020 | 0 | 纯代数/alias | 22 | 0.434 | -43.566/-109.566/-219.566 | -0.810 | A;C合并代数段 |
| 98/106 | connection:b14 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3879 | 0.020 | 0 | 纯代数/alias | 22 | 0.433 | -43.567/-109.567/-219.567 | -0.810 | A;C合并代数段 |
| 394/147 | connection:b67 | R49,R74,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,3,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3874 | 0.020 | 0 | 纯代数/alias | 22 | 0.432 | -43.568/-109.568/-219.568 | -0.810 | A;C合并代数段 |
| 391/426 | alias:amesim_p4node2_2.port_4 | R49,R74,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,3,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3848 | 0.020 | 0 | 纯代数/alias | 22 | 0.429 | -43.571/-109.571/-219.571 | -0.810 | A;C合并代数段 |
| 378/422 | alias:amesim_p4node2_1.port_4 | R49,R71,R129,R178,R224,R279,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3838 | 0.019 | 0 | 纯代数/alias | 22 | 0.428 | -43.572/-109.572/-219.572 | -0.810 | A;C合并代数段 |
| 416/433 | alias:amesim_p4node2_4.port_3 | R49,R80,R225,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3838 | 0.019 | 0 | 纯代数/alias | 22 | 0.428 | -43.572/-109.572/-219.572 | -0.810 | A;C合并代数段 |
| 104/112 | connection:b28 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3825 | 0.019 | 0 | 纯代数/alias | 22 | 0.427 | -43.573/-109.573/-219.573 | -0.810 | A;C合并代数段 |
| 399/321 | connection:q[147] | R49,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3825 | 0.024 | 0 | 纯代数/alias | 18 | 0.427 | -35.573/-89.573/-179.573 | -0.991 | A;C合并代数段 |
| 94/102 | connection:b4 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3802 | 0.019 | 0 | 纯代数/alias | 22 | 0.424 | -43.576/-109.576/-219.576 | -0.811 | A;C合并代数段 |
| 106/114 | connection:b30 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3796 | 0.019 | 0 | 纯代数/alias | 22 | 0.424 | -43.576/-109.576/-219.576 | -0.811 | A;C合并代数段 |
| 95/103 | connection:b7 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3794 | 0.019 | 0 | 纯代数/alias | 22 | 0.423 | -43.577/-109.577/-219.577 | -0.811 | A;C合并代数段 |
| 105/113 | connection:b29 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3781 | 0.019 | 0 | 纯代数/alias | 22 | 0.422 | -43.578/-109.578/-219.578 | -0.811 | A;C合并代数段 |
| 389/424 | alias:amesim_p4node2_2.port_1 | R49,R74,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,3,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3767 | 0.019 | 0 | 纯代数/alias | 22 | 0.420 | -43.580/-109.580/-219.580 | -0.811 | A;C合并代数段 |
| 402/428 | alias:amesim_p4node2_3.port_1 | R49,R77,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3765 | 0.019 | 0 | 纯代数/alias | 22 | 0.420 | -43.580/-109.580/-219.580 | -0.811 | A;C合并代数段 |
| 403/429 | alias:amesim_p4node2_3.port_3 | R49,R77,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3765 | 0.019 | 0 | 纯代数/alias | 22 | 0.420 | -43.580/-109.580/-219.580 | -0.811 | A;C合并代数段 |
| 404/430 | alias:amesim_p4node2_3.port_4 | R49,R77,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3763 | 0.019 | 0 | 纯代数/alias | 22 | 0.420 | -43.580/-109.580/-219.580 | -0.811 | A;C合并代数段 |
| 409/287 | connection:q[79] | R49,R77,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3756 | 0.019 | 0 | 纯代数/alias | 22 | 0.419 | -43.581/-109.581/-219.581 | -0.811 | A;C合并代数段 |
| 100/108 | connection:b24 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3756 | 0.019 | 0 | 纯代数/alias | 22 | 0.419 | -43.581/-109.581/-219.581 | -0.811 | A;C合并代数段 |
| 93/101 | connection:b3 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3751 | 0.019 | 0 | 纯代数/alias | 22 | 0.419 | -43.581/-109.581/-219.581 | -0.811 | A;C合并代数段 |
| 107/115 | connection:b31 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3736 | 0.019 | 0 | 纯代数/alias | 22 | 0.417 | -43.583/-109.583/-219.583 | -0.811 | A;C合并代数段 |
| 390/425 | alias:amesim_p4node2_2.port_3 | R49,R74,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,3,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3720 | 0.019 | 0 | 纯代数/alias | 22 | 0.415 | -43.585/-109.585/-219.585 | -0.811 | A;C合并代数段 |
| 87/95 | flow:amesim_pnl00r_7.port_2 | R5,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3716 | 0.019 | 0 | 纯代数/alias | 22 | 0.415 | -43.585/-109.585/-219.585 | -0.811 | A;C合并代数段 |
| 101/109 | connection:b25 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3712 | 0.019 | 0 | 纯代数/alias | 22 | 0.414 | -43.586/-109.586/-219.586 | -0.811 | A;C合并代数段 |
| 380/25 | flow:amesim_pnvo001_1.port_3 | R49,R71,R129,R178,R224,R279,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3703 | 0.019 | 0 | 纯代数/alias | 22 | 0.413 | -43.587/-109.587/-219.587 | -0.811 | A;C合并代数段 |
| 422/277 | connection:q[69] | R49,R80,R225,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3701 | 0.019 | 0 | 纯代数/alias | 22 | 0.413 | -43.587/-109.587/-219.587 | -0.811 | A;C合并代数段 |
| 396/275 | connection:q[67] | R49,R74,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,3,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3684 | 0.019 | 0 | 纯代数/alias | 22 | 0.411 | -43.589/-109.589/-219.589 | -0.811 | A;C合并代数段 |
| 102/110 | connection:b26 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3678 | 0.019 | 0 | 纯代数/alias | 22 | 0.410 | -43.590/-109.590/-219.590 | -0.811 | A;C合并代数段 |
| 423/301 | connection:q[109] | R49,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3677 | 0.023 | 0 | 纯代数/alias | 18 | 0.410 | -35.590/-89.590/-179.590 | -0.992 | A;C合并代数段 |
| 377/421 | alias:amesim_p4node2_1.port_3 | R49,R71,R129,R178,R224,R279,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3676 | 0.019 | 0 | 纯代数/alias | 22 | 0.410 | -43.590/-109.590/-219.590 | -0.811 | A;C合并代数段 |
| 383/273 | connection:q[65] | R49,R71,R129,R178,R224,R279,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3671 | 0.019 | 0 | 纯代数/alias | 22 | 0.410 | -43.590/-109.590/-219.590 | -0.811 | A;C合并代数段 |
| 419/31 | flow:amesim_pnvo001_4.port_3 | R49,R80,R225,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3671 | 0.019 | 0 | 纯代数/alias | 22 | 0.410 | -43.590/-109.590/-219.590 | -0.811 | A;C合并代数段 |
| 103/111 | connection:b27 | R5,R96,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,2,3,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3667 | 0.019 | 0 | 纯代数/alias | 22 | 0.409 | -43.591/-109.591/-219.591 | -0.811 | A;C合并代数段 |
| 412/322 | connection:q[149] | R49,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3664 | 0.023 | 0 | 纯代数/alias | 18 | 0.409 | -35.591/-89.591/-179.591 | -0.992 | A;C合并代数段 |
| 393/27 | flow:amesim_pnvo001_2.port_3 | R49,R74,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,3,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3650 | 0.019 | 0 | 纯代数/alias | 22 | 0.407 | -43.593/-109.593/-219.593 | -0.811 | A;C合并代数段 |
| 108/116 | connection:b36 | R5,R243,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,5,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 17920 | 0.3647 | 0.020 | 0 | 纯代数/alias | 20 | 0.407 | -39.593/-99.593/-199.593 | -0.893 | A;C合并代数段 |
| 384/289 | connection:q[97] | R49,R129,R457,R462,R467,R472,R477,R482,R487,R492 | 0,2,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3636 | 0.023 | 0 | 纯代数/alias | 18 | 0.406 | -35.594/-89.594/-179.594 | -0.992 | A;C合并代数段 |
| 406/29 | flow:amesim_pnvo001_3.port_3 | R49,R77,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,4,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3617 | 0.018 | 0 | 纯代数/alias | 22 | 0.404 | -43.596/-109.596/-219.596 | -0.812 | A;C合并代数段 |
| 83/91 | flow:amesim_pnl00r_5.port_2 | R145,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3616 | 0.018 | 0 | 纯代数/alias | 22 | 0.404 | -43.596/-109.596/-219.596 | -0.812 | A;C合并代数段 |
| 425/323 | connection:q[151] | R49,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3615 | 0.022 | 0 | 纯代数/alias | 18 | 0.403 | -35.597/-89.597/-179.597 | -0.992 | A;C合并代数段 |
| 430/438 | alias:amesim_p4node2_5.port_4 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3608 | 0.024 | 0 | 纯代数/alias | 17 | 0.403 | -33.597/-84.597/-169.597 | -1.050 | A;C合并代数段 |
| 85/93 | flow:amesim_pnl00r_6.port_2 | R95,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 2,4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3601 | 0.018 | 0 | 纯代数/alias | 22 | 0.402 | -43.598/-109.598/-219.598 | -0.812 | A;C合并代数段 |
| 374/418 | alias:amesim_pnvo001_8.port_2 | R49,R71,R129,R178,R224,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 18816 | 0.3588 | 0.019 | 0 | 纯代数/alias | 21 | 0.400 | -41.600/-104.600/-209.600 | -0.850 | A;C合并代数段 |
| 373/417 | alias:amesim_pnvo001_7.port_3 | R49,R71,R129,R178,R224,R457,R462,R467,R472,R477,R482,R487,R492 | 0,1,2,3,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 18816 | 0.3583 | 0.019 | 0 | 纯代数/alias | 21 | 0.400 | -41.600/-104.600/-209.600 | -0.850 | A;C合并代数段 |
| 81/89 | flow:amesim_pnl00r_4.port_2 | R194,R242,R296,R342,R385,R455,R460,R465,R470,R475,R480,R485,R490 | 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 19712 | 0.3560 | 0.018 | 0 | 纯代数/alias | 22 | 0.397 | -43.603/-109.603/-219.603 | -0.812 | A;C合并代数段 |
| 397/293 | connection:q[101] | R49,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3553 | 0.022 | 0 | 纯代数/alias | 18 | 0.397 | -35.603/-89.603/-179.603 | -0.992 | A;C合并代数段 |
| 369/413 | alias:amesim_pnvo001_5.port_3 | R129,R178,R224,R457,R462,R467,R472,R477,R482,R487,R492 | 2,3,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 17024 | 0.3526 | 0.021 | 0 | 纯代数/alias | 19 | 0.393 | -37.607/-94.607/-189.607 | -0.940 | A;C合并代数段 |
| 113/121 | connection:b41 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3520 | 0.023 | 0 | 纯代数/alias | 17 | 0.393 | -33.607/-84.607/-169.607 | -1.051 | A;C合并代数段 |
| 410/297 | connection:q[105] | R49,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3483 | 0.022 | 0 | 纯代数/alias | 18 | 0.389 | -35.611/-89.611/-179.611 | -0.993 | A;C合并代数段 |
| 110/118 | connection:b38 | R5,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 17024 | 0.3413 | 0.020 | 0 | 纯代数/alias | 19 | 0.381 | -37.619/-94.619/-189.619 | -0.941 | A;C合并代数段 |
| 109/117 | connection:b37 | R5,R426,R455,R460,R465,R470,R475,R480,R485,R490 | 0,1,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 17024 | 0.3377 | 0.020 | 0 | 纯代数/alias | 19 | 0.377 | -37.623/-94.623/-189.623 | -0.941 | A;C合并代数段 |
| 111/119 | connection:b39 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3241 | 0.021 | 0 | 纯代数/alias | 17 | 0.362 | -33.638/-84.638/-169.638 | -1.053 | A;C合并代数段 |
| 372/416 | alias:amesim_pnvo001_7.port_2 | R129,R178,R224,R457,R462,R467,R472,R477,R482,R487,R492 | 2,3,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 17024 | 0.3236 | 0.019 | 0 | 纯代数/alias | 19 | 0.361 | -37.639/-94.639/-189.639 | -0.942 | A;C合并代数段 |
| 115/123 | connection:b43 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3230 | 0.021 | 0 | 纯代数/alias | 17 | 0.360 | -33.640/-84.640/-169.640 | -1.053 | A;C合并代数段 |
| 122/130 | connection:b50 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3227 | 0.021 | 0 | 纯代数/alias | 17 | 0.360 | -33.640/-84.640/-169.640 | -1.053 | A;C合并代数段 |
| 371/415 | alias:amesim_pnvo001_6.port_3 | R129,R178,R224,R457,R462,R467,R472,R477,R482,R487,R492 | 2,3,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 17024 | 0.3205 | 0.019 | 0 | 纯代数/alias | 19 | 0.358 | -37.642/-94.642/-189.642 | -0.942 | A;C合并代数段 |
| 146/162 | connection:b82 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.3201 | 0.022 | 0 | 纯代数/alias | 16 | 0.357 | -31.643/-79.643/-159.643 | -1.119 | A;C合并代数段 |
| 367/410 | alias:amesim_pnvo001_4.port_2 | R178,R457,R462,R467,R472,R477,R482,R487,R492 | 3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3189 | 0.021 | 0 | 纯代数/alias | 17 | 0.356 | -33.644/-84.644/-169.644 | -1.053 | A;C合并代数段 |
| 121/129 | connection:b49 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3148 | 0.021 | 0 | 纯代数/alias | 17 | 0.351 | -33.649/-84.649/-169.649 | -1.053 | A;C合并代数段 |
| 120/128 | connection:b48 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3145 | 0.021 | 0 | 纯代数/alias | 17 | 0.351 | -33.649/-84.649/-169.649 | -1.053 | A;C合并代数段 |
| 433/441 | alias:amesim_p4node2_6.port_3 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3130 | 0.021 | 0 | 纯代数/alias | 17 | 0.349 | -33.651/-84.651/-169.651 | -1.053 | A;C合并代数段 |
| 432/440 | alias:amesim_p4node2_6.port_1 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3123 | 0.020 | 0 | 纯代数/alias | 17 | 0.348 | -33.652/-84.652/-169.652 | -1.053 | A;C合并代数段 |
| 370/414 | alias:amesim_pnvo001_6.port_2 | R129,R178,R224,R457,R462,R467,R472,R477,R482,R487,R492 | 2,3,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 17024 | 0.3118 | 0.018 | 0 | 纯代数/alias | 19 | 0.348 | -37.652/-94.652/-189.652 | -0.943 | A;C合并代数段 |
| 411/299 | connection:q[107] | R49,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3110 | 0.019 | 0 | 纯代数/alias | 18 | 0.347 | -35.653/-89.653/-179.653 | -0.995 | A;C合并代数段 |
| 114/122 | connection:b42 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3093 | 0.020 | 0 | 纯代数/alias | 17 | 0.345 | -33.655/-84.655/-169.655 | -1.054 | A;C合并代数段 |
| 142/158 | connection:b78 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.3089 | 0.022 | 0 | 纯代数/alias | 16 | 0.345 | -31.655/-79.655/-159.655 | -1.120 | A;C合并代数段 |
| 112/120 | connection:b40 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3088 | 0.020 | 0 | 纯代数/alias | 17 | 0.345 | -33.655/-84.655/-169.655 | -1.054 | A;C合并代数段 |
| 123/131 | connection:b51 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.3087 | 0.022 | 0 | 纯代数/alias | 16 | 0.344 | -31.656/-79.656/-159.656 | -1.120 | A;C合并代数段 |
| 398/295 | connection:q[103] | R49,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3083 | 0.019 | 0 | 纯代数/alias | 18 | 0.344 | -35.656/-89.656/-179.656 | -0.995 | A;C合并代数段 |
| 161/177 | connection:b97 | R460,R465,R470,R475,R480,R485,R490 | 12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 12544 | 0.3081 | 0.025 | 0 | 纯代数/alias | 14 | 0.344 | -27.656/-69.656/-139.656 | -1.280 | A;C合并代数段 |
| 424/303 | connection:q[111] | R49,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3061 | 0.019 | 0 | 纯代数/alias | 18 | 0.342 | -35.658/-89.658/-179.658 | -0.995 | A;C合并代数段 |
| 440/448 | alias:amesim_p4node2_8.port_1 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3059 | 0.020 | 0 | 纯代数/alias | 17 | 0.341 | -33.659/-84.659/-169.659 | -1.054 | A;C合并代数段 |
| 413/409 | alias:amesim_pnvo001_3.port_3 | R49,R225,R457,R462,R467,R472,R477,R482,R487,R492 | 0,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3043 | 0.019 | 0 | 纯代数/alias | 18 | 0.340 | -35.660/-89.660/-179.660 | -0.995 | A;C合并代数段 |
| 400/407 | alias:amesim_pnvo001_2.port_3 | R49,R179,R457,R462,R467,R472,R477,R482,R487,R492 | 0,3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3033 | 0.019 | 0 | 纯代数/alias | 18 | 0.338 | -35.662/-89.662/-179.662 | -0.996 | A;C合并代数段 |
| 426/411 | alias:amesim_pnvo001_4.port_3 | R49,R281,R457,R462,R467,R472,R477,R482,R487,R492 | 0,5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.3024 | 0.019 | 0 | 纯代数/alias | 18 | 0.337 | -35.663/-89.663/-179.663 | -0.996 | A;C合并代数段 |
| 116/124 | connection:b44 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3023 | 0.020 | 0 | 纯代数/alias | 17 | 0.337 | -33.663/-84.663/-169.663 | -1.054 | A;C合并代数段 |
| 119/127 | connection:b47 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3016 | 0.020 | 0 | 纯代数/alias | 17 | 0.337 | -33.663/-84.663/-169.663 | -1.054 | A;C合并代数段 |
| 332/364 | alias:amesim_pnor001_1.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.3013 | 0.021 | 0 | 纯代数/alias | 16 | 0.336 | -31.664/-79.664/-159.664 | -1.120 | A;C合并代数段 |
| 340/374 | alias:amesim_pn3node2_3.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.3008 | 0.021 | 0 | 纯代数/alias | 16 | 0.336 | -31.664/-79.664/-159.664 | -1.120 | A;C合并代数段 |
| 118/126 | connection:b46 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.3000 | 0.020 | 0 | 纯代数/alias | 17 | 0.335 | -33.665/-84.665/-169.665 | -1.054 | A;C合并代数段 |
| 138/150 | connection:b70 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2990 | 0.021 | 0 | 纯代数/alias | 16 | 0.334 | -31.666/-79.666/-159.666 | -1.120 | A;C合并代数段 |
| 387/405 | alias:amesim_pnvo001_1.port_3 | R49,R129,R457,R462,R467,R472,R477,R482,R487,R492 | 0,2,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.2988 | 0.019 | 0 | 纯代数/alias | 18 | 0.334 | -35.666/-89.666/-179.666 | -0.996 | A;C合并代数段 |
| 127/135 | connection:b55 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2983 | 0.021 | 0 | 纯代数/alias | 16 | 0.333 | -31.667/-79.667/-159.667 | -1.120 | A;C合并代数段 |
| 117/125 | connection:b45 | R426,R455,R460,R465,R470,R475,R480,R485,R490 | 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2963 | 0.019 | 0 | 纯代数/alias | 17 | 0.331 | -33.669/-84.669/-169.669 | -1.055 | A;C合并代数段 |
| 141/153 | connection:b73 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2962 | 0.021 | 0 | 纯代数/alias | 16 | 0.331 | -31.669/-79.669/-159.669 | -1.120 | A;C合并代数段 |
| 364/404 | alias:amesim_pnvo001_1.port_2 | R178,R457,R462,R467,R472,R477,R482,R487,R492 | 3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2960 | 0.019 | 0 | 纯代数/alias | 17 | 0.330 | -33.670/-84.670/-169.670 | -1.055 | A;C合并代数段 |
| 385/291 | connection:q[99] | R49,R129,R457,R462,R467,R472,R477,R482,R487,R492 | 0,2,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 16128 | 0.2955 | 0.018 | 0 | 纯代数/alias | 18 | 0.330 | -35.670/-89.670/-179.670 | -0.996 | A;C合并代数段 |
| 348/384 | alias:amesim_pn3node2_5.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2954 | 0.021 | 0 | 纯代数/alias | 16 | 0.330 | -31.670/-79.670/-159.670 | -1.121 | A;C合并代数段 |
| 145/161 | connection:b81 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2939 | 0.021 | 0 | 纯代数/alias | 16 | 0.328 | -31.672/-79.672/-159.672 | -1.121 | A;C合并代数段 |
| 368/412 | alias:amesim_pnvo001_5.port_2 | R178,R457,R462,R467,R472,R477,R482,R487,R492 | 3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2924 | 0.019 | 0 | 纯代数/alias | 17 | 0.326 | -33.674/-84.674/-169.674 | -1.055 | A;C合并代数段 |
| 350/387 | alias:amesim_pnor001_5.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2922 | 0.020 | 0 | 纯代数/alias | 16 | 0.326 | -31.674/-79.674/-159.674 | -1.121 | A;C合并代数段 |
| 147/163 | connection:b83 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2917 | 0.020 | 0 | 纯代数/alias | 16 | 0.326 | -31.674/-79.674/-159.674 | -1.121 | A;C合并代数段 |
| 129/137 | connection:b57 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2913 | 0.020 | 0 | 纯代数/alias | 16 | 0.325 | -31.675/-79.675/-159.675 | -1.121 | A;C合并代数段 |
| 149/165 | connection:b85 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2912 | 0.020 | 0 | 纯代数/alias | 16 | 0.325 | -31.675/-79.675/-159.675 | -1.121 | A;C合并代数段 |
| 143/159 | connection:b79 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2910 | 0.020 | 0 | 纯代数/alias | 16 | 0.325 | -31.675/-79.675/-159.675 | -1.121 | A;C合并代数段 |
| 150/166 | connection:b86 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2903 | 0.020 | 0 | 纯代数/alias | 16 | 0.324 | -31.676/-79.676/-159.676 | -1.121 | A;C合并代数段 |
| 437/445 | alias:amesim_p4node2_7.port_3 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2900 | 0.019 | 0 | 纯代数/alias | 17 | 0.324 | -33.676/-84.676/-169.676 | -1.055 | A;C合并代数段 |
| 366/408 | alias:amesim_pnvo001_3.port_2 | R178,R457,R462,R467,R472,R477,R482,R487,R492 | 3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2898 | 0.019 | 0 | 纯代数/alias | 17 | 0.323 | -33.677/-84.677/-169.677 | -1.055 | A;C合并代数段 |
| 153/169 | connection:b89 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2898 | 0.020 | 0 | 纯代数/alias | 16 | 0.323 | -31.677/-79.677/-159.677 | -1.121 | A;C合并代数段 |
| 126/134 | connection:b54 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2893 | 0.020 | 0 | 纯代数/alias | 16 | 0.323 | -31.677/-79.677/-159.677 | -1.121 | A;C合并代数段 |
| 128/136 | connection:b56 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2885 | 0.020 | 0 | 纯代数/alias | 16 | 0.322 | -31.678/-79.678/-159.678 | -1.121 | A;C合并代数段 |
| 362/401 | alias:amesim_pn3node2_9.port_1 | R178,R457,R462,R467,R472,R477,R482,R487,R492 | 3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2882 | 0.019 | 0 | 纯代数/alias | 17 | 0.322 | -33.678/-84.678/-169.678 | -1.055 | A;C合并代数段 |
| 152/168 | connection:b88 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2881 | 0.020 | 0 | 纯代数/alias | 16 | 0.322 | -31.678/-79.678/-159.678 | -1.121 | A;C合并代数段 |
| 148/164 | connection:b84 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2877 | 0.020 | 0 | 纯代数/alias | 16 | 0.321 | -31.679/-79.679/-159.679 | -1.121 | A;C合并代数段 |
| 151/167 | connection:b87 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2872 | 0.020 | 0 | 纯代数/alias | 16 | 0.321 | -31.679/-79.679/-159.679 | -1.121 | A;C合并代数段 |
| 363/402 | alias:amesim_pn3node2_9.port_3 | R178,R457,R462,R467,R472,R477,R482,R487,R492 | 3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2868 | 0.019 | 0 | 纯代数/alias | 17 | 0.320 | -33.680/-84.680/-169.680 | -1.055 | A;C合并代数段 |
| 428/436 | alias:amesim_p4node2_5.port_1 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2858 | 0.019 | 0 | 纯代数/alias | 17 | 0.319 | -33.681/-84.681/-169.681 | -1.055 | A;C合并代数段 |
| 131/139 | connection:b59 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2857 | 0.020 | 0 | 纯代数/alias | 16 | 0.319 | -31.681/-79.681/-159.681 | -1.121 | A;C合并代数段 |
| 429/437 | alias:amesim_p4node2_5.port_3 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2854 | 0.019 | 0 | 纯代数/alias | 17 | 0.319 | -33.681/-84.681/-169.681 | -1.055 | A;C合并代数段 |
| 365/406 | alias:amesim_pnvo001_2.port_2 | R178,R457,R462,R467,R472,R477,R482,R487,R492 | 3,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2843 | 0.019 | 0 | 纯代数/alias | 17 | 0.317 | -33.683/-84.683/-169.683 | -1.055 | A;C合并代数段 |
| 140/152 | connection:b72 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2832 | 0.020 | 0 | 纯代数/alias | 16 | 0.316 | -31.684/-79.684/-159.684 | -1.121 | A;C合并代数段 |
| 132/140 | connection:b60 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2831 | 0.020 | 0 | 纯代数/alias | 16 | 0.316 | -31.684/-79.684/-159.684 | -1.121 | A;C合并代数段 |
| 156/172 | connection:b92 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2829 | 0.020 | 0 | 纯代数/alias | 16 | 0.316 | -31.684/-79.684/-159.684 | -1.121 | A;C合并代数段 |
| 164/180 | connection:b100 | R460,R465,R470,R475,R480,R485,R490 | 12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 12544 | 0.2828 | 0.023 | 0 | 纯代数/alias | 14 | 0.316 | -27.684/-69.684/-139.684 | -1.282 | A;C合并代数段 |
| 442/450 | alias:amesim_p4node2_8.port_4 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2825 | 0.019 | 0 | 纯代数/alias | 17 | 0.315 | -33.685/-84.685/-169.685 | -1.055 | A;C合并代数段 |
| 441/449 | alias:amesim_p4node2_8.port_3 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2824 | 0.019 | 0 | 纯代数/alias | 17 | 0.315 | -33.685/-84.685/-169.685 | -1.055 | A;C合并代数段 |
| 159/175 | connection:b95 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2821 | 0.020 | 0 | 纯代数/alias | 16 | 0.315 | -31.685/-79.685/-159.685 | -1.121 | A;C合并代数段 |
| 434/442 | alias:amesim_p4node2_6.port_4 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2819 | 0.019 | 0 | 纯代数/alias | 17 | 0.315 | -33.685/-84.685/-169.685 | -1.055 | A;C合并代数段 |
| 336/368 | alias:amesim_pn3node2_1.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2815 | 0.020 | 0 | 纯代数/alias | 16 | 0.314 | -31.686/-79.686/-159.686 | -1.121 | A;C合并代数段 |
| 133/141 | connection:b61 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2809 | 0.020 | 0 | 纯代数/alias | 16 | 0.314 | -31.686/-79.686/-159.686 | -1.122 | A;C合并代数段 |
| 438/446 | alias:amesim_p4node2_7.port_4 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2808 | 0.018 | 0 | 纯代数/alias | 17 | 0.313 | -33.687/-84.687/-169.687 | -1.056 | A;C合并代数段 |
| 155/171 | connection:b91 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2804 | 0.020 | 0 | 纯代数/alias | 16 | 0.313 | -31.687/-79.687/-159.687 | -1.122 | A;C合并代数段 |
| 124/132 | connection:b52 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2804 | 0.020 | 0 | 纯代数/alias | 16 | 0.313 | -31.687/-79.687/-159.687 | -1.122 | A;C合并代数段 |
| 139/151 | connection:b71 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2799 | 0.020 | 0 | 纯代数/alias | 16 | 0.312 | -31.688/-79.688/-159.688 | -1.122 | A;C合并代数段 |
| 157/173 | connection:b93 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2793 | 0.019 | 0 | 纯代数/alias | 16 | 0.312 | -31.688/-79.688/-159.688 | -1.122 | A;C合并代数段 |
| 160/176 | connection:b96 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2793 | 0.019 | 0 | 纯代数/alias | 16 | 0.312 | -31.688/-79.688/-159.688 | -1.122 | A;C合并代数段 |
| 436/444 | alias:amesim_p4node2_7.port_1 | R49,R457,R462,R467,R472,R477,R482,R487,R492 | 0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 15232 | 0.2786 | 0.018 | 0 | 纯代数/alias | 17 | 0.311 | -33.689/-84.689/-169.689 | -1.056 | A;C合并代数段 |
| 144/160 | connection:b80 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2784 | 0.019 | 0 | 纯代数/alias | 16 | 0.311 | -31.689/-79.689/-159.689 | -1.122 | A;C合并代数段 |
| 134/142 | connection:b62 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2774 | 0.019 | 0 | 纯代数/alias | 16 | 0.310 | -31.690/-79.690/-159.690 | -1.122 | A;C合并代数段 |
| 326/358 | connection:q[204] | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2772 | 0.019 | 0 | 纯代数/alias | 16 | 0.309 | -31.691/-79.691/-159.691 | -1.122 | A;C合并代数段 |
| 344/379 | alias:amesim_pnor001_4.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2771 | 0.019 | 0 | 纯代数/alias | 16 | 0.309 | -31.691/-79.691/-159.691 | -1.122 | A;C合并代数段 |
| 158/174 | connection:b94 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2766 | 0.019 | 0 | 纯代数/alias | 16 | 0.309 | -31.691/-79.691/-159.691 | -1.122 | A;C合并代数段 |
| 136/144 | connection:b64 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2739 | 0.019 | 0 | 纯代数/alias | 16 | 0.306 | -31.694/-79.694/-159.694 | -1.122 | A;C合并代数段 |
| 325/357 | connection:q[203] | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2734 | 0.019 | 0 | 纯代数/alias | 16 | 0.305 | -31.695/-79.695/-159.695 | -1.122 | A;C合并代数段 |
| 137/145 | connection:b65 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2733 | 0.019 | 0 | 纯代数/alias | 16 | 0.305 | -31.695/-79.695/-159.695 | -1.122 | A;C合并代数段 |
| 360/398 | alias:amesim_pn3node2_8.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2723 | 0.019 | 0 | 纯代数/alias | 16 | 0.304 | -31.696/-79.696/-159.696 | -1.122 | A;C合并代数段 |
| 345/380 | alias:amesim_pnor001_4.port_2 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2723 | 0.019 | 0 | 纯代数/alias | 16 | 0.304 | -31.696/-79.696/-159.696 | -1.122 | A;C合并代数段 |
| 135/143 | connection:b63 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2719 | 0.019 | 0 | 纯代数/alias | 16 | 0.303 | -31.697/-79.697/-159.697 | -1.122 | A;C合并代数段 |
| 349/385 | alias:amesim_pn3node2_5.port_3 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2717 | 0.019 | 0 | 纯代数/alias | 16 | 0.303 | -31.697/-79.697/-159.697 | -1.122 | A;C合并代数段 |
| 339/372 | alias:amesim_pn3node2_2.port_3 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2713 | 0.019 | 0 | 纯代数/alias | 16 | 0.303 | -31.697/-79.697/-159.697 | -1.122 | A;C合并代数段 |
| 357/395 | alias:amesim_pnor001_7.port_2 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2710 | 0.019 | 0 | 纯代数/alias | 16 | 0.302 | -31.698/-79.698/-159.698 | -1.122 | A;C合并代数段 |
| 125/133 | connection:b53 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2707 | 0.019 | 0 | 纯代数/alias | 16 | 0.302 | -31.698/-79.698/-159.698 | -1.122 | A;C合并代数段 |
| 327/359 | connection:q[205] | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2704 | 0.019 | 0 | 纯代数/alias | 16 | 0.302 | -31.698/-79.698/-159.698 | -1.122 | A;C合并代数段 |
| 333/365 | alias:amesim_pnor001_1.port_2 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2695 | 0.019 | 0 | 纯代数/alias | 16 | 0.301 | -31.699/-79.699/-159.699 | -1.122 | A;C合并代数段 |
| 355/392 | alias:amesim_pn3node2_6.port_3 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2688 | 0.019 | 0 | 纯代数/alias | 16 | 0.300 | -31.700/-79.700/-159.700 | -1.122 | A;C合并代数段 |
| 354/391 | alias:amesim_pn3node2_6.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2685 | 0.019 | 0 | 纯代数/alias | 16 | 0.300 | -31.700/-79.700/-159.700 | -1.122 | A;C合并代数段 |
| 334/366 | alias:amesim_pnor001_2.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2684 | 0.019 | 0 | 纯代数/alias | 16 | 0.300 | -31.700/-79.700/-159.700 | -1.122 | A;C合并代数段 |
| 329/361 | connection:q[227] | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2671 | 0.019 | 0 | 纯代数/alias | 16 | 0.298 | -31.702/-79.702/-159.702 | -1.122 | A;C合并代数段 |
| 335/367 | alias:amesim_pnor001_2.port_2 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2662 | 0.019 | 0 | 纯代数/alias | 16 | 0.297 | -31.703/-79.703/-159.703 | -1.123 | A;C合并代数段 |
| 353/390 | alias:amesim_pnor001_6.port_2 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2649 | 0.018 | 0 | 纯代数/alias | 16 | 0.296 | -31.704/-79.704/-159.704 | -1.123 | A;C合并代数段 |
| 130/138 | connection:b58 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2646 | 0.018 | 0 | 纯代数/alias | 16 | 0.295 | -31.705/-79.705/-159.705 | -1.123 | A;C合并代数段 |
| 338/371 | alias:amesim_pn3node2_2.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2645 | 0.018 | 0 | 纯代数/alias | 16 | 0.295 | -31.705/-79.705/-159.705 | -1.123 | A;C合并代数段 |
| 361/399 | alias:amesim_pn3node2_8.port_3 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2645 | 0.018 | 0 | 纯代数/alias | 16 | 0.295 | -31.705/-79.705/-159.705 | -1.123 | A;C合并代数段 |
| 328/360 | connection:q[225] | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2643 | 0.018 | 0 | 纯代数/alias | 16 | 0.295 | -31.705/-79.705/-159.705 | -1.123 | A;C合并代数段 |
| 359/397 | alias:amesim_pnor001_8.port_2 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2642 | 0.018 | 0 | 纯代数/alias | 16 | 0.295 | -31.705/-79.705/-159.705 | -1.123 | A;C合并代数段 |
| 337/369 | alias:amesim_pn3node2_1.port_3 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2642 | 0.018 | 0 | 纯代数/alias | 16 | 0.295 | -31.705/-79.705/-159.705 | -1.123 | A;C合并代数段 |
| 352/389 | alias:amesim_pnor001_6.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2641 | 0.018 | 0 | 纯代数/alias | 16 | 0.295 | -31.705/-79.705/-159.705 | -1.123 | A;C合并代数段 |
| 154/170 | connection:b90 | R455,R460,R465,R470,R475,R480,R485,R490 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2638 | 0.018 | 0 | 纯代数/alias | 16 | 0.294 | -31.706/-79.706/-159.706 | -1.123 | A;C合并代数段 |
| 347/382 | alias:amesim_pn3node2_4.port_3 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2636 | 0.018 | 0 | 纯代数/alias | 16 | 0.294 | -31.706/-79.706/-159.706 | -1.123 | A;C合并代数段 |
| 351/388 | alias:amesim_pnor001_5.port_2 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2635 | 0.018 | 0 | 纯代数/alias | 16 | 0.294 | -31.706/-79.706/-159.706 | -1.123 | A;C合并代数段 |
| 346/381 | alias:amesim_pn3node2_4.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2635 | 0.018 | 0 | 纯代数/alias | 16 | 0.294 | -31.706/-79.706/-159.706 | -1.123 | A;C合并代数段 |
| 358/396 | alias:amesim_pnor001_8.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2633 | 0.018 | 0 | 纯代数/alias | 16 | 0.294 | -31.706/-79.706/-159.706 | -1.123 | A;C合并代数段 |
| 343/378 | alias:amesim_pnor001_3.port_2 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2632 | 0.018 | 0 | 纯代数/alias | 16 | 0.294 | -31.706/-79.706/-159.706 | -1.123 | A;C合并代数段 |
| 341/375 | alias:amesim_pn3node2_3.port_3 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2628 | 0.018 | 0 | 纯代数/alias | 16 | 0.293 | -31.707/-79.707/-159.707 | -1.123 | A;C合并代数段 |
| 331/363 | connection:q[231] | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2608 | 0.018 | 0 | 纯代数/alias | 16 | 0.291 | -31.709/-79.709/-159.709 | -1.123 | A;C合并代数段 |
| 323/355 | connection:q[199] | R457,R462,R467,R472,R477,R482,R487 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23 | 12544 | 0.2600 | 0.021 | 0 | 纯代数/alias | 14 | 0.290 | -27.710/-69.710/-139.710 | -1.283 | A;C合并代数段 |
| 330/362 | connection:q[229] | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2593 | 0.018 | 0 | 纯代数/alias | 16 | 0.289 | -31.711/-79.711/-159.711 | -1.123 | A;C合并代数段 |
| 342/377 | alias:amesim_pnor001_3.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2588 | 0.018 | 0 | 纯代数/alias | 16 | 0.289 | -31.711/-79.711/-159.711 | -1.123 | A;C合并代数段 |
| 356/394 | alias:amesim_pnor001_7.port_1 | R457,R462,R467,R472,R477,R482,R487,R492 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 14336 | 0.2587 | 0.018 | 0 | 纯代数/alias | 16 | 0.289 | -31.711/-79.711/-159.711 | -1.123 | A;C合并代数段 |
| 163/179 | connection:b99 | R460,R465,R470,R475,R480,R485,R490 | 12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 12544 | 0.2570 | 0.020 | 0 | 纯代数/alias | 14 | 0.287 | -27.713/-69.713/-139.713 | -1.284 | A;C合并代数段 |
| 165/181 | connection:b101 | R460,R465,R470,R475,R480,R485,R490 | 12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 12544 | 0.2553 | 0.020 | 0 | 纯代数/alias | 14 | 0.285 | -27.715/-69.715/-139.715 | -1.284 | A;C合并代数段 |
| 162/178 | connection:b98 | R460,R465,R470,R475,R480,R485,R490 | 12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 12544 | 0.2540 | 0.020 | 0 | 纯代数/alias | 14 | 0.284 | -27.716/-69.716/-139.716 | -1.284 | A;C合并代数段 |
| 307/339 | connection:q[175] | R457,R462,R467 | 10,11,12,13,14,15 | 5376 | 0.2491 | 0.046 | 0 | 纯代数/alias | 6 | 0.278 | -11.722/-29.722/-59.722 | -2.997 | A;C合并代数段 |
| 322/354 | connection:q[198] | R457,R462,R467,R472,R477,R482,R487 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23 | 12544 | 0.2453 | 0.020 | 0 | 纯代数/alias | 14 | 0.274 | -27.726/-69.726/-139.726 | -1.285 | A;C合并代数段 |
| 166/182 | connection:b102 | R465,R470,R475,R480,R485,R490 | 14,15,16,17,18,19,20,21,22,23,24,25 | 10752 | 0.2440 | 0.023 | 0 | 纯代数/alias | 12 | 0.272 | -23.728/-59.728/-119.728 | -1.499 | A;C合并代数段 |
| 321/353 | connection:q[197] | R457,R462,R467,R472,R477,R482,R487 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23 | 12544 | 0.2388 | 0.019 | 0 | 纯代数/alias | 14 | 0.267 | -27.733/-69.733/-139.733 | -1.285 | A;C合并代数段 |
| 324/356 | connection:q[202] | R457,R462,R467,R472,R477,R482,R487 | 10,11,12,13,14,15,16,17,18,19,20,21,22,23 | 12544 | 0.2360 | 0.019 | 0 | 纯代数/alias | 14 | 0.263 | -27.737/-69.737/-139.737 | -1.285 | A;C合并代数段 |
| 167/183 | connection:b103 | R465,R470,R475,R480,R485,R490 | 14,15,16,17,18,19,20,21,22,23,24,25 | 10752 | 0.2334 | 0.022 | 0 | 纯代数/alias | 12 | 0.260 | -23.740/-59.740/-119.740 | -1.500 | A;C合并代数段 |
| 445/453 | alias:amesim_pnpl01_2.port_1 | R462,R467,R472,R477,R482,R487,R492 | 12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 12544 | 0.2320 | 0.018 | 0 | 纯代数/alias | 14 | 0.259 | -27.741/-69.741/-139.741 | -1.286 | A;C合并代数段 |
| 444/452 | alias:amesim_pnpl01_1.port_1 | R462,R467,R472,R477,R482,R487,R492 | 12,13,14,15,16,17,18,19,20,21,22,23,24,25 | 12544 | 0.2265 | 0.018 | 0 | 纯代数/alias | 14 | 0.253 | -27.747/-69.747/-139.747 | -1.286 | A;C合并代数段 |
| 446/454 | alias:amesim_pnpl01_3.port_1 | R467,R472,R477,R482,R487,R492 | 14,15,16,17,18,19,20,21,22,23,24,25 | 10752 | 0.2229 | 0.021 | 0 | 纯代数/alias | 12 | 0.249 | -23.751/-59.751/-119.751 | -1.501 | A;C合并代数段 |
| 168/184 | connection:b104 | R465,R470,R475,R480,R485,R490 | 14,15,16,17,18,19,20,21,22,23,24,25 | 10752 | 0.2211 | 0.021 | 0 | 纯代数/alias | 12 | 0.247 | -23.753/-59.753/-119.753 | -1.501 | A;C合并代数段 |
| 317/349 | connection:q[191] | R457,R462,R467,R472,R477,R482 | 10,11,12,13,14,15,16,17,18,19,20,21 | 10752 | 0.2148 | 0.020 | 0 | 纯代数/alias | 12 | 0.240 | -23.760/-59.760/-119.760 | -1.501 | A;C合并代数段 |
| 318/350 | connection:q[192] | R457,R462,R467,R472,R477,R482 | 10,11,12,13,14,15,16,17,18,19,20,21 | 10752 | 0.2124 | 0.020 | 0 | 纯代数/alias | 12 | 0.237 | -23.763/-59.763/-119.763 | -1.502 | A;C合并代数段 |
| 319/351 | connection:q[193] | R457,R462,R467,R472,R477,R482 | 10,11,12,13,14,15,16,17,18,19,20,21 | 10752 | 0.2111 | 0.020 | 0 | 纯代数/alias | 12 | 0.236 | -23.764/-59.764/-119.764 | -1.502 | A;C合并代数段 |
| 447/455 | alias:amesim_pnpl01_4.port_1 | R467,R472,R477,R482,R487,R492 | 14,15,16,17,18,19,20,21,22,23,24,25 | 10752 | 0.1987 | 0.018 | 0 | 纯代数/alias | 12 | 0.222 | -23.778/-59.778/-119.778 | -1.503 | A;C合并代数段 |
| 320/352 | connection:q[196] | R457,R462,R467,R472,R477,R482 | 10,11,12,13,14,15,16,17,18,19,20,21 | 10752 | 0.1975 | 0.018 | 0 | 纯代数/alias | 12 | 0.220 | -23.780/-59.780/-119.780 | -1.503 | A;C合并代数段 |
| 448/456 | alias:amesim_pnpl01_5.port_1 | R472,R477,R482,R487,R492 | 16,17,18,19,20,21,22,23,24,25 | 8960 | 0.1857 | 0.021 | 0 | 纯代数/alias | 10 | 0.207 | -19.793/-49.793/-99.793 | -1.805 | A;C合并代数段 |
| 170/186 | connection:b106 | R470,R475,R480,R485,R490 | 16,17,18,19,20,21,22,23,24,25 | 8960 | 0.1836 | 0.020 | 0 | 纯代数/alias | 10 | 0.205 | -19.795/-49.795/-99.795 | -1.805 | A;C合并代数段 |
| 171/187 | connection:b107 | R470,R475,R480,R485,R490 | 16,17,18,19,20,21,22,23,24,25 | 8960 | 0.1789 | 0.020 | 0 | 纯代数/alias | 10 | 0.200 | -19.800/-49.800/-99.800 | -1.806 | A;C合并代数段 |
| 313/345 | connection:q[185] | R457,R462,R467,R472,R477 | 10,11,12,13,14,15,16,17,18,19 | 8960 | 0.1765 | 0.020 | 0 | 纯代数/alias | 10 | 0.197 | -19.803/-49.803/-99.803 | -1.806 | A;C合并代数段 |
| 314/346 | connection:q[186] | R457,R462,R467,R472,R477 | 10,11,12,13,14,15,16,17,18,19 | 8960 | 0.1755 | 0.020 | 0 | 纯代数/alias | 10 | 0.196 | -19.804/-49.804/-99.804 | -1.806 | A;C合并代数段 |
| 169/185 | connection:b105 | R470,R475,R480,R485,R490 | 16,17,18,19,20,21,22,23,24,25 | 8960 | 0.1752 | 0.020 | 0 | 纯代数/alias | 10 | 0.196 | -19.804/-49.804/-99.804 | -1.806 | A;C合并代数段 |
| 315/347 | connection:q[187] | R457,R462,R467,R472,R477 | 10,11,12,13,14,15,16,17,18,19 | 8960 | 0.1730 | 0.019 | 0 | 纯代数/alias | 10 | 0.193 | -19.807/-49.807/-99.807 | -1.806 | A;C合并代数段 |
| 308/340 | connection:q[178] | R457,R462,R467 | 10,11,12,13,14,15 | 5376 | 0.1706 | 0.032 | 0 | 纯代数/alias | 6 | 0.190 | -11.810/-29.810/-59.810 | -3.011 | A;C合并代数段 |
| 316/348 | connection:q[190] | R457,R462,R467,R472,R477 | 10,11,12,13,14,15,16,17,18,19 | 8960 | 0.1644 | 0.018 | 0 | 纯代数/alias | 10 | 0.183 | -19.817/-49.817/-99.817 | -1.807 | A;C合并代数段 |
| 449/457 | alias:amesim_pnpl01_6.port_1 | R472,R477,R482,R487,R492 | 16,17,18,19,20,21,22,23,24,25 | 8960 | 0.1624 | 0.018 | 0 | 纯代数/alias | 10 | 0.181 | -19.819/-49.819/-99.819 | -1.808 | A;C合并代数段 |
| 310/342 | connection:q[180] | R457,R462,R467,R472 | 10,11,12,13,14,15,16,17 | 7168 | 0.1525 | 0.021 | 0 | 纯代数/alias | 8 | 0.170 | -15.830/-39.830/-79.830 | -2.261 | A;C合并代数段 |
| 173/189 | connection:b109 | R475,R480,R485,R490 | 18,19,20,21,22,23,24,25 | 7168 | 0.1500 | 0.021 | 0 | 纯代数/alias | 8 | 0.167 | -15.833/-39.833/-79.833 | -2.261 | A;C合并代数段 |
| 311/343 | connection:q[181] | R457,R462,R467,R472 | 10,11,12,13,14,15,16,17 | 7168 | 0.1468 | 0.020 | 0 | 纯代数/alias | 8 | 0.164 | -15.836/-39.836/-79.836 | -2.262 | A;C合并代数段 |
| 172/188 | connection:b108 | R475,R480,R485,R490 | 18,19,20,21,22,23,24,25 | 7168 | 0.1435 | 0.020 | 0 | 纯代数/alias | 8 | 0.160 | -15.840/-39.840/-79.840 | -2.262 | A;C合并代数段 |
| 174/190 | connection:b110 | R475,R480,R485,R490 | 18,19,20,21,22,23,24,25 | 7168 | 0.1435 | 0.020 | 0 | 纯代数/alias | 8 | 0.160 | -15.840/-39.840/-79.840 | -2.262 | A;C合并代数段 |
| 309/341 | connection:q[179] | R457,R462,R467,R472 | 10,11,12,13,14,15,16,17 | 7168 | 0.1425 | 0.020 | 0 | 纯代数/alias | 8 | 0.159 | -15.841/-39.841/-79.841 | -2.262 | A;C合并代数段 |
| 312/344 | connection:q[184] | R457,R462,R467,R472 | 10,11,12,13,14,15,16,17 | 7168 | 0.1358 | 0.019 | 0 | 纯代数/alias | 8 | 0.152 | -15.848/-39.848/-79.848 | -2.263 | A;C合并代数段 |
| 451/459 | alias:amesim_pnpl01_8.port_1 | R477,R482,R487,R492 | 18,19,20,21,22,23,24,25 | 7168 | 0.1349 | 0.019 | 0 | 纯代数/alias | 8 | 0.151 | -15.849/-39.849/-79.849 | -2.263 | A;C合并代数段 |
| 450/458 | alias:amesim_pnpl01_7.port_1 | R477,R482,R487,R492 | 18,19,20,21,22,23,24,25 | 7168 | 0.1348 | 0.019 | 0 | 纯代数/alias | 8 | 0.150 | -15.850/-39.850/-79.850 | -2.263 | A;C合并代数段 |
| 13/13 | flow:amesim_pnor001_7.port_2 | R89,R136,R186,R232,R287,R420 | 2,3,4,5,6,9 | 5376 | 0.1287 | 0.024 | 0 | 纯代数/alias | 6 | 0.144 | -11.856/-29.856/-59.856 | -3.019 | A;C合并代数段 |
| 27/35 | flow:amesim_pnvo001_6.port_3 | R91,R139,R189,R290,R338,R422 | 2,3,4,6,7,9 | 5376 | 0.1272 | 0.024 | 0 | 纯代数/alias | 6 | 0.142 | -11.858/-29.858/-59.858 | -3.019 | A;C合并代数段 |
| 25/33 | flow:amesim_pnvo001_5.port_3 | R2,R139,R234,R290 | 0,1,3,5,6,8 | 5376 | 0.1240 | 0.023 | 0 | 纯代数/alias | 6 | 0.138 | -11.862/-29.862/-59.862 | -3.020 | A;C合并代数段 |
| 31/39 | flow:amesim_pnvo001_8.port_3 | R3,R54,R91,R139,R189,R381 | 0,1,2,3,4,8 | 5376 | 0.1230 | 0.023 | 0 | 纯代数/alias | 6 | 0.137 | -11.863/-29.863/-59.863 | -3.020 | A;C合并代数段 |
| 29/37 | flow:amesim_pnvo001_7.port_3 | R91,R139,R189,R235,R338,R381 | 2,3,4,5,7,8 | 5376 | 0.1209 | 0.022 | 0 | 纯代数/alias | 6 | 0.135 | -11.865/-29.865/-59.865 | -3.020 | A;C合并代数段 |
| 176/192 | connection:b112 | R480,R485,R490 | 20,21,22,23,24,25 | 5376 | 0.1148 | 0.021 | 0 | 纯代数/alias | 6 | 0.128 | -11.872/-29.872/-59.872 | -3.022 | A;C合并代数段 |
| 15/15 | flow:amesim_pnor001_8.port_2 | R89,R136,R232,R336,R379,R420 | 2,3,5,7,8,9 | 5376 | 0.1136 | 0.021 | 0 | 纯代数/alias | 6 | 0.127 | -11.873/-29.873/-59.873 | -3.022 | A;C合并代数段 |
| 11/11 | flow:amesim_pnor001_6.port_2 | R89,R136,R186,R232,R287,R335 | 2,3,4,5,6,7 | 5376 | 0.1128 | 0.021 | 0 | 纯代数/alias | 6 | 0.126 | -11.874/-29.874/-59.874 | -3.022 | A;C合并代数段 |
| 306/338 | connection:q[174] | R457,R462,R467 | 10,11,12,13,14,15 | 5376 | 0.1104 | 0.021 | 0 | 纯代数/alias | 6 | 0.123 | -11.877/-29.877/-59.877 | -3.022 | A;C合并代数段 |
| 305/337 | connection:q[173] | R457,R462,R467 | 10,11,12,13,14,15 | 5376 | 0.1104 | 0.021 | 0 | 纯代数/alias | 6 | 0.123 | -11.877/-29.877/-59.877 | -3.022 | A;C合并代数段 |
| 177/193 | connection:b113 | R480,R485,R490 | 20,21,22,23,24,25 | 5376 | 0.1102 | 0.020 | 0 | 纯代数/alias | 6 | 0.123 | -11.877/-29.877/-59.877 | -3.022 | A;C合并代数段 |
| 175/191 | connection:b111 | R480,R485,R490 | 20,21,22,23,24,25 | 5376 | 0.1049 | 0.020 | 0 | 纯代数/alias | 6 | 0.117 | -11.883/-29.883/-59.883 | -3.023 | A;C合并代数段 |
| 453/461 | alias:amesim_pnpl01_10.port_1 | R482,R487,R492 | 20,21,22,23,24,25 | 5376 | 0.0992 | 0.018 | 0 | 纯代数/alias | 6 | 0.111 | -11.889/-29.889/-59.889 | -3.024 | A;C合并代数段 |
| 452/460 | alias:amesim_pnpl01_9.port_1 | R482,R487,R492 | 20,21,22,23,24,25 | 5376 | 0.0981 | 0.018 | 0 | 纯代数/alias | 6 | 0.109 | -11.891/-29.891/-59.891 | -3.025 | A;C合并代数段 |
| 9/9 | flow:amesim_pnor001_5.port_2 | R89,R136,R186,R232 | 2,3,4,5 | 3584 | 0.0814 | 0.023 | 0 | 纯代数/alias | 4 | 0.091 | -7.909/-19.909/-39.909 | -4.542 | A;C合并代数段 |
| 303/335 | connection:q[169] | R457,R462 | 10,11,12,13 | 3584 | 0.0803 | 0.022 | 0 | 纯代数/alias | 4 | 0.090 | -7.910/-19.910/-39.910 | -4.542 | A;C合并代数段 |
| 179/195 | connection:b115 | R485,R490 | 22,23,24,25 | 3584 | 0.0791 | 0.022 | 0 | 纯代数/alias | 4 | 0.088 | -7.912/-19.912/-39.912 | -4.542 | A;C合并代数段 |
| 302/334 | connection:q[168] | R457,R462 | 10,11,12,13 | 3584 | 0.0762 | 0.021 | 0 | 纯代数/alias | 4 | 0.085 | -7.915/-19.915/-39.915 | -4.543 | A;C合并代数段 |
| 178/194 | connection:b114 | R485,R490 | 22,23,24,25 | 3584 | 0.0743 | 0.021 | 0 | 纯代数/alias | 4 | 0.083 | -7.917/-19.917/-39.917 | -4.544 | A;C合并代数段 |
| 301/333 | connection:q[167] | R457,R462 | 10,11,12,13 | 3584 | 0.0732 | 0.020 | 0 | 纯代数/alias | 4 | 0.082 | -7.918/-19.918/-39.918 | -4.544 | A;C合并代数段 |
| 304/336 | connection:q[172] | R457,R462 | 10,11,12,13 | 3584 | 0.0729 | 0.020 | 0 | 纯代数/alias | 4 | 0.081 | -7.919/-19.919/-39.919 | -4.544 | A;C合并代数段 |
| 180/196 | connection:b116 | R485,R490 | 22,23,24,25 | 3584 | 0.0728 | 0.020 | 0 | 纯代数/alias | 4 | 0.081 | -7.919/-19.919/-39.919 | -4.544 | A;C合并代数段 |
| 454/462 | alias:amesim_pnpl01_11.port_1 | R487,R492 | 22,23,24,25 | 3584 | 0.0720 | 0.020 | 0 | 纯代数/alias | 4 | 0.080 | -7.920/-19.920/-39.920 | -4.544 | A;C合并代数段 |
| 455/463 | alias:amesim_pnpl01_12.port_1 | R487,R492 | 22,23,24,25 | 3584 | 0.0690 | 0.019 | 0 | 纯代数/alias | 4 | 0.077 | -7.923/-19.923/-39.923 | -4.545 | A;C合并代数段 |
| 5/5 | flow:amesim_pnor001_3.port_2 | R88,R185 | 2,4 | 1792 | 0.0492 | 0.027 | 0 | 纯代数/alias | 2 | 0.055 | -3.945/-9.945/-19.945 | -9.101 | A;C合并代数段 |
| 299/331 | connection:q[163] | R457 | 10,11 | 1792 | 0.0464 | 0.026 | 0 | 纯代数/alias | 2 | 0.052 | -3.948/-9.948/-19.948 | -9.103 | A;C合并代数段 |
| 298/330 | connection:q[162] | R457 | 10,11 | 1792 | 0.0454 | 0.025 | 0 | 纯代数/alias | 2 | 0.051 | -3.949/-9.949/-19.949 | -9.104 | A;C合并代数段 |
| 7/7 | flow:amesim_pnor001_4.port_2 | R136,R286 | 3,6 | 1792 | 0.0448 | 0.025 | 0 | 纯代数/alias | 2 | 0.050 | -3.950/-9.950/-19.950 | -9.104 | A;C合并代数段 |
| 183/199 | connection:b119 | R490 | 24,25 | 1792 | 0.0422 | 0.024 | 0 | 纯代数/alias | 2 | 0.047 | -3.953/-9.953/-19.953 | -9.105 | A;C合并代数段 |
| 3/3 | flow:amesim_pnor001_2.port_2 | R185,R231 | 4,5 | 1792 | 0.0421 | 0.023 | 0 | 纯代数/alias | 2 | 0.047 | -3.953/-9.953/-19.953 | -9.105 | A;C合并代数段 |
| 182/198 | connection:b118 | R490 | 24,25 | 1792 | 0.0411 | 0.023 | 0 | 纯代数/alias | 2 | 0.046 | -3.954/-9.954/-19.954 | -9.106 | A;C合并代数段 |
| 297/329 | connection:q[161] | R457 | 10,11 | 1792 | 0.0405 | 0.023 | 0 | 纯代数/alias | 2 | 0.045 | -3.955/-9.955/-19.955 | -9.106 | A;C合并代数段 |
| 300/332 | connection:q[166] | R457 | 10,11 | 1792 | 0.0377 | 0.021 | 0 | 纯代数/alias | 2 | 0.042 | -3.958/-9.958/-19.958 | -9.108 | A;C合并代数段 |
| 456/464 | alias:amesim_pnpl01_13.port_1 | R492 | 24,25 | 1792 | 0.0371 | 0.021 | 0 | 纯代数/alias | 2 | 0.041 | -3.959/-9.959/-19.959 | -9.108 | A;C合并代数段 |
| 457/465 | alias:amesim_pnpl01_14.port_1 | R492 | 24,25 | 1792 | 0.0367 | 0.020 | 0 | 纯代数/alias | 2 | 0.041 | -3.959/-9.959/-19.959 | -9.108 | A;C合并代数段 |
| 181/197 | connection:b117 | R490 | 24,25 | 1792 | 0.0363 | 0.020 | 0 | 纯代数/alias | 2 | 0.041 | -3.959/-9.959/-19.959 | -9.109 | A;C合并代数段 |
@@ -0,0 +1,219 @@
# Context fallback:盈利条件与下一步决策
日期:2026-09-17。范围:既有八路模型 0–10 s、896 个 Jacobian 的历史诊断数据。本轮只做数据归并和预算分析,没有构建新 worker、修改求解器、扩展 semantic replay 或重跑性能实验。
## 结论
**停止按单个 operation 推进 semantic replay。下一次只值得先测 `position379 / PNVO001_1` 中 `state_valve` 数值尾部的 kernel memo。** 原 context 查询、物性写入、valid 演变和 native 调用仍照常发生;这与跳过整个 operation 是不同实验。
- 当前 343 个 fallback operation 的跨组单次均值最高只有 **1.315 µs**;即使拆到 5,157 个 `(group, position)` 组合,最高均值也只有 **1.642 µs**。没有一个能承担当前 **7.109 µs** 的单 operation replay 开销,尚未计 baseline。
- 较贵 interval 是多个廉价 operation 的集合。不能把整个 interval 的预算发给其中每个 operation。
- whole-context guard 成功的 **20,608 次**继续使用原机制;失败的 **139,776 次**不能直接恢复 baseline 全 context,其真实出口均与 baseline context 不同。
- 不继续优化 R288,不启动 position52、R475 或其他区间的 semantic replay 实现。R490 等长区间只保留为预算上的备选,尚不具备立即实验的收益与语义证据。
## 1. 数据覆盖与计时口径
| 数据层 | 覆盖 / 结果 | 本报告用途 |
|---|---|---|
| 全量 census | 115 个失败 interval;156 个 group/interval;343 个 position;5,157 个 group/position | 次数、归属和摊销上限 |
| fallback 原计算 | 139,776 次 interval;4,620,672 次 operation 执行 | 去重后的成本分母 |
| 分层 profiling,扣空标记估计 | 全轨迹 context fallback **613.201 ms** | 总量交叉检查 |
| interval 独立计时 | **682.630 ms** | 原区间预算;含区间调度及诊断 hook,不含失败前的 guard |
| operation 独立计时 | **660.431 ms** | 分离 native 语句与纯代数/alias;operation 预算 |
| function 独立计时 | exclusive 合计 **879.118 ms** | 函数排序,不能加到以上时间或视为净收益 |
interval、operation、function 各三轮;每轮分层抽取 112/896 个 Jacobian,按频率折算全轨迹后取三轮算术平均。这里的“µs/次”是累计折算时间除以全量次数,不是单次延迟分位数。完整表另外保留三轮值和 min/max。
这些是**带插桩的预算估计**。极短 operation 的计时可能明显高估;函数 exclusive 仅扣除已插桩子调用。不同模式的差值不能直接当作 dispatch 成本,更不能叠加。R288 最新实验的机器负载与旧 profiling 不同,跨批代入只用于成本量级情景,不能预测加速。
### 可查验的完整交付物
- [115 个 interval、156 个 group/interval 的全部成本和预算,以及 196 个纯代数连续段](fallback_profitability_intervals.md)。按 `次数 × 单次成本` 排序,含 operation/native 数量、纯代数时间、主要函数、共享上限。
- [343 个 operation 的全部热点和预算](fallback_profitability_operations.md)。含 interval、group、原始 operation ID、原生调用、次数、时间、摊销边界与建议。
- [完整 JSON](../../test/fallback-profitability-20260917/analysis.json):另含全部 5,157 条 group/position 映射、原代码、逐区间函数数据、三轮折算原始数值、输入 SHA-256、预算情景和检查结果。
- [复现脚本](analyze_fallback_profitability.py):只读取历史产物。运行 `.venv-win/Scripts/python.exe -B tests/manual/analyze_fallback_profitability.py`。
原始依据:[fallback 诊断](../../test/context-fallback-20260917/report.md)、[原始统计](../../test/context-fallback-20260917/analysis.json)、[代码及依赖计划](../../test/context-fallback-20260917/plan.json)、[分层 profiling](local_probe_profile.md)、[R288 typed 实验](r288_typed_replay_report.md)。当前工作区其他改动未作为这份历史测量的重新验收对象。
## 2. 热点:大区间与廉价 operation 必须分开
### 累计成本最高的区间及重要对照
“纯代数 ms”来自 operation 模式,只计完全不含 native 调用的语句;不包含 native 内部的代数部分,后者尚无独立测量。
| interval / 范围 | group | 次数 | 原区间累计 ms | 原区间 µs/次 | ops / native ops | 纯代数 ms |
|---|---|---:|---:|---:|---:|---:|
| R490 [49,184) | 24,25 | 1,792 | 33.807 | 18.865 | 135 / 15 | 4.139 |
| R485 [50,181) | 22,23 | 1,792 | 31.667 | 17.671 | 131 / 14 | 4.072 |
| R480 [51,178) | 20,21 | 1,792 | 30.269 | 16.891 | 127 / 13 | 3.954 |
| R475 [52,175) | 18,19 | 1,792 | 27.257 | 15.211 | 123 / 12 | 3.841 |
| R470 [53,172) | 16,17 | 1,792 | 25.381 | 14.164 | 119 / 11 | 见完整表 |
| R465 [54,169) | 14,15 | 1,792 | 22.936 | 12.799 | 115 / 10 | 见完整表 |
| R460 [55,166) | 12,13 | 1,792 | 22.117 | 12.342 | 111 / 9 | 见完整表 |
| R3 [30,48) | 0 | 896 | 21.393 | 23.876 | 18 / 17 | 0.033 |
| R455 [56,161) | 10,11 | 1,792 | 20.735 | 11.571 | 105 / 8 | 见完整表 |
| R457 [297,444) | 10,11 | 1,792 | 15.880 | 8.862 | 147 / 4 | 5.653 |
| R49 [373,444) | 0 | 896 | 15.782 | 17.614 | 71 / 4 | 1.814 |
| R288 [16,17) | 6 | 896 | 0.918 | 1.024 | 1 / 1 | 0 |
R490–R455 前半区域主要是 `native_pipe_flow_cached_context`,包含 PH context 获取和 `state_valve` 等链路;R457/R49 的四个 native 是 `native_medium_orifice_context`。R3 的 17 个 native 集中在管路/节流流量,因而短区间也可能更贵。
**R49 暂不作首选**:区间模式是 17.614 µs/次,operation 模式总和却只有 7.283 µs/次。三轮区间值都偏高,说明不是简单挑掉一轮就能解决;尚未分离具体原因。不能把两者差额承诺为可消除的调度成本。R3 也存在 23.876 vs 20.471 µs 的模式差异,预算应同时参考两种口径。
group 0–9 合计 **361.823 ms**,group 10–25 合计 **320.807 ms**,group26 为 0。大区间并未占据全部 fallback。
### 累计最贵的几个 operation
| position / 原ID | operation | 失败执行次数 | 原计算累计 ms | µs/次 | 每 Jacobian 结构复用上限 |
|---|---|---:|---:|---:|---:|
| 379 / 24 | PNVO001_1.port_2 | 19,712 | 25.926 | 1.315 | 22 |
| 405 / 28 | PNVO001_3.port_2 | 19,712 | 24.907 | 1.264 | 22 |
| 392 / 26 | PNVO001_2.port_2 | 19,712 | 24.721 | 1.254 | 22 |
| 80 / 88 | PNL00R_4.port_1 | 19,712 | 24.410 | 1.238 | 22 |
| 82 / 90 | PNL00R_5.port_1 | 19,712 | 23.721 | 1.203 | 22 |
| 418 / 30 | PNVO001_4.port_2 | 19,712 | 23.021 | 1.168 | 22 |
重复 22 次让 baseline 有更好的摊销机会,**不能让每次 1.315 µs 的计算承担 7 µs 的 probe 成本**。同一 position 的 baseline 可以在结构上被这些组引用,但查询分支、消费字段、kernel 参数是否相同,仍须单独验证。现有“显式 operation 输入/输出相同”不是这种证明。
## 3. Break-even:必须给 baseline 与 probe 共用一个预算
令 `C` 为每次可省的原计算,`P` 为 validation + patch + commit + 新增调度等 probe 开销,`H` 为每个 Jacobian 新增 baseline 捕获成本,`m` 为这份记录可成功服务的 probe 数。单位均为 µs:
```text
net_per_J = m × (C − P) − H
P < C − H/m
H < m × (C − P)
```
不能同时把 `C` 分别全部分配给 H 和 P。表中 H 上限以 P=0 或指定值计算;只有严格低于上限才盈利。暂用全部成功、无新增失败成本的乐观条件。
若仅有 `k` 次成功、其余尝试付出失败 guard 成本 `F`:
```text
net_per_J = k × (C_success − P_success) − H − (attempts − k) × F
```
同一区间跨组最多共享一次 baseline 的结构上限通常是 1 或 2;operation 跨重叠区间可达到 22。**不能把同一 group/position 在多个 interval 中重复计价**,本分析按 census 的唯一归属去重。若动态 schema 无法共享,m 必须减小;如果要按组捕获 H,则不能继续除以全部组数。
### 用当前 R288 typed 成本作量级筛选
同批实测中位数:原 operation **1.775 µs**,typed 三阶段 **7.109 µs**,完整路径 **7.575 µs**,baseline 新增 **18.258 µs/J**。计入 baseline 的配对净收益为 **−20.415 ms/896 次**;所有轮次均为负。旧批的 1.143/22.521 µs 不能和最新批混为一组对照。
以下把 `H=18.258` 仅作为预算情景;P 也必须覆盖新增调度/统计等成本,使用三阶段 7.109 已比完整路径更乐观。
| 候选 | C µs | m 上限 | Hmax,P=0 | Hmax,P=5 | Pmax,H=18.258 | P≈7.109 的判断 |
|---|---:|---:|---:|---:|---:|---|
| R288,最新同批 native | 1.775 | 1 | 1.775 | −3.225 | −16.483 | 即使H=0也不行;停止 |
| position379,原 operation | 1.315 | 22 | 28.935 | −81.065 | 0.485 | 单operation replay不行 |
| R490,整个区间 | 18.865 | 2 | 37.731 | 27.731 | 9.736 | 一次融合replay账面可行;未验证 |
| R485,整个区间 | 17.671 | 2 | 35.342 | 25.342 | 8.542 | 同上 |
| R480,整个区间 | 16.891 | 2 | 33.783 | 23.783 | 7.763 | 余量很小 |
| R475,整个区间 | 15.211 | 2 | 30.421 | 20.421 | 6.082 | 当前量级不行;不扩展 |
| R3,整个区间 | 23.876 | 1 | 23.876 | 18.876 | 5.619 | 需低于约5µs;op口径仅允许2.213µs |
| R457,整个区间 | 8.862 | 2 | 17.724 | 7.724 | −0.267 | baseline本身已超预算 |
R490 包含 15 个 native operation,若逐个使用 7.109 µs replay,单 probe 仅 replay 就要约 **106.636 µs**,远超整个区间 18.865 µs。表中可行性只适用于**整个区间一次融合处理**。不能假设 15 个 operation 的查询、字段保护与副作用能以一个 R288 的成本处理。
若要求每个长区间 `P<2/5/10 µs`,各自允许的 baseline 成本已在完整表逐项列出;并非只给一个统一的 7 µs 门槛。所有单 operation 的原测量均值都低于 2 µs,甚至 `<2 µs` 本身也不足以证明它们盈利。
## 4. 按真正的函数成本选择方法
以下 exclusive 时间来自函数模式,只作排名,不与 operation 模式相加。`state_valve` exclusive 包括未插桩的数学函数和包装等,**不是已经单独测出的纯数值尾部成本**。
| 函数 / 工作 | 调用数 | exclusive ms | 平均 exclusive µs | 建议 |
|---|---:|---:|---:|---|
| state_valve | 495,790 | 388.070 | 0.783 | E:先测数值尾部;保留所有context访问 |
| property_pt | 1,225,458 | 114.822 | 0.094 | F:若未来优化查找,必须保持有序first-match;不能承担µs级额外guard |
| local_isentropic | 989,704 | 92.872 | 0.094 | 现有valid命中已跳过部分计算;需先分开hit与真实重算,不能整体memo掉observe/valid写入 |
| native_jacobian_scalar_get | 1,439,304 | 62.124 | 0.043 | 既有memo查询成本;避免再叠通用查表/完整key解释器 |
| property_density | 1,399,544 | 52.772 | 0.038 | 主要是cache/memo路径,保持原机制 |
| native_pipe_flow_context | 379,904 | 50.607 | 0.133 | inclusive约601.523ms包含下游;不能再加到state_valve |
| native_temperature_ph_context | 320,836 | 43.470 | 0.135 | 查询/包装仍发生,实际PH反算为0 |
| native_viscosity | 307,709 | 25.702 | 0.084 | A;单独加memo通常预算太小,尚无盈利证据 |
| native_medium_orifice_context | 125,440 | 17.183 | 0.137 | 包装本身廉价,关注其state_valve数学部分 |
| native_pipe_flow_cached_context | 379,904 | 16.638 | 0.044 | 保留pipe语义;不要为省wrapper引入replay |
`native_temperature_ph`、`native_density`、`native_pipe_resistance` 在这批 fallback 中实际执行次数都为 **0**。它们的昂贵求解已被 Jacobian memo 覆盖,不能再次把这些计算算成新优化的可省部分。
源码依据:归档 [properties.c](../../test/local-probe-20260917/worker/properties.c) 中 `state_valve` 先执行 `isentropic` 与 `property_density`,之后才进行 `pow/sqrt/log/tanh` 等数学计算;[orifice.c](../../test/local-probe-20260917/worker/orifice.c) 的 PNVO 调用链还会获取 PH/PT、更新context并进行有限性返回检查。整个 `state_valve` 并不是无副作用的纯函数。
### A–F 明确决策
| 类型 | 适用对象 | 决策 |
|---|---|---|
| A 直接原计算 | R288/position16;所有当前需要约7µs replay的单operation;廉价lookup/viscosity/alias | 停止逐operation semantic replay;默认重算 |
| B 原whole-context restore | 现有guard成功的20,608次context复用 | 保留原guard与restore;不扩大到guard失败路径 |
| C 切分interval | 196个完全不含native的连续段;例R457 [297,379) | 只考虑批量段和低成本输出恢复;不逐alias加guard |
| D 局部context replay | R490/R485/R480的融合区间是假设候选 | 仅预算保留;没有全区间读写契约,也无足够实测收益,暂不实施 |
| E 昂贵纯数值kernel memo | 首选position379中的state_valve数值尾部 | 下一次唯一建议实验;先测小于µs的真实成本与key复用 |
| F 更便宜方案 | property_pt有序查询、现有memo定位、静态代数段恢复 | 先证明确有热点且额外成本在几十至数百ns预算内;不作为本次扩展任务 |
B 的全部成功区间为 R0、R135、R378、R454、R459、R464、R469、R474、R479、R484、R489、R494。只沿用已经满足原 guard 的 group/interval 对,不由区间ID推导新的复用范围。
## 5. 候选排序:只推荐启动一个
### 第一名:position379 / PNVO001_1 的 state_valve 数值尾部
- 当前完整 operation 为 **1.315 µs/次**,19,712 次,累计 **25.926 ms**。三轮累计 25.631–26.268 ms。组为 0–5、10–25,共22组。完整 group→interval 映射在JSON中。
- 主要工作是 `native_medium_orifice_context → medium_valve → state_valve`。保留 PH/PT、等熵字段计算/valid写入、density读取及所有context副作用,**只考虑在这些步骤之后**复用数学尾部的 `cm/velocity`。
- 为什么优于 R288 replay:不是因为单次 operation 更贵,而是换成只比较少量最终数值输入的 kernel 机制,避免有序条目重放;baseline 在结构上最多可摊到22组,而 R288 的目标只有1组。候选还是累计最贵的单个 operation,且数值尾部边界可明确核对。
- **可省上界**:25.926 ms 是整个 operation 零成本消失的宽松上界;数值尾部只占其中一部分,实际可省必须更小。没有该position的尾部独立计时,不能把全局0.783µs当作它的实测尾部成本,更不能承诺25.926ms收益。
- 令尾部实测每次成本为 `K`、memo probe成本为 `G`、baseline新增为 `H`,全部22组可命中时必须满足 `G + H/22 < K < 1.315 µs`。一轮净收益是 `19712×(K−G)−896×H` µs;命中不足时另扣miss开销。
- 初始工程目标可设 `G≤0.25 µs`、`H≤1 µs/J`,其成本门槛约 **0.295 µs/命中**;这是待测目标,不是已有实现速度。若尾部K≤该值或实际开销不达标,则停止。若仍沿用18.258µs的baseline新增,则仅H/22就要0.830µs,很可能再次失败。
**下一次实验的边界**:仍用独立 worker,只选这个 position;先采集数学尾部的实际输入位与成本,确认同一Jacobian跨组key重复比例,再实现可拒绝的最小kernel memo。候选key应依据尾部真实使用的 `p/T/pd/g/rho` 等值确定,不拿operation显式输入相同代替。必须核对分支、非有限值、errno/浮点状态、memo生命周期;数学库也可能有副作用,不能默认忽略。
数值尾部不再触碰property context有利于验证,但不是免验。要求所有矩阵、求解输出、context/pipe/memo、warning和计数逐位一致;成本版无shadow。测量命中/拒绝、尾部原成本、key检查、命中返回和baseline新增成本,只有完整机制净收益为正才考虑其他position。**本轮没有实施这个实验。**
### 第二名(备选,不立即启动):R457 的纯代数连续段
R457/group10,11 整体 8.862µs、15.880ms;主要native仍是4个节流口。真正切分对象为 **[297,379)** 的82个纯代数operation,平均段成本 **1.846µs**,1,792次合计 **3.308ms**。这里不省任何native物理计算,保留全部context操作,可验证性较高。
选择C而非D:每段额外guard/restore加baseline摊销必须 `<1.846µs`,实际应显著低于1µs以留下余量。由于82个短语句分别计时,3.308ms可能含明显marker成本,先用段级低扰动测量核实;不能把该数字当作可兑现收益。全部代数段都低于2µs,逐条加机制会失败。它比R288 replay更简单,但单点总上界较小,排在kernel之后。
### 第三名(预算备选,暂不做):R490 的融合局部context机制
R490/group24,25 整体 **18.865µs**,1,792次 **33.807ms**;其中15个native语句,主要是pipe flow、state_valve及查询。两个组可结构共享一次baseline,预算明显比R288宽。
若H仍为18.258µs,**全部验证、所有patch和commit合计必须低于9.736µs/整个interval**;要有可靠余量,目标至少应低于5µs。若P=7.109、100%成功且两组共享记录,账面仅剩约 **4.708ms**,尚未扣完整路径额外开销。未知读写覆盖、15个native的effects以及guard扩张很容易用完预算,因此不建议现在开始完整semantic实现。
相比之下,先把state_valve里的数学部分分离,能检验是否存在更低成本方法,再决定是否需要复杂的融合局部context。R485/R480也是同类预算备选,不扩展实现。
## 6. 还剩多少空间:条件上界,不是加速承诺
### 以完整interval只付一次开销的乐观情景
每行只统计满足预算的interval,并扣除该行假设的P/H;各行是不同情景,不能相加。H每个Jacobian捕获一次并跨所有失败组共享,100%接受,无新增失败开销。跨批typed数字只用作量级参照。
| 假设每interval的P / baseline H,µs | 过预算interval数 | 涉及原fallback ms | 占原区间fallback | 情景剩余净空间 ms |
|---|---:|---:|---:|---:|
| 2 / 0 | 72 | 613.980 | 89.94% | 438.364 |
| 5 / 0 | 35 | 496.359 | 72.71% | 249.959 |
| 7.109 / 0 | 24 | 415.138 | 60.81% | 160.350 |
| 10 / 0 | 13 | 284.669 | 41.70% | 96.509 |
| 2 / 18.258 | 9 | 235.564 | 34.51% | 57.870 |
| 5 / 18.258 | 6 | 169.775 | 24.87% | 22.341 |
| 7.109 / 18.258 | 3 | 95.742 | 14.03% | 8.448 |
| 10 / 18.258 | 0 | 0 | 0% | 0 |
因此,“高成本interval占60.81%”只对应零baseline、整个interval固定7.109µs的理想条件。计入当前baseline量级后缩到14.03%,而这14.03%也只是**原计算覆盖量**,扣成本后仅8.448ms。三个区间在独立operation模式下也过相同账面门槛,但仍未证明融合实现可以达到该成本。
若每个group都要独立捕获,H不能被2组摊薄;在P=7.109/H=18.258情景下,所有interval都不再盈利。当前没有任何一个新interval被证明具有实际semantic replay净收益。
### 换方法后的空间
- **单operation semantic replay,维持当前成本量级:可盈利覆盖为0%。** 这不是证明所有更便宜的semantic方法都不可能,只是否定当前这条逐operation路径。
- **native语句工作池:577.486ms,占operation模式87.44%。** 这是55个pipe/orifice语句全部计算的宽松上界,里面有必须保留的context访问和现有memo开销,不能全部归给kernel优化。
- **四个PNVO候选position379/392/405/418:98.575ms的整个operation上界。** 下一次只测379,其25.926ms上界之外不先承诺扩展。kernel尾部占比和实际命中率尚未测出,所以不能给出“已值得优化的kernel占fallback百分比”。
- **纯代数/alias:82.944ms,占operation模式12.56%。** 分布在196个连续段,最大段均值也只有1.846µs。只能考虑少数长段的廉价批量恢复;全量消除的82.944ms是含短语句插桩的宽松上界。
- 全部fallback原工作约 **0.61–0.68s/轨迹** 只是所有相关工作免费消失的极宽上界,不能当成可实现的净节省,也不能由此推算最终solver加速比例。其余Jacobian/积分成本仍然存在。
**方向调整**:R288负收益证明的是低成本operation不适合当前这种replay机制,不是semantic replay原理不可行。眼下数据并未发现单次昂贵的fallback operation;发现的是大量重复的小计算,以及由它们组成的大interval。下一步应验证**低成本的kernel复用能否赚回自己的开销**,而不是继续扩大context重放范围。
## 7. 本轮验证与未确定项
分析脚本重新从三轮原始ticks/frequency/采样数还原interval和operation时间,与原analysis逐项核对;检查全部计数、唯一group/position归属、native分类一致性、代数段和各层汇总守恒。生成的JSON保留输入哈希和检查清单。没有用容差修改仿真结果,也没有新增数值正确性声明。
仍未测量:候选379的数学尾部专属成本与key命中率、长区间融合metadata成本、跨组动态契约共享率、切分后低扰动段成本,以及R49跨模式计时差异的成因。这些缺口决定了目前可以提出可证伪的性能实验,不能宣布新的优化已经盈利。
**最终选择:保留whole-context机制和廉价原计算;停止R288 replay;下一次只做position379的state_valve数值尾部kernel memo成本/正确性实验。**
+40
View File
@@ -0,0 +1,40 @@
**局部 Jacobian probe 实验的运行说明**
这是独立 Windows native worker 实验,不改变生产默认求解路径。输入默认为八路模型 `tests/data/test-mql-8-corrected.json`。生成源码、可执行文件、计数、二进制结果及报告保存在 `test/local-probe-20260917/`。
先构建验证版本,再按 group 26 → group 18–26 → 全部 group 验证;发现任意 dy/w 差异时程序立即终止。
```powershell
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py prepare --audit
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --audit --label reference-audit --mask 0
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --audit --label group26-final-audit --mask 0x4000000
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --audit --label small-final-audit --mask 0x7fc0000
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --audit --label all-audit --mask 0x7ffffff
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py guard
& .venv-win/Scripts/python.exe -B -m unittest tests.test_local_probe_experiment
```
再构建不含影子计算、矩阵落盘和内核入口计数的性能版本。先预热,然后交替运行五对;测量期间不要并行编译或启动其他仿真。
```powershell
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py prepare
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --label reference-warmup --mask 0
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --label all-warmup --mask 0x7ffffff
for ($probePair = 0; $probePair -lt 5; $probePair++) {
if ($probePair % 2 -eq 0) {
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --label "reference-run-$probePair" --mask 0
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --label "all-run-$probePair" --mask 0x7ffffff
} else {
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --label "all-run-$probePair" --mask 0x7ffffff
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py run --label "reference-run-$probePair" --mask 0
}
}
& .venv-win/Scripts/python.exe -B tests/manual/local_probe_experiment.py cold
& .venv-win/Scripts/python.exe -B tests/manual/compare_local_probe.py
```
重复使用相同 label 会覆盖该实验目录下的同名运行证据。比较器针对默认八路、0–10 s 的验收集,其他模型或终止时间应使用独立输出目录和对应验收配置,不能混入当前比较集。
`plan.json` 保存每组扰动状态、保守依赖范围、执行区间、上下文检查点及操作输入输出;`comparison.json` 包含逐组实际执行/跳过计数、所有矩阵比对结果和全部计时样本;`report.md` 为中文分析。
实验限制:只接受已有 canonical Jacobian reuse 且无循环调度块的生成模型;支持最多63组。property context 和 pipe cache 采用逐位保护,时间或组外状态变化会失效。运行期对象属于独立单次 worker,不能直接将该代码嵌入同进程并行求解。audit额外计算不进入求解器nfev,单独计入auditComparisons;性能测量必须使用未开启audit的worker。
+292
View File
@@ -0,0 +1,292 @@
"""Isolated Jacobian-only experiment. Never installs a production build.
prepare builds a reference worker and an opt-in local-probe worker. The latter
keeps the original model_eval_internal verbatim and adds a canonical clone.
Only compiler-owned, acyclic schedule operations are eligible for skipping.
Exact live property-context and pipe-cache guards preserve graph-external
cache semantics. An audit compares every probe's complete dy/w to the original.
"""
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import argparse, hashlib, json, os, re, shutil, subprocess, sys, time
ROOT=Path(__file__).resolve().parents[2]
sys.path.insert(0,str(ROOT))
from app.main import compile_system_xml_network
from app.simulation.native_codegen.input import load_input
from app.simulation.native_codegen.compiler import compile_native_program
from app.simulation.native_codegen.jacobian import StateDependencies
from app.simulation.native_codegen.schedule import EvaluationSchedule
from app.simulation.native_codegen import build as builder
OUT=ROOT/'test/local-probe-20260917'
TEMPLATE=Path(__file__).with_name('local_probe_support.c')
KERNELS=[('properties','native_medium_gas'),('properties','native_temperature_ph'),
('properties','native_density'),('properties','native_viscosity'),('properties','state_valve'),
('properties','property_pt'),('properties','local_isentropic'),
('pipe','native_pipe_resistance'),('pipe','native_pipe_flow_context'),
('pipe','native_pipe_diagnostics_context'),('orifice','native_medium_orifice_context')]
def write(path,value):path.write_text(json.dumps(value,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
def replace(s,old,new):
assert s.count(old)==1,(old,s.count(old));return s.replace(old,new)
def function_span(s,name):
m=re.search(r'(?m)^(?:static\s+|NATIVE_COMPONENT_INTERNAL\s+)?(?:int|double|void|NativePropertyState\s*\*)\s*'+re.escape(name)+r'\([^;{}]*\)\s*\{',s)
assert m,name
# Generated/native reviewed functions have no braces in string literals in
# the functions selected below. Mask comments and strings for brace matching.
masked=re.sub(r'/\*[\s\S]*?\*/|//[^\n]*|"(?:\\.|[^"\\])*"',lambda x:' '*len(x[0]),s)
b=masked.index('{',m.start());depth=1;e=b+1
while depth:depth+=(masked[e]=='{')-(masked[e]=='}');e+=1
return m.start(),b,e
def dependency_masks(d):
keys=set(d.seeds)|set(d.inputs)|set().union(*d.inputs.values())
unknown=keys-set(d.seeds)-set(d.inputs);full=(1<<d.state_count)-1
masks={k:full if k in unknown else d.seeds.get(k,0) for k in keys}
for _ in range(len(keys)+1):
changed=False
for k,inputs in d.inputs.items():
value=masks[k]
for ref in inputs:value|=masks[ref]
if value!=masks[k]:masks[k]=value;changed=True
if not changed:return masks,unknown
raise AssertionError('dependency closure did not converge')
def capture(model):
saved={};old_build=StateDependencies.build;old_emit=EvaluationSchedule.emit
def dep(self):saved['dependencies']=self;return old_build(self)
def emit(self):
saved['schedule']=self;result=old_emit(self);saved['schedule_lines']=result[1];return result
StateDependencies.build=dep;EvaluationSchedule.emit=emit
try:
_,doc=load_input(model);network=compile_system_xml_network(doc);program=compile_native_program(network)
finally:StateDependencies.build=old_build;EvaluationSchedule.emit=old_emit
return program,saved
def generate(program,saved):
schedule=saved['schedule'];d=saved['dependencies'];structure=d.build()
if not structure.enabled or not program.manifest()['jacobianStructure']['reuse']['enabled']:
raise ValueError('Experiment requires eligible canonical Jacobian reuse')
if any(b.cyclic for b in schedule.blocks):
raise ValueError('Experimental local schedule rejects SCCs; use the unmodified worker')
if structure.color_count>63:raise ValueError('Experimental group mask supports at most 63 groups')
order=[b.members[0] for b in schedule.blocks];ops=[schedule.computations[i] for i in order]
masks,unknown=dependency_masks(d);full=(1<<d.state_count)-1
opmask=[0]*len(ops)
for i,op in enumerate(ops):
for ref in op.inputs:opmask[i]|=masks.get(ref,full)
if any(not re.fullmatch(r'(?:p|h|q|w|fb)\[\d+\]',v) for v in op.outputs):
raise ValueError('Unsupported local operation outputs')
regions=[];plans=[];groups=[]
for color in range(structure.color_count):
states=[j for j,c in enumerate(structure.colors) if c==color]
mask=sum(1<<j for j in states);affected=[bool(v&mask) for v in opmask]
plan=[-1]*len(ops);i=0
while i<len(ops):
if affected[i]:i+=1;continue
start=i
while i<len(ops) and not affected[i]:i+=1
region=(start,i)
if region not in regions:regions.append(region)
plan[start]=regions.index(region)
plans.append(plan)
groups.append(dict(color=color,stateIndices=states,states=[program.state_keys[i] for i in states],
affectedOperations=[order[i] for i,v in enumerate(affected) if v],
regions=[list(regions[x]) for x in plan if x>=0]))
boundaries=sorted({x for pair in regions for x in pair})
# Pure assignments cannot mutate either context. Share their checkpoint
# slot, retaining a new checkpoint after every unreviewed/native call.
pure_calls={'if','for','sizeof','fmax','fmin','fabs','sqrt','copysign','pow'}
versions=[0]
for op in ops:
calls=set(re.findall(r'\b([A-Za-z_]\w*)\s*\(', '\n'.join(op.code)))
versions.append(versions[-1]+bool(calls-pure_calls))
checkpoints=sorted({versions[x] for x in boundaries})
boundary={x:checkpoints.index(versions[x]) for x in boundaries}
source=program.source
dims={name:int(re.search(pattern,source)[1]) for name,pattern in {
'NP':r'double p\[(\d+)\]', 'NQ':r'h\[(\d+)\]', 'NFB':r'double fb\[(\d+)\]',
'NPC':r'NativePipeCache pipe_cache\[(\d+)\]', 'NPS':r'NativePropertyState property_states\[(\d+)\]'}.items()}
outputs=[];offsets=[0]
arraynames=['p','h','q','w','fb']
for op in ops:
for target in op.outputs:
a,n=re.fullmatch(r'(p|h|q|w|fb)\[(\d+)\]',target).groups();outputs.append((arraynames.index(a),int(n)))
offsets.append(len(outputs))
macros={'NC':structure.color_count,'NO':len(ops),'NR':len(regions),'NB':len(checkpoints),'NK':len(KERNELS),**dims}
header='''#ifndef LOCAL_PROBE_EXPERIMENT_H
#define LOCAL_PROBE_EXPERIMENT_H
#include "model.h"
#include <stdint.h>
'''+''.join(f'#define LP_{k} {v}\n' for k,v in macros.items())+'''
extern int lp_color,lp_active,lp_capture,lp_observe;
extern uint64_t lp_mask;
extern const int lp_plan[LP_NC][LP_NO],lp_end[LP_NR];
void lp_start(void);void lp_finish(void);void lp_begin(double,const double*);void lp_ready(int);
void lp_snapshot(int,NativePropertyCache*,NativePipeCache*);
void lp_save(double*,double*,double*,double*,double*);
int lp_reuse(int,NativePropertyCache*,NativePipeCache*,double*,double*,double*,double*,double*);
#if LP_OBSERVE
void lp_operation(int);
#else
#define lp_operation(position) ((void)(position))
#endif
void lp_kernel(int);int lp_valid(double,const double*);
void lp_note_eval(int);
void lp_compare(int,const double*,const double*,int,const double*,const double*);
void lp_matrix(double,const double*,const double*);void lp_event(double,const double*);
uint64_t lp_tick(void);void lp_jac_time(uint64_t);void lp_newton(long,long);
int lp_eval(double,const double*,double*,double*,ModelJacobianWorkspace*);
#endif
'''
declarations='const int lp_end[LP_NR]={'+','.join(str(b) for a,b in regions)+'};\n'
declarations+='const int lp_plan[LP_NC][LP_NO]={'+','.join('{'+','.join(map(str,p))+'}' for p in plans)+'};\n'
declarations+='static const int lp_before[LP_NR]={'+','.join(str(boundary[a]) for a,b in regions)+'};\n'
declarations+='static const int lp_after[LP_NR]={'+','.join(str(boundary[b]) for a,b in regions)+'};\n'
declarations+='static const int lp_first_output[LP_NR]={'+','.join(str(offsets[a]) for a,b in regions)+'};\n'
declarations+='static const int lp_last_output[LP_NR]={'+','.join(str(offsets[b]) for a,b in regions)+'};\n'
declarations+='static const int lp_start_op[LP_NR]={'+','.join(str(a) for a,b in regions)+'};\n'
declarations+='static const int lp_output_map[][2]={'+','.join('{'+str(a)+','+str(i)+'}' for a,i in outputs)+'};\n'
declarations+='static const int lp_operation_id[LP_NO]={'+','.join(map(str,order))+'};\n'
declarations+='static const int lp_colors[NSTATES]={'+','.join(map(str,structure.colors))+'};\n'
declarations+='static const char *lp_kernel_names[LP_NK]={'+','.join(json.dumps(f) for _,f in KERNELS)+'};\n'
# Keep the original entire model evaluator byte-for-byte. Add an opt-in clone
# whose only numerical statement change is replacement of the schedule.
a,b,e=function_span(source,'model_eval_internal');original=source[a:e]
schedule_text='\n'.join(saved['schedule_lines']);assert original.count(schedule_text)==1
capture_lines=[]
for pos,op in enumerate(ops):
if pos in boundary:capture_lines.append(f'lp_snapshot({boundary[pos]},properties,pipe_cache);')
capture_lines.extend(op.code)
if len(ops) in boundary:capture_lines.append(f'lp_snapshot({boundary[len(ops)]},properties,pipe_cache);')
capture_lines.append('lp_save(p,h,q,w,fb);')
local=['if(lp_capture){',*capture_lines,'}else{','for(int pos=0;pos<LP_NO;){',
'int region=lp_plan[lp_color][pos];',
'if(region>=0 && lp_reuse(region,properties,pipe_cache,p,h,q,w,fb)){pos=lp_end[region];continue;}',
'lp_operation(pos);','switch(pos){']
for pos,op in enumerate(ops):local += [f'case {pos}:{{',*op.code,'break;}']
local += ['default:return 0;}','pos++;','}}']
clone=original.replace('model_eval_internal(', 'model_eval_local_internal(',1).replace(schedule_text,'\n'.join(local))
wrapper='''
int lp_eval(double t,const double *y,double *dy,double *w,ModelJacobianWorkspace *workspace){
static ModelJacobianWorkspace shadow;
int active=lp_color+1;lp_active=active;
int eligible=lp_mask && workspace && (lp_color<0 || (lp_mask&(UINT64_C(1)<<lp_color)));
if(lp_color<0){lp_begin(t,y);if(lp_observe)model_jacobian_begin(&shadow);}
int valid=lp_color<0 || lp_valid(t,y);
lp_capture=lp_color<0;
lp_note_eval(eligible && valid);
int result=eligible && valid ? model_eval_local_internal(t,y,dy,w,1,NULL,NULL,workspace) : model_eval_jacobian_reuse(t,y,dy,w,workspace);
if(eligible && valid && workspace)workspace->scalars.recording=0;
if(lp_color<0)lp_ready(eligible && result);
if(lp_observe){
double expected_dy[NSTATES],expected_w[NOUTPUTS];lp_active=-1;
int expected=model_eval_jacobian_reuse(t,y,expected_dy,expected_w,&shadow);
lp_compare(result,dy,w,expected,expected_dy,expected_w);
}
lp_active=-1;return result;
}
'''
local_source=source+'\n'+clone+'\n'+wrapper
# The reference original is never replaced in the timing worker. Observation
# counts reference operations analytically in support code, not by modifying it.
assert original in local_source
metadata=dict(stateKeys=program.state_keys,groups=groups,order=order,regions=regions,boundaries=boundaries,contextCheckpointSlots=boundary,
operations=[dict(id=order[i],key=op.key,inputs=sorted(op.inputs),outputs=list(op.outputs),stateIndices=[j for j in range(d.state_count) if opmask[i]>>j&1]) for i,op in enumerate(ops)],
unknownDependencyLeaves=sorted(unknown),macros=macros,originalEvaluatorSha256=hashlib.sha256(original.encode()).hexdigest())
return '#include "local_probe.h"\n'+local_source,header,declarations,metadata
def kernel_counter(s,fn,index):
a,b,e=function_span(s,fn)
return s[:b+1]+f'if(lp_active>=0)lp_kernel({index});'+s[b+1:]
def prepare(model,observe=False):
OUT.mkdir(parents=True,exist_ok=True);started=time.perf_counter();program,saved=capture(model)
local,header,tables,meta=generate(program,saved)
write(OUT/'plan.json',meta);(OUT/'original-model.c').write_text(program.source,encoding='utf-8')
work=OUT/('audit' if observe else 'worker');work.mkdir(exist_ok=True)
(work/'model.h').write_text(program.header,encoding='utf-8');(work/'local_probe.h').write_text(header,encoding='utf-8')
sources={p.relative_to(builder.NATIVE).as_posix():p.read_text(encoding='utf-8') for p in builder._runtime_sources(program)}
source_hashes={key:hashlib.sha256(value.encode()).hexdigest() for key,value in sources.items()}
sources['model.c']=local
common=sources['runtime/common.c']
common=replace(common,'int ok=r->options.bdf ? native_bdf(r) : native_rk45(r);','lp_start();int ok=r->options.bdf ? native_bdf(r) : native_rk45(r);')
common=replace(common,'r->solve_cpu_seconds=native_cpu_time()-r->cpu_start;','r->solve_cpu_seconds=native_cpu_time()-r->cpu_start;lp_finish();')
common=replace(common,'r->events++;','lp_event(stop,accepted_state);r->events++;')
sources['runtime/common.c']=common
cv=sources['runtime/cvode_solver.c']
cv=replace(cv,'model_eval_jacobian_reuse(t,N_VGetArrayPointer(y),N_VGetArrayPointer(f),outputs,workspace)',
'lp_eval(t,N_VGetArrayPointer(y),N_VGetArrayPointer(f),outputs,workspace)')
cv=replace(cv,'for (int color=0;color<MODEL_JACOBIAN_COLOR_COUNT;color++) {\n memcpy(test,state,NSTATES*sizeof(double));',
'for (int color=0;color<MODEL_JACOBIAN_COLOR_COUNT;color++) {\n lp_color=color;memcpy(test,state,NSTATES*sizeof(double));')
a,b,e=function_span(cv,'cv_jacobian');sig=cv[a:b].strip()
impl=cv[a:e].replace('cv_jacobian(', 'cv_jacobian_original(',1)
wrapper=sig+'{lp_color=-1;uint64_t start=lp_tick();int result=cv_jacobian_original(t,y,fy,matrix,user,tmp1,tmp2,tmp3);lp_jac_time(start);if(!result)lp_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));return result;}'
cv=cv[:a]+impl+'\n'+wrapper+cv[e:]
cv=replace(cv,'long int value=0;', 'long int value=0,nni=0,ncf=0;CVodeGetNumNonlinSolvIters(solver,&nni);CVodeGetNumNonlinSolvConvFails(solver,&ncf);lp_newton(nni,ncf);')
sources['runtime/cvode_solver.c']=cv
if observe:
for i,(module,fn) in enumerate(KERNELS):
key=f'components/modules/{module}.c';sources[key]=kernel_counter(sources[key],fn,i)
sources={key:'#include "local_probe.h"\n'+src for key,src in sources.items()}
sources['local_probe_support.c']=TEMPLATE.read_text(encoding='utf-8').replace('/* GENERATED_TABLES */',tables)
cc,sun,_=builder.toolchain();flags,libs,dlls,exe=builder.platform_build_inputs(sun);flags += [f'-DLP_OBSERVE={int(observe)}']
generated=time.perf_counter()
def compile_one(item):
i,(key,source)=item;path=work/Path(key).name;path.write_text(source,encoding='utf-8',newline='\n');obj=work/f'unit-{i}.o';log=[]
builder._command([cc,*flags,'-I',str(work),'-I',str(builder.NATIVE/'include'),'-I',str(sun/'include'),'-c',str(path),'-o',str(obj)],log=log,timeout=180)
return obj,log
with ThreadPoolExecutor(max_workers=4) as pool:objects=list(pool.map(compile_one,enumerate(sources.items())))
log=[];builder._command([cc,*flags,*[str(x) for x,_ in objects],*builder.link_library_arguments(libs),'-lm','-o',str(work/exe)],log=log)
for dll in dlls:shutil.copyfile(dll,work/dll.name)
(work/'build.log').write_text('\n'.join([*sum([x for _,x in objects],[]),*log]),encoding='utf-8')
write(work/'build-metadata.json',dict(commit=subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip(),
inputSha256=hashlib.sha256(model.read_bytes()).hexdigest(),sourceHashes=source_hashes,
codegenSeconds=generated-started,compileSeconds=time.perf_counter()-generated,totalSeconds=time.perf_counter()-started))
print('BUILT',work.name,'seconds',time.perf_counter()-started,'regions',len(meta['regions']),'boundaries',len(meta['boundaries']),flush=True)
def run(label,mask,observe=False,stop=10):
work=OUT/label;work.mkdir(exist_ok=True);exe=OUT/('audit' if observe else 'worker')/'model.exe'
env=os.environ.copy();env['LOCAL_PROBE_MASK']=str(mask)
args=[str(exe),'--method','BDF','--start','0','--stop',str(stop),'--sample-step','.01','--max-step','1e30','--rtol','1e-8','--timeout','300',
'--sample-file',str(work/'states.bin'),'--output-block-file',str(work/'outputs.bin'),'--output',str(work/'result.json')]
start=time.perf_counter()
with (work/'stderr.log').open('wb') as f:p=subprocess.run(args,cwd=work,env=env,stdout=subprocess.PIPE,stderr=f,timeout=330,creationflags=subprocess.CREATE_NO_WINDOW)
elapsed=time.perf_counter()-start
if p.returncode:raise RuntimeError((label,p.returncode,(work/'stderr.log').read_text(encoding='utf-8')[-5000:]))
r=json.loads((work/'result.json').read_text(encoding='utf-8'));diag=json.loads((work/'probe.json').read_text())
record={k:r[k] for k in ('success','finalState','final','propertyWarnings','acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu','solveSeconds','solveCpuSeconds')}
for name in ('states','outputs','events','jacobians'):
path=work/f'{name}.bin'
if path.exists():
with path.open('rb') as f:record[name+'Sha256']=hashlib.file_digest(f,'sha256').hexdigest()
record[name+'Bytes']=path.stat().st_size
record.update(processSeconds=elapsed,mask=mask,diagnostic=diag)
write(work/'measurement.json',record)
print('RUN',label,{k:record[k] for k in ('solveSeconds','solveCpuSeconds','processSeconds','acceptedSteps','nfev','njev','nlu')},'newton',diag['newtonIterations'],'audit',diag['auditComparisons'],diag['auditDifferences'],flush=True)
def cold_runs(model):
for name,mask in [('reference',0),('all',0x7ffffff)]:
start=time.perf_counter();prepare(model,False);prepared=time.perf_counter()-start
run('cold-'+name,mask);elapsed=time.perf_counter()-start
record=json.loads((OUT/('cold-'+name)/'measurement.json').read_text(encoding='utf-8'))
write(OUT/('cold-'+name+'-pipeline.json'),dict(mode=name,prepareSeconds=prepared,
workerSeconds=record['processSeconds'],pipelineSeconds=elapsed))
def guard_test():
work=OUT/'audit';cc,sun,_=builder.toolchain();flags,_,_,_=builder.platform_build_inputs(sun);log=[]
executable=work/'guard-test.exe'
builder._command([cc,*flags,'-DLP_OBSERVE=1','-I',str(work),'-I',str(builder.NATIVE/'include'),
str(Path(__file__).with_name('local_probe_guard_test.c')),'-lm','-o',str(executable)],log=log)
target=OUT/'guard-selftest';target.mkdir(exist_ok=True);env=os.environ.copy();env['LOCAL_PROBE_MASK']=str(1<<26)
subprocess.run([str(executable)],cwd=target,env=env,check=True,creationflags=subprocess.CREATE_NO_WINDOW)
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('action',choices=['prepare','run','cold','guard']);p.add_argument('--audit',action='store_true')
p.add_argument('--model',type=Path,default=ROOT/'tests/data/test-mql-8-corrected.json');p.add_argument('--label',default='run');p.add_argument('--mask',type=lambda v:int(v,0),default=0);p.add_argument('--stop',type=float,default=10)
a=p.parse_args()
if a.action=='prepare':prepare(a.model,a.audit)
elif a.action=='cold':cold_runs(a.model)
elif a.action=='guard':guard_test()
else:run(a.label,a.mask,a.audit,a.stop)
+39
View File
@@ -0,0 +1,39 @@
/* Compile against an experiment's generated model/support, not production. */
#include <local_probe_support.c>
#define CHECK(x) do{if(!(x)){fprintf(stderr,"guard self-test failed line %d\n",__LINE__);return 1;}}while(0)
int main(void){
lp_start();CHECK(saved && lp_mask);
int group=-1,region=-1;
for(int g=0;g<LP_NC && group<0;g++)if(lp_mask&(UINT64_C(1)<<g))for(int p=0;p<LP_NO;p++)if(lp_plan[g][p]>=0){group=g;region=lp_plan[g][p];break;}
CHECK(group>=0);lp_color=group;lp_active=group+1;
double y[NSTATES]={0},p[LP_NP]={0},h[LP_NQ]={0},q[LP_NQ]={0},w[NOUTPUTS]={0},fb[LP_NFB]={0};
NativePropertyState states[LP_NPS]={0};
NativePipeCache pipes[LP_NPC]={0};NativeJacobianScalars jacobian={0};
NativePropertyCache context={.states=states,.count=1,.capacity=LP_NPS,.jacobian=&jacobian};
states[0].p=123;states[0].T=456;states[0].valid=NATIVE_PROPERTY_PT;states[0].jacobian=&jacobian;
lp_begin(.5,y);lp_snapshot(lp_before[region],&context,pipes);
states[0].rho=42;states[0].valid|=NATIVE_PROPERTY_RHO;
lp_snapshot(lp_after[region],&context,pipes);
lp_save(p,h,q,w,fb);lp_ready(1);
CHECK(lp_valid(.5,y));CHECK(!lp_valid(nextafter(.5,1),y));
int own=-1,other=-1;
for(int i=0;i<NSTATES;i++){if(lp_colors[i]==group)own=i;else other=i;}
CHECK(own>=0 && other>=0);y[own]=1;CHECK(lp_valid(.5,y));y[other]=1;CHECK(!lp_valid(.5,y));y[other]=0;
Snapshot *before=&saved->snapshots[lp_before[region]],*after=&saved->snapshots[lp_after[region]];
context.count=before->count;memcpy(states,before->states,before->count*sizeof(*states));memcpy(pipes,before->pipes,sizeof(pipes));
CHECK(lp_reuse(region,&context,pipes,p,h,q,w,fb));
CHECK(context.count==after->count && !memcmp(states,after->states,after->count*sizeof(*states)));
memcpy(states,before->states,before->count*sizeof(*states));context.count=before->count;
states[0].rho=nextafter(states[0].rho,1);CHECK(!lp_reuse(region,&context,pipes,p,h,q,w,fb));
memcpy(states,before->states,before->count*sizeof(*states));pipes[0].valid=1;CHECK(!lp_reuse(region,&context,pipes,p,h,q,w,fb));
memcpy(pipes,before->pipes,sizeof(pipes));context.count++;CHECK(!lp_reuse(region,&context,pipes,p,h,q,w,fb));
context.count=before->count;context.jacobian=NULL;CHECK(!lp_reuse(region,&context,pipes,p,h,q,w,fb));
int pure=-1;for(int r=0;r<LP_NR;r++)if(lp_before[r]==lp_after[r]){pure=r;break;}
CHECK(pure>=0);NativePropertyState held=states[0];NativePipeCache held_pipe=pipes[0];
size_t held_count=context.count;
CHECK(lp_reuse(pure,&context,pipes,p,h,q,w,fb));
CHECK(context.count==held_count && context.jacobian==NULL);
CHECK(!memcmp(&held,states,sizeof(held)) && !memcmp(&held_pipe,pipes,sizeof(held_pipe)));
lp_begin(.5,y);CHECK(!lp_valid(.5,y));
lp_finish();puts("exact context, pipe-cache, time, state and lifetime guards passed");return 0;
}
+86
View File
@@ -0,0 +1,86 @@
/* No numerical work lives here. Runtime selection is outside the hot model. */
#include "local_probe_profile.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* PROFILE_TABLES */
PfBucket pf_buckets[LP_NC+2][PF_CATEGORIES],*pf_current;
uint64_t pf_previous_tick;
unsigned pf_row;
int pf_coarse;
static uint64_t evaluations[LP_NC+2];
uint64_t pf_operations[LP_NC][2];
static uint64_t calls,sampled,sampled_ticks,frequency,total_events;
static uint64_t sampled_qpc_ticks,tsc_start,tsc_end,clock_t0;
static double clock_q0;
static unsigned stride=16,offset,seed=1;
static FILE *matrix;
static const char *names[PF_CATEGORIES]={"other","perturbation_amount","state_copy_perturb",
"matrix_zero","difference_matrix_write","baseline_compute","initialization","gas_state_preparation",
"schedule_retained","schedule_context_fallback","context_compare","snapshot_capture_save",
"context_output_restore","node_energy","port_outputs","mechanical_equations",
"gas_mass_energy","remaining_outputs","pipe_diagnostics","finite_check","schedule_dispatch","whole_probe_fallback"};
void pf_initialize(void){
LARGE_INTEGER f;QueryPerformanceFrequency(&f);frequency=(uint64_t)f.QuadPart;
unsigned a,b,c,d;
if(!__get_cpuid(0x80000007,&a,&b,&c,&d) || !(d&(1u<<8))){fprintf(stderr,"invariant TSC required for diagnostic clock\n");abort();}
uint64_t q0=lp_tick();clock_t0=pf_clock();uint64_t q1=lp_tick();clock_q0=((double)q0+(double)q1)/2;
const char *s=getenv("PROBE_PROFILE_STRIDE"),*o=getenv("PROBE_PROFILE_OFFSET"),*v=getenv("PROBE_PROFILE_MATRICES");
const char *coarse=getenv("PROBE_PROFILE_COARSE");pf_coarse=coarse?atoi(coarse):0;
if(s)stride=(unsigned)strtoul(s,NULL,0);
if(o)offset=(unsigned)strtoul(o,NULL,0);
seed=offset+1;
if(v && atoi(v)){matrix=fopen("jacobians.bin","wb");if(!matrix)abort();setvbuf(matrix,NULL,_IOFBF,1024*1024);}
}
int pf_select(void){
/* One randomly positioned callback per consecutive stratum. The offset is
reproducible, and avoids locking onto a recurring Newton/event pattern. */
unsigned pos=(unsigned)(calls++% (stride?stride:1));
if(!stride)return 0;
if(!pos){seed^=seed<<13;seed^=seed>>17;seed^=seed<<5;offset=seed%stride;}
if(pos!=offset)return 0;
pf_row=0;return 1;
}
void pf_eval_count(void){evaluations[pf_row]++;}
void pf_open(void){pf_current=&pf_buckets[0][PF_OTHER];tsc_start=pf_clock();pf_previous_tick=tsc_start;}
void pf_close(void){tsc_end=pf_clock();pf_current->ticks+=tsc_end-pf_previous_tick;}
void pf_flush(uint64_t start,uint64_t end){
sampled++;sampled_qpc_ticks+=end-start;sampled_ticks+=tsc_end-tsc_start;
}
void pf_validate_matrix(double t,const double *y,const double *m){
if(matrix){fwrite(&t,8,1,matrix);fwrite(y,8,NSTATES,matrix);fwrite(m,8,NSTATES*NSTATES,matrix);}
}
static void array(FILE *f,const uint64_t *a,int n){fputc('[',f);for(int i=0;i<n;i++)fprintf(f,"%s%llu",i?",":"",(unsigned long long)a[i]);fputc(']',f);}
void pf_finish(void){
if(matrix){fclose(matrix);matrix=NULL;}
uint64_t q0=lp_tick(),t1=pf_clock(),q1=lp_tick();
double tsc_frequency=(double)(t1-clock_t0)/((((double)q0+(double)q1)/2-clock_q0)/frequency);
/* Calibration after integration. Same fenced clock and bucket accounting;
no correction is applied to raw ticks. This cannot remove serialization,
compiler-layout, cache, or other workload-dependent indirect effects. */
double calibration[9];PfBucket saved_buckets[LP_NC+2][PF_CATEGORIES];
memcpy(saved_buckets,pf_buckets,sizeof(saved_buckets));
for(int k=0;k<9;k++){
pf_row=0;pf_current=&pf_buckets[0][PF_OTHER];uint64_t begin=pf_clock();pf_previous_tick=begin;
for(int i=0;i<24576;i++)PF_MARK(i&1?PF_OTHER:PF_DISPATCH);
calibration[k]=(double)(pf_clock()-begin)/24576;
}
memcpy(pf_buckets,saved_buckets,sizeof(saved_buckets));
for(int r=0;r<LP_NC+2;r++)for(int c=0;c<PF_CATEGORIES;c++)total_events+=pf_buckets[r][c].intervals;
FILE *f=fopen("profile.json","wb");if(!f)abort();
fprintf(f,"{\"frequency\":%.9f,\"qpcFrequency\":%llu,\"sampledCallbackQpcTicks\":%llu,\"stride\":%u,\"callbacks\":%llu,\"sampledCallbacks\":%llu,\"sampledCallbackTicks\":%llu,\"markerCount\":%llu,\"calibrationTicksPerMarker\":[",
tsc_frequency,(unsigned long long)frequency,(unsigned long long)sampled_qpc_ticks,stride,(unsigned long long)calls,(unsigned long long)sampled,(unsigned long long)sampled_ticks,(unsigned long long)total_events);
for(int i=0;i<9;i++)fprintf(f,"%s%.9f",i?",":"",calibration[i]);
fprintf(f,"],\"categories\":[");for(int i=0;i<PF_CATEGORIES;i++)fprintf(f,"%s\"%s\"",i?",":"",names[i]);
fprintf(f,"],\"rows\":[");uint64_t sum=0;
for(int r=0;r<LP_NC+2;r++){
uint64_t rt[PF_CATEGORIES],ri[PF_CATEGORIES];
for(int c=0;c<PF_CATEGORIES;c++){rt[c]=pf_buckets[r][c].ticks;ri[c]=pf_buckets[r][c].intervals;}
fprintf(f,"%s{\"group\":%d,\"evaluations\":%llu,\"ticks\":",r?",":"",r-2,(unsigned long long)evaluations[r]);array(f,rt,PF_CATEGORIES);
fprintf(f,",\"intervals\":");array(f,ri,PF_CATEGORIES);
if(r>=2){fprintf(f,",\"operations\":");array(f,pf_operations[r-2],2);}fputc('}',f);
for(int c=0;c<PF_CATEGORIES;c++)sum+=rt[c];
}
fprintf(f,"],\"ledgerSumTicks\":%llu}\n",(unsigned long long)sum);fclose(f);
if(sum!=sampled_ticks)abort();
}
+53
View File
@@ -0,0 +1,53 @@
/* Diagnostic-only exclusive buckets; included only by the copied worker. */
#ifndef LOCAL_PROBE_PROFILE_H
#define LOCAL_PROBE_PROFILE_H
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#include <windows.h>
#include <stdint.h>
#include <cpuid.h>
#include "local_probe.h"
enum { PF_OTHER, PF_INCREMENT, PF_PERTURB, PF_ZERO, PF_ASSEMBLY,
PF_BASELINE, PF_INIT, PF_GAS_PREP, PF_RETAINED, PF_FALLBACK,
PF_CONTEXT, PF_SNAPSHOT, PF_RESTORE, PF_NODE, PF_PORT,
PF_MECHANICAL, PF_GAS_EQUATIONS, PF_OUTPUTS, PF_PIPE,
PF_FINITE, PF_DISPATCH, PF_FULL_FALLBACK, PF_CATEGORIES };
/* row 0 = outer callback, row 1 = baseline, row 2+g = probe group g. */
typedef struct { uint64_t ticks,intervals; } PfBucket;
extern PfBucket pf_buckets[LP_NC+2][PF_CATEGORIES],*pf_current;
extern uint64_t pf_previous_tick;
extern unsigned pf_row;
extern int pf_coarse;
extern const unsigned char pf_affected[LP_NC][LP_NO];
extern uint64_t pf_operations[LP_NC][2];
static inline uint64_t pf_clock(void){
unsigned lo,hi;
__asm__ __volatile__("lfence\n\trdtsc\n\tlfence" : "=a"(lo),"=d"(hi) :: "memory");
return ((uint64_t)hi<<32)|lo;
}
static inline void pf_mark(unsigned category) {
uint64_t t=pf_clock();
pf_current->ticks+=t-pf_previous_tick;
pf_current->intervals++;
pf_previous_tick=t;pf_current=&pf_buckets[pf_row][category];
}
#define PF_MARK(category) pf_mark(category)
#define PF_SCOPE(row,category) do {pf_row=(row);pf_mark(category);} while(0)
#define PF_PROBE(category) do {if(lp_color>=0)pf_mark(category);} while(0)
void pf_initialize(void);void pf_finish(void);
int pf_select(void);void pf_flush(uint64_t,uint64_t);
void pf_eval_count(void);
static inline void pf_operation(int position){
int retained=pf_affected[lp_color][position];
pf_operations[lp_color][retained?0:1]++;
PF_MARK(retained?PF_RETAINED:PF_FALLBACK);
}
void pf_open(void);void pf_close(void);
void pf_snapshot(int,NativePropertyCache*,NativePipeCache*);
void pf_save(double*,double*,double*,double*,double*);
int pf_reuse(int,NativePropertyCache*,NativePipeCache*,double*,double*,double*,double*,double*);
int pf_lp_eval(double,const double*,double*,double*,ModelJacobianWorkspace*);
void lp_jac_time_end(uint64_t,uint64_t);
void pf_validate_matrix(double,const double*,const double*);
#endif
+34
View File
@@ -0,0 +1,34 @@
**局部 probe 的独立诊断计时工具**
本工具只读取上一轮 `test/local-probe-20260917/worker`,在
`test/local-probe-profile-direct-20260917` 构建带计时副本的独立 worker。
不编辑旧实验实现或生产文件,不改变求解容差、物性算法或执行范围。
仅适用于本次已核验的132状态、27组、484个schedule操作的八路模型。
```powershell
& .venv-win/Scripts/python.exe -B tests/manual/profile_local_probe.py prepare
& .venv-win/Scripts/python.exe -B tests/manual/profile_local_probe.py run --label validate-all --stride 1 --matrices
& .venv-win/Scripts/python.exe -B tests/manual/profile_local_probe.py batch
& .venv-win/Scripts/python.exe -B tests/manual/profile_local_probe.py prepare --coarse
& .venv-win/Scripts/python.exe -B tests/manual/profile_local_probe.py run --label coarse-0 --stride 1 --coarse
& .venv-win/Scripts/python.exe -B tests/manual/profile_local_probe.py run --label coarse-1 --stride 1 --coarse
& .venv-win/Scripts/python.exe -B tests/manual/profile_local_probe.py run --label coarse-2 --stride 1 --coarse
& .venv-win/Scripts/python.exe -B tests/manual/analyze_local_probe_profile.py
```
相同label会覆盖同名诊断运行。请串行测量,期间不要并行编译或运行其他仿真。
完整数值核验保留所有矩阵;主性能测量不保存矩阵、不做shadow求值。
每个run都会比较states/outputs/events、warning、求解器计数以及context保护计数。
正常callback走未改写的原函数;分层随机抽中的callback才进入细分计时副本。
粗粒度worker进入原模型函数,只测完整baseline/probe及Jacobian外层。
分类定义、全部27组均值、原始/扣空标记值和测量误差见生成的report.md;
每组全部分类数据见comparison.json的main.rows[group+2]。
外层时钟为QPC,内部使用带lfence的invariant TSC,以整轮QPC/TSC读数校准频率。
内部计数桶逐tick闭合;内外计时边界差单独归入outer other。
扣空标记只是估计,不能去除计时屏障、编译布局与缓存造成的间接扰动。
本次估计总量仍高于未插桩约7%,小group的偏差更大;不得把细分百分比
直接用作优化收益承诺。原QPC记录版及TSC记录版分别保存在
`test/local-probe-profile-20260917`、`test/local-probe-profile-tsc-20260917`,
用于保留计时方法改进的证据,不与最终主样本混用。
+133
View File
@@ -0,0 +1,133 @@
/* Standalone experiment support; never linked by the production builder. */
#define _WIN32_WINNT 0x0600
#include "local_probe.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/* GENERATED_TABLES */
int lp_color=-1,lp_active=-1,lp_capture=0,lp_observe=LP_OBSERVE;
uint64_t lp_mask;
typedef struct {
size_t count,capacity;
NativePropertyTemperatures *temperatures;
NativeJacobianScalars *jacobian;
NativePropertyState states[LP_NPS];
NativePipeCache pipes[LP_NPC];
} Snapshot;
typedef struct {
Snapshot snapshots[LP_NB];
double p[LP_NP],h[LP_NQ],q[LP_NQ],w[NOUTPUTS],fb[LP_NFB];
double t,y[NSTATES];int ready;
} Workspace;
static Workspace *saved;
static unsigned char needed[LP_NB],captured[LP_NB];
static FILE *matrix_file,*event_file;
static uint64_t frequency,jac_ticks,jac_calls,newton_iterations,newton_failures,audit_count,audit_diff;
static uint64_t executed[LP_NC][LP_NO],skipped[LP_NC][LP_NO],kernel_calls[LP_NC+1][LP_NK];
static uint64_t attempts[LP_NC],hits[LP_NC],context_misses[LP_NC],invalid_trials[LP_NC];
static uint64_t compared_bytes,copied_bytes,model_calls[LP_NC+1];
uint64_t lp_tick(void){LARGE_INTEGER t;QueryPerformanceCounter(&t);return (uint64_t)t.QuadPart;}
void lp_jac_time(uint64_t start){jac_ticks+=lp_tick()-start;jac_calls++;}
void lp_newton(long iterations,long failures){newton_iterations+=(uint64_t)iterations;newton_failures+=(uint64_t)failures;}
void lp_start(void){
LARGE_INTEGER f;QueryPerformanceFrequency(&f);frequency=(uint64_t)f.QuadPart;
const char *mask=getenv("LOCAL_PROBE_MASK");lp_mask=mask?strtoull(mask,NULL,0):0;
if(lp_mask){saved=calloc(1,sizeof(*saved));if(!saved){fprintf(stderr,"local probe workspace allocation failed\n");abort();}}
for(int g=0;g<LP_NC;g++)if(lp_mask&(UINT64_C(1)<<g))for(int pos=0;pos<LP_NO;pos++){
int r=lp_plan[g][pos];if(r>=0){needed[lp_before[r]]=1;needed[lp_after[r]]=1;}
}
event_file=fopen("events.bin","wb");if(!event_file)abort();
if(lp_observe){matrix_file=fopen("jacobians.bin","wb");if(!matrix_file)abort();setvbuf(matrix_file,NULL,_IOFBF,1024*1024);}
}
void lp_begin(double t,const double *y){
if(saved){saved->ready=0;saved->t=t;memcpy(saved->y,y,sizeof(saved->y));memset(captured,0,sizeof(captured));}
}
void lp_ready(int ok){if(saved)saved->ready=ok;}
int lp_valid(double t,const double *y){
if(!saved || !saved->ready || memcmp(&t,&saved->t,sizeof(double)))return 0;
for(int i=0;i<NSTATES;i++)if(lp_colors[i]!=lp_color && memcmp(y+i,saved->y+i,sizeof(double))){invalid_trials[lp_color]++;return 0;}
return 1;
}
void lp_snapshot(int index,NativePropertyCache *properties,NativePipeCache *pipes){
if(!needed[index] || captured[index])return;
captured[index]=1;
Snapshot *s=&saved->snapshots[index];
if(properties->count>LP_NPS || properties->capacity!=LP_NPS || properties->temperatures){fprintf(stderr,"unsupported local property context\n");abort();}
s->count=properties->count;s->capacity=properties->capacity;s->temperatures=properties->temperatures;s->jacobian=properties->jacobian;
memcpy(s->states,properties->states,s->count*sizeof(*s->states));memcpy(s->pipes,pipes,sizeof(s->pipes));
copied_bytes+=s->count*sizeof(*s->states)+sizeof(s->pipes);
}
void lp_save(double *p,double *h,double *q,double *w,double *fb){
memcpy(saved->p,p,sizeof(saved->p));memcpy(saved->h,h,sizeof(saved->h));memcpy(saved->q,q,sizeof(saved->q));
memcpy(saved->w,w,sizeof(saved->w));memcpy(saved->fb,fb,sizeof(saved->fb));
}
int lp_reuse(int region,NativePropertyCache *properties,NativePipeCache *pipes,double *p,double *h,double *q,double *w,double *fb){
Snapshot *before=&saved->snapshots[lp_before[region]],*after=&saved->snapshots[lp_after[region]];
attempts[lp_color]++;
int contextual=lp_before[region]!=lp_after[region];
if(contextual && (properties->count!=before->count || properties->capacity!=before->capacity ||
properties->temperatures!=before->temperatures || properties->jacobian!=before->jacobian)){context_misses[lp_color]++;return 0;}
size_t n=before->count*sizeof(*before->states);
if(contextual){compared_bytes+=n+sizeof(before->pipes);
if(memcmp(properties->states,before->states,n) || memcmp(pipes,before->pipes,sizeof(before->pipes))){context_misses[lp_color]++;return 0;}
}
/* Preserve the exact baseline cache mutations, including seeded rho/h and
computed isentropic fields; never synthesize a context from (p,T) alone.
Jacobian scalar entries are immutable after baseline; only their telemetry
counters differ when actual kernel requests are avoided. */
if(contextual){properties->count=after->count;
memcpy(properties->states,after->states,after->count*sizeof(*after->states));memcpy(pipes,after->pipes,sizeof(after->pipes));
copied_bytes+=after->count*sizeof(*after->states)+sizeof(after->pipes);
}
double *dst[]={p,h,q,w,fb};double *src[]={saved->p,saved->h,saved->q,saved->w,saved->fb};
for(int i=lp_first_output[region];i<lp_last_output[region];i++)dst[lp_output_map[i][0]][lp_output_map[i][1]]=src[lp_output_map[i][0]][lp_output_map[i][1]];
hits[lp_color]++;
if(LP_OBSERVE)for(int i=lp_start_op[region];i<lp_end[region];i++)skipped[lp_color][lp_operation_id[i]]++;
return 1;
}
#if LP_OBSERVE
void lp_operation(int position){if(lp_active>0)executed[lp_active-1][lp_operation_id[position]]++;}
#endif
void lp_kernel(int kind){if(lp_active>=0 && lp_active<=LP_NC)kernel_calls[lp_active][kind]++;}
void lp_compare(int ok,const double *dy,const double *w,int expected_ok,const double *expected_dy,const double *expected_w){
audit_count++;
if(ok!=expected_ok || (ok && (memcmp(dy,expected_dy,NSTATES*sizeof(double)) || memcmp(w,expected_w,NOUTPUTS*sizeof(double))))){
audit_diff++;fprintf(stderr,"probe exact mismatch color=%d comparison=%llu\n",lp_color,(unsigned long long)audit_count);
for(int i=0;i<NSTATES;i++)if(memcmp(dy+i,expected_dy+i,8))fprintf(stderr,"dy[%d] %.17g != %.17g\n",i,dy[i],expected_dy[i]);
for(int i=0;i<NOUTPUTS;i++)if(memcmp(w+i,expected_w+i,8))fprintf(stderr,"w[%d] %.17g != %.17g\n",i,w[i],expected_w[i]);
abort();
}
}
void lp_matrix(double t,const double *y,const double *matrix){
if(matrix_file){fwrite(&t,8,1,matrix_file);fwrite(y,8,NSTATES,matrix_file);fwrite(matrix,8,NSTATES*NSTATES,matrix_file);}
}
void lp_event(double t,const double *y){if(event_file){fwrite(&t,8,1,event_file);fwrite(y,8,NSTATES,event_file);}}
void lp_note_eval(int local){
model_calls[lp_color+1]++;
/* Reference/disabled/invalid evaluations run every original operation. */
if(LP_OBSERVE && lp_color>=0 && !local)for(int i=0;i<LP_NO;i++)executed[lp_color][i]++;
}
static void vector(FILE *f,const uint64_t *a,int n){
fputc('[',f);for(int i=0;i<n;i++){fprintf(f,"%s%llu",i?",":"",(unsigned long long)a[i]);}fputc(']',f);
}
void lp_finish(void){
if(matrix_file){fclose(matrix_file);matrix_file=NULL;}if(event_file){fclose(event_file);event_file=NULL;}
FILE *f=fopen("probe.json","wb");if(!f)abort();
fprintf(f,"{\"mask\":%llu,\"workspaceBytes\":%llu,\"jacobianCalls\":%llu,\"jacobianSeconds\":%.9f,\"newtonIterations\":%llu,\"newtonConvergenceFailures\":%llu,\"auditComparisons\":%llu,\"auditDifferences\":%llu,\"contextComparedBytes\":%llu,\"contextCopiedBytes\":%llu,\"modelCalls\":",
(unsigned long long)lp_mask,(unsigned long long)(saved?sizeof(*saved):0),(unsigned long long)jac_calls,(double)jac_ticks/frequency,
(unsigned long long)newton_iterations,(unsigned long long)newton_failures,(unsigned long long)audit_count,(unsigned long long)audit_diff,
(unsigned long long)compared_bytes,(unsigned long long)copied_bytes);
vector(f,model_calls,LP_NC+1);fprintf(f,",\"groups\":[");
for(int g=0;g<LP_NC;g++){
fprintf(f,"%s{\"color\":%d,\"attempts\":%llu,\"hits\":%llu,\"contextMisses\":%llu,\"invalidTrials\":%llu,\"executed\":",g?",":"",g,(unsigned long long)attempts[g],(unsigned long long)hits[g],(unsigned long long)context_misses[g],(unsigned long long)invalid_trials[g]);
vector(f,executed[g],LP_NO);fprintf(f,",\"skipped\":");vector(f,skipped[g],LP_NO);fprintf(f,"}");
}
fprintf(f,"],\"kernelNames\":[");for(int i=0;i<LP_NK;i++){fprintf(f,"%s\"%s\"",i?",":"",lp_kernel_names[i]);}
fprintf(f,"],\"kernelCalls\":[");for(int g=0;g<=LP_NC;g++){if(g)fputc(',',f);vector(f,kernel_calls[g],LP_NK);}fprintf(f,"]}\n");fclose(f);
free(saved);saved=NULL;lp_mask=0;
}
+145
View File
@@ -0,0 +1,145 @@
"""Isolated mechanical-event builds; never edits production numerics or cache.
Augment the generated program with local contact descriptors and an optional
read-only release-drive evaluator. The ordinary RHS, Jacobian, property reuse,
state layout, output layout, and component formulas stay byte-for-byte intact.
"""
from dataclasses import replace
import hashlib
import json
from pathlib import Path
import re
import shutil
from app.simulation.components.amesim.semantics import contact_stiffness
HERE = Path(__file__).resolve().parent
MODES = ('off', 'mass', 'lstp', 'all')
def replace_once(text, old, new):
if text.count(old) != 1:
raise ValueError(f'Experiment patch no longer matches the reviewed runtime: {old[:100]}')
return text.replace(old, new, 1)
def prepare_runtime(original, destination):
original, destination = Path(original), Path(destination)
shutil.copytree(original, destination, dirs_exist_ok=True)
shutil.copyfile(HERE/'mechanical_events_runtime.c', destination/'runtime/experimental_events.c')
common = destination/'runtime/common.c'
source = common.read_text(encoding='utf-8')
source = replace_once(source, 'int native_accept(', '#include "experimental_events.c"\n\nint native_accept(')
previous_size=('2*(NSTOPS+NFRICTIONS+NCONTACTS+1)' if '2*(NSTOPS+NFRICTIONS+NCONTACTS+1)' in source
else '2*(NSTOPS+NFRICTIONS+1)')
event_size=previous_size[:-2]+'NEXPERIMENT_CONTACTS+NEXPERIMENT_RELEASES+1)'
source = source.replace(previous_size,event_size)
for field in ('bounds','restitution','thresholds'):
declaration=field+'['+event_size+']'
if declaration+'={0}' not in source:
source=replace_once(source,declaration,declaration+'={0}')
source = replace_once(source, ' double stop=next;', ''' if (!experiment_candidates(r,t,next,old,trial,dense,context,when,indices,friction,&count)) return -1;
double stop=next;''')
if 'if(friction[i]>=0) continue;' in source:
source = replace_once(source, 'if(friction[i]>=0) continue;', 'if(friction[i]!=-1) continue;')
elif 'if(friction[i]!=-1) continue;' not in source:
raise ValueError('Unknown hard-stop event dispatch')
source = source.replace('if(friction[i]<0 &&', 'if(friction[i]==-1 &&')
source = replace_once(source, ' if (!native_append(r,stop,accepted_state)) return -1;', ''' experiment_commit(r,stop,accepted_state,when,indices,friction,count);
if (!native_append(r,stop,accepted_state)) return -1;''')
common.write_text(source, encoding='utf-8')
header = destination/'include/runtime.h'
source = header.read_text(encoding='utf-8')
source = replace_once(source, ' NativePropertyWarning property_warnings[6];', ''' double experiment_last[2*NEXPERIMENT_CONTACTS+NEXPERIMENT_RELEASES+1];
int experiment_direction[2*NEXPERIMENT_CONTACTS+NEXPERIMENT_RELEASES+1];
int experiment_pending[2*NEXPERIMENT_CONTACTS+NEXPERIMENT_RELEASES+1];
unsigned long experiment_checks, experiment_dense, experiment_roots, experiment_rhs;
unsigned long experiment_mass, experiment_lstp, experiment_clipping, experiment_release;
NativePropertyWarning property_warnings[6];''')
header.write_text(source, encoding='utf-8')
main = destination/'runtime/main.c'
source = main.read_text(encoding='utf-8')
source = replace_once(source, ' fprintf(f,",\\\"propertyWarnings\\\":"); native_property_warnings_json(r,f);', ''' fprintf(f,",\\\"experimentalEvents\\\":{\\\"checks\\\":%lu,\\\"denseCalls\\\":%lu,\\\"rootIterations\\\":%lu,\\\"releaseRhsCalls\\\":%lu,\\\"massContactTransitions\\\":%lu,\\\"lstpContactTransitions\\\":%lu,\\\"forceClipTransitions\\\":%lu,\\\"massReleaseTransitions\\\":%lu}",
r->experiment_checks,r->experiment_dense,r->experiment_roots,r->experiment_rhs,
r->experiment_mass,r->experiment_lstp,r->experiment_clipping,r->experiment_release);
fprintf(f,",\\\"propertyWarnings\\\":"); native_property_warnings_json(r,f);''')
main.write_text(source, encoding='utf-8')
def event_program(program, network, mode):
if mode not in MODES:
raise ValueError(mode)
# Reproduce opt-in experiments after production LSTP integration too:
# descriptors remain available, but only the selected experimental events
# run. This also keeps the tests portable when the frozen snapshot is absent.
program=replace(program,header=re.sub(r'#define NCONTACTS \d+','#define NCONTACTS 0',program.header))
# The off program uses the unmodified runtime and generated program.
if mode == 'off':
return program, dict(mode=mode, contacts=[], releases=[])
slots = {v.key: i for i, v in enumerate(program.variables)}
assignments = {}
for w, y in re.findall(r'w\[(\d+)\]\s*=\s*y\[(\d+)\]\s*;', program.source):
if int(w) in assignments and assignments[int(w)] != int(y):
raise ValueError('Ambiguous kinematic state alias')
assignments[int(w)] = int(y)
def velocity(key):
vi = assignments[slots[key+'.v']]
if assignments[slots[key+'.x']] != vi+1:
raise ValueError('Experiment requires the reviewed contiguous v/x state layout')
return vi
contacts = []
for c in network.components.values():
if c.model_type == 'amesim_lstp00a' and mode in ('lstp', 'all'):
contacts.append(dict(name=c.name, values=(3, velocity(c.name+'.port_1'), velocity(c.name+'.port_2'),
c.gap0, contact_stiffness(c), c.rcont, c.Pdis, int(c.discContactOption))))
if c.model_type == 'amesim_mecmas21' and int(c.stoptype) == 2 and mode in ('mass', 'all'):
for kind, side in ((1, 'min'), (2, 'max')):
contacts.append(dict(name=c.name+'.'+side, values=(kind, velocity(c.name), -1,
getattr(c,'x'+side), getattr(c,'Kb'+side), getattr(c,'Db'+side),
getattr(c,'Pd'+side), int(c.discContactOption))))
stop_count = int(re.search(r'#define NSTOPS (\d+)', program.header)[1])
releases = list(range(stop_count)) if mode in ('mass', 'all') else []
header = f'''
#define NEXPERIMENT_CONTACTS {len(contacts)}
#define NEXPERIMENT_RELEASES {len(releases)}
typedef struct {{ int kind, v1, v2; double boundary, stiffness, damping, pdis; int signed_force; }} ExperimentContact;
extern const ExperimentContact experiment_contacts[{max(1,len(contacts))}];
int model_experiment_release_drives(double t,const double *y,double *drives);
'''
data = ','.join('{'+','.join(str(v) if isinstance(v,int) else repr(float(v)) for v in c['values'])+'}' for c in contacts)
additions = f'\nconst ExperimentContact experiment_contacts[{max(1,len(contacts))}] = {{{data or "{0,0,0,0,0,0,0,0}"}}};\n'
if releases:
start = program.source.index('static int model_eval_internal(')
body_start = program.source.index('{', start)
depth, end = 1, body_start+1
while depth:
depth += (program.source[end] == '{') - (program.source[end] == '}')
end += 1
function = program.source[start:end]
function = replace_once(function, 'model_eval_internal(', 'model_experiment_release_internal(')
pos = function.index(') {')
function = function[:pos] + ',double *release_drives' + function[pos:]
found = []
def capture(match):
vi = int(re.search(r'&dy\[(\d+)\]', match[0])[1])
index = len(found)
found.append(vi)
return f'release_drives[{index}]=dy[{vi}];'
function = re.sub(r'native_stop_motion\([^;]+\);', capture, function)
if len(found) != stop_count:
raise ValueError('Release evaluator does not match generated stop count')
extended = 'ModelJacobianWorkspace' in function
args = '0,NULL,NULL,NULL,drives' if extended else 'NULL,drives'
additions += function + '\nint model_experiment_release_drives(double t,const double *y,double *drives) {double dy[NSTATES],w[NOUTPUTS];return model_experiment_release_internal(t,y,dy,w,'+args+');}\n'
else:
additions += 'int model_experiment_release_drives(double t,const double *y,double *drives) {(void)t;(void)y;(void)drives;return 1;}\n'
# Add declarations before the include guard closes, preserving all generated
# RHS and Jacobian source bytes as an exact prefix.
at = program.header.rfind('#endif')
modified = replace(program, source=program.source+additions, header=program.header[:at]+header+program.header[at:])
assert modified.source.startswith(program.source)
return modified, dict(mode=mode, contacts=contacts, releases=releases,
originalModelSourceSha256=hashlib.sha256(program.source.encode()).hexdigest(),
jacobianStructure=program.jacobian_structure)
@@ -0,0 +1,37 @@
# MASS / LSTP 独立事件实验
此实验只修改复制的原生运行库和实验生成程序,不改生产 `native/`、组件公式、生成器、工况、容差、雅可比分色或物性复用。平台默认运行不启用这里的事件。
`mechanical_event_variant.py` 在原生成源码后追加只读事件描述与限位释放驱动力求值入口;原 RHS 和雅可比源码保持为完全相同的前缀,状态/输出布局、求值调度、稀疏结构元数据一致。`prepare_runtime` 仅在指定的独立目标目录增加事件定位、提交和诊断;严格检查替换位置,生产代码变化后不静默套用未知补丁。
四种构建:
- `off`:使用未修改的原生源码和生成程序。
- `mass`:弹性上下限接触/脱离;非负力模式的原始力过零;理想/恢复限位保持后的释放。已有碰撞、恢复系数和摩擦事件保持原处理。
- `lstp`:LSTP 间隙过零;非负力模式的原始力过零。允许负力时不注册力截断事件。
- `all`:两类同时启用。
弹性事件不重置位置或速度;限位释放从约束位置、零速度继续。已接受事件才更新防重复触发记录;试算、雅可比和结果重放不写物理模式。接触力仍由原来的时间、状态、参数纯函数求值,事件记录不改变力公式。相同浮点时刻的事件一次提交和重启,保留已有输出语义。
定位使用已接受步的密集插值及二分,浮点区间不可再分时停止。相对速度换向时分两段查找间隙根,覆盖端点同号但中间接触/脱离的情况;孤立切触不制造来回事件。本实验与原有事件机制一样依赖积分步已经解析运动,未证明可捕获一个接受步内任意多次高频振荡。根定位误差不等于积分状态误差,必须做步长与容差核验。
MASS 释放检查只在已贴近限位且速度接近零时执行。为隔离已有优化,本实验采用独立只读整模型求值取得未施加限位约束的加速度,不修改正常 RHS 的数据通路;这会产生可单独计数的 `releaseRhsCalls`。因此其耗时是本实现的代价,不能当成所有限位释放算法必然的开销。
运行独立解析解、非负力、指数阻尼、无接触/初始贴边、密集插值换向及已有 Amesim 参考测试:
```powershell
.\.venv-win\Scripts\python.exe -X utf8 -m unittest tests.test_mechanical_event_experiment -v
```
八路对照依赖本工作站已有 Amesim 2404、当前循环 AME 和之前保存的非循环 AME,以及上轮审计对齐的工程副本:
```powershell
.\.venv-win\Scripts\python.exe -X utf8 tests/manual/evaluate_mechanical_events.py --output test/mechanical-events-new-run --repeats 5
.\.venv-win\Scripts\python.exe -X utf8 tests/manual/summarize_mechanical_events.py test/mechanical-events-new-run
```
主目录必须新建;`--resume` 仅用于未变更实验源码情况下,保留已经完成的准确性/计时结果并续跑。因 Windows 偶发缓存目录重命名拒绝,构建可在独立新缓存重试。若编译子进程启动不稳定,可追加 `--serial-build`,只串行编译实验程序,不修改求解配置或生产构建逻辑。
每个工况重新运行一次 Amesim 作为四种配置共同的参照。每种配置保留一次完整输出用于同阶段对照;再轮换配置顺序做五次纯求解计时。编译和输出序列化不计入纯求解耗时,单独的完整运行数据仍保存。每个比较点同时核验十个信号阶段,所有曲线共用同一对真实保存行;不能选择误差最小的力值来配对。
完整指标与限制见本次 `docs/other/` 的独立实验报告。
+178
View File
@@ -0,0 +1,178 @@
/* Included only by an isolated runtime copy. No production RHS mode, force
* formula, state dimension, Jacobian coloring or property cache is changed. */
#include <float.h>
#if NEXPERIMENT_CONTACTS
static double experiment_penetration(int j,const double *y) {
ExperimentContact c=experiment_contacts[j];
if(c.kind==1) return c.boundary-y[c.v1+1];
if(c.kind==2) return y[c.v1+1]-c.boundary;
/* Preserve exactly the production gap expression and operation order. */
return -(c.boundary+y[c.v2+1]-y[c.v1+1]);
}
static double experiment_velocity(int j,const double *y) {
ExperimentContact c=experiment_contacts[j];
if(c.kind==1) return -y[c.v1];
if(c.kind==2) return y[c.v1];
return y[c.v1]-y[c.v2];
}
static double experiment_value(int j,int force,const double *y) {
double p=experiment_penetration(j,y);
if(force==2) return experiment_velocity(j,y);
if(!force) return p;
ExperimentContact c=experiment_contacts[j];
double fraction=c.pdis>0 ? -expm1(-fmax(p,0)/c.pdis) : 1;
return c.stiffness*p+fraction*c.damping*experiment_velocity(j,y);
}
static double experiment_locate(NativeRun *r,int j,int force,double left,double right,
double sign,NativeDense dense,void *context) {
double y[NSTATES];
for(int k=0;k<60;k++) {
double mid=left+.5*(right-left);
if(mid<=left || mid>=right) break;
r->experiment_dense++;r->experiment_roots++;
if(!dense(context,mid,y)) return NAN;
double g=experiment_value(j,force,y);
if(!isfinite(g)) return NAN;
if(sign<0 ? g>=0 : g<=0) right=mid; else left=mid;
}
return right;
}
static double experiment_bracket(NativeRun *r,int j,int force,double left,double right,
double a,double b,NativeDense dense,void *context,int *direction) {
int slot=2*j+force;
if(r->experiment_direction[slot] && r->experiment_last[slot]==left)
a=r->experiment_direction[slot]*DBL_MIN;
if(a==0 || (a>0 ? b>0 : b<0)) return INFINITY;
*direction=a<0 ? 1 : -1;
return experiment_locate(r,j,force,left,right,a,dense,context);
}
static double experiment_contact_candidate(NativeRun *r,int j,int force,double t,double next,
const double *old,const double *trial,
NativeDense dense,void *context,int *direction) {
double a=experiment_value(j,force,old), b=experiment_value(j,force,trial);
if(!isfinite(a) || !isfinite(b)) return NAN;
/* Detect a gap excursion and return through the same boundary even when
* the endpoint gaps have the same sign: split at the velocity reversal.
* Like the existing event locator, this relies on resolved accepted steps;
* arbitrarily many unresolved oscillations in one step are not certified. */
if(!force) {
double va=experiment_velocity(j,old),vb=experiment_velocity(j,trial);
if((va<0 && vb>0) || (va>0 && vb<0)) {
double turn=experiment_locate(r,j,2,t,next,va,dense,context),y[NSTATES];
if(!isfinite(turn)) return NAN;
r->experiment_dense++;
if(!dense(context,turn,y)) return NAN;
double g=experiment_value(j,force,y);
if(g==0 && ((a<0 && b<0) || (a>0 && b>0))) return INFINITY;
double found=experiment_bracket(r,j,force,t,turn,a,g,dense,context,direction);
if(isfinite(found) || isnan(found)) return found;
return experiment_bracket(r,j,force,turn,next,g,b,dense,context,direction);
}
}
return experiment_bracket(r,j,force,t,next,a,b,dense,context,direction);
}
#endif
#if NEXPERIMENT_RELEASES
static int experiment_drives(NativeRun *r,double t,const double *y,double *drives) {
r->nfev++;r->experiment_rhs++;
return model_experiment_release_drives(t,y,drives);
}
static double experiment_release_root(NativeRun *r,int j,int lower,double left,double right,
NativeDense dense,void *context) {
double y[NSTATES],drives[NEXPERIMENT_RELEASES];
for(int k=0;k<60;k++) {
double mid=left+.5*(right-left);
if(mid<=left || mid>=right) break;
r->experiment_dense++;r->experiment_roots++;
if(!dense(context,mid,y) || !experiment_drives(r,mid,y,drives)) return NAN;
if(lower ? drives[j]>0 : drives[j]<0) right=mid; else left=mid;
}
return right;
}
#endif
static int experiment_candidates(NativeRun *r,double t,double next,const double *old,const double *trial,
NativeDense dense,void *context,double *when,int *indices,int *kinds,int *count) {
(void)r;(void)t;(void)next;(void)old;(void)trial;(void)dense;(void)context;
(void)when;(void)indices;(void)kinds;(void)count;
#if NEXPERIMENT_CONTACTS
for(int j=0;j<NEXPERIMENT_CONTACTS;j++) {
r->experiment_checks++;
for(int force=0;force<2;force++) {
if(force && (experiment_contacts[j].signed_force==1 ||
experiment_penetration(j,old)<=0)) continue;
int direction=0;
double at=experiment_contact_candidate(r,j,force,t,next,old,trial,dense,context,&direction);
if(isnan(at)) return 0;
if(isfinite(at)) {
if(force) {
double y[NSTATES];r->experiment_dense++;
if(!dense(context,at,y)) return 0;
if(experiment_penetration(j,y)<=0) continue;
}
int n=(*count)++;
when[n]=at;indices[n]=j;kinds[n]=force?-3:-2;
r->experiment_pending[2*j+force]=direction;
}
}
}
#endif
#if NEXPERIMENT_RELEASES
double before[NEXPERIMENT_RELEASES],after[NEXPERIMENT_RELEASES];
int evaluated=0;
for(int j=0;j<NEXPERIMENT_RELEASES;j++) {
NativeStop s=model_stops[j];int v=s.velocity_index, x=v+1;
if(fabs(old[v])>1e-12*fmax(fabs(old[v]),1)) continue;
if(r->experiment_direction[2*NEXPERIMENT_CONTACTS+j] &&
r->experiment_last[2*NEXPERIMENT_CONTACTS+j]==t) continue;
for(int lower=0;lower<2;lower++) {
double bound=lower?s.lower:s.upper,tol=1e-12*fmax(fabs(bound),1);
if(lower ? old[x]>bound+tol : old[x]<bound-tol) continue;
if(!evaluated) {
if(!experiment_drives(r,t,old,before) || !experiment_drives(r,next,trial,after)) return 0;
evaluated=1;
}
r->experiment_checks++;
if(lower ? !(before[j]<=0 && after[j]>0) : !(before[j]>=0 && after[j]<0)) continue;
double at=experiment_release_root(r,j,lower,t,next,dense,context);
if(!isfinite(at)) return 0;
int n=(*count)++;when[n]=at;indices[n]=j;kinds[n]=lower?-4:-5;
}
}
#endif
return 1;
}
static void experiment_commit(NativeRun *r,double t,double *y,const double *when,const int *indices,const int *kinds,int count) {
(void)y;
for(int i=0;i<count;i++) {
if(kinds[i]>-2 || when[i]!=t) continue;
int j=indices[i];
(void)j;
#if NEXPERIMENT_CONTACTS
if(kinds[i]==-2 || kinds[i]==-3) {
int force=kinds[i]==-3,slot=2*j+force;
r->experiment_last[slot]=t;
r->experiment_direction[slot]=r->experiment_pending[slot];
if(force) r->experiment_clipping++;
else if(experiment_contacts[j].kind==3) r->experiment_lstp++;
else r->experiment_mass++;
fprintf(stderr,"{\"phase\":\"mechanical-event\",\"kind\":\"%s\",\"index\":%d,\"time\":%.17g,\"direction\":%d,\"penetration\":%.17g,\"relativeVelocity\":%.17g}\n",
force?"force-clip":experiment_contacts[j].kind==3?"lstp-contact":"mass-contact",j,t,
r->experiment_direction[slot],experiment_penetration(j,y),experiment_velocity(j,y));
}
#endif
#if NEXPERIMENT_RELEASES
if(kinds[i]==-4 || kinds[i]==-5) {
NativeStop s=model_stops[j];int slot=2*NEXPERIMENT_CONTACTS+j;
y[s.velocity_index]=0;y[s.velocity_index+1]=kinds[i]==-4?s.lower:s.upper;
r->experiment_last[slot]=t;r->experiment_direction[slot]=1;r->experiment_release++;
fprintf(stderr,"{\"phase\":\"mechanical-event\",\"kind\":\"mass-release\",\"index\":%d,\"time\":%.17g,\"side\":\"%s\"}\n",
j,t,kinds[i]==-4?"lower":"upper");
}
#endif
}
(void)r;
}
+118
View File
@@ -0,0 +1,118 @@
"""Eight-branch SI mapping, carried forward from the audited 2026-09-14 comparison.
The caller supplies a fresh topology audit and network; no historical curves are loaded.
"""
import numpy as np
from app.simulation.reporting.amesim_results import _parse_variable_line, parse_amesim_results_bytes
def configure(current_audit, current_network):
global audit, network, aliases, nodes, adj
audit, network = current_audit, current_network
aliases = {row['component']: row['ameAlias'] for row in audit['mapping']}
nodes = {c.name: c for c in network.components.values()
if c.model_type in ('amesim_pn3node2', 'amesim_p4node2')}
adj = {}
for edge in network.connections:
if edge.kind == 'physical':
a, b = (e.key for e in edge.endpoints)
adj[a], adj[b] = b, a
def native_curves(series):
values = {k: np.asarray(v) for k, v in series.items()}
def energy(name):
key = name + '.reference_enthalpy_flow'
if key not in values:
result = np.zeros_like(values['time'])
for port in nodes[name].active_port_definitions:
if port.name == 'port_2':
continue
other = adj[name, port.name]
if other[0] in nodes and other[1] == 'port_2':
result += energy(other[0])
else:
q = values[name + '.' + port.name + '.m_flow']
result += q * np.where(q > 0, values['.'.join(other) + '.h_outflow'],
values[name + '.' + port.name + '.h_outflow'])
values[key] = result
return values[key]
for name in nodes:
energy(name)
values[name + '.reference_mass_flow'] = -values[name + '.port_2.m_flow']
for component in network.components.values():
if component.model_type == 'amesim_pnch012':
name = component.name
values[name + '.volume_work'] = -values[name + '.p'] * values[name + '.dvol']
return values
def read_ame(directory):
variables = tuple(_parse_variable_line(i, line) for i,line in enumerate(
(directory/'test_mql_.var').read_text(encoding='latin1').splitlines()))
return parse_amesim_results_bytes((directory/'test_mql_.results').read_bytes(), variables)
def curve_mapping():
result = []
for row in audit['mapping']:
kind, name, alias = row['type'],row['component'],row['ameAlias']
fields = []
if kind == 'amesim_mecmas21':
fields = [('x','x1','displacement','m',1,0),('v','v1','velocity','m/s',1,0)]
elif kind == 'amesim_lstp00a':
fields = [('force','f1','force','N',1,0),('gap','gap','gap','m',.001,0)]
elif kind in ('amesim_pnch023','amesim_pnch012'):
fields = [('p','press','pressure','Pa',1,101300),('T','temp','temperature','K',1,0)]
if kind == 'amesim_pnch012':
fields += [('vol','vol','volume','m3',1e-6,0),('m','mgas1','mass','kg',1e-3,0)]
elif kind in ('amesim_pnl0001','amesim_pnl0002'):
s = '2' if kind.endswith('1') else 'ctr'
fields = [('p','p'+s,'pressure','Pa',1,101300),('T','t'+s,'temperature','K',1,0)]
elif kind == 'amesim_pnl0003':
fields = [(k+str(i), k.lower()+str(i), 'pressure' if k=='p' else 'temperature',
'Pa' if k=='p' else 'K',1,101300 if k=='p' else 0) for k in ('p','T') for i in (1,2)]
elif kind in ('amesim_ud00','amesim_step0'):
fields = [('out.signal','output' if kind=='amesim_ud00' else 'out','signal','1',1,0)]
elif kind in ('amesim_pn3node2','amesim_p4node2'):
fields = [('reference_enthalpy_flow','dh2','enthalpy_flow','W',1,0),
('reference_mass_flow','dm2','mass_flow','kg/s',1e-3,0)]
elif kind == 'amesim_pnrp17':
fields = [('volume','vol1','piston_volume','m3',1e-6,0),
('volume_flow','vvol1','volume_rate','m3/s',1e-3/60,0)]
for prop, field, quantity, unit, scale, offset in fields:
result.append(dict(key=name+'.'+prop, amePath=field+'@'+alias,
quantity=quantity, unit=unit, scale=scale, offset=offset))
if kind == 'amesim_pnch012':
sources = []
volumes = []
for p in network.components[name].active_port_definitions:
other = adj[name,p.name]
component = network.components[other[0]]
if component.model_type == 'amesim_pnrp17':
sources.append('vvol1@'+aliases[other[0]])
volumes.append('vol1@'+aliases[other[0]])
elif other[0] in nodes and other[1] == 'port_2':
sources.append('dvol2@'+aliases[other[0]])
volumes.append('vol2@'+aliases[other[0]])
chamber = network.components[name]
effective_volume = dict(volumePaths=volumes, deadVolume=chamber.cvol0,
prescribedVolume=sum(chamber.external_volumes.values()))
result.append(dict(key=name+'.dvol',amePaths=sources, quantity='chamber_volume_rate',unit='m3/s', **effective_volume))
result.append(dict(key=name+'.volume_work',amePaths=sources,pressurePath='press@'+alias,
quantity='volume_work',unit='W', **effective_volume))
return result
def ame_curve(ame, m):
if 'amePath' in m:
return np.array(ame.series(m['amePath']))*m['scale']+m['offset']
rate = sum((np.array(ame.series(p)) for p in m['amePaths']), np.zeros(len(ame.times)))*(1e-3/60)
volume = sum((np.array(ame.series(p)) for p in m['volumePaths']), np.zeros(len(ame.times)))*1e-6
volume += m['deadVolume']+m['prescribedVolume']
# PNCH012.c sets its internal dvol to zero while the volume is limited.
# Summing raw piston rates without this condition invents enormous work
# that the original Amesim chamber does not actually apply.
limit = m['deadVolume']/100
rate = np.where((volume<limit) | ((volume<=limit) & (rate<0)), 0, rate)
if m['quantity'] == 'volume_work':
return -(np.array(ame.series(m['pressurePath']))+101300)*rate
return rate
+112
View File
@@ -0,0 +1,112 @@
"""Create shareable plots from evaluate_mql8_correctness.py evidence (Matplotlib)."""
import argparse
import json
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import font_manager
import numpy as np
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('directory', type=Path)
args = parser.parse_args()
out = args.directory
font = Path('C:/Windows/Fonts/msyh.ttc')
if font.exists():
font_manager.fontManager.addfont(str(font))
plt.rcParams['font.family'] = font_manager.FontProperties(fname=str(font)).get_name()
plt.rcParams.update({'font.size': 9, 'axes.spines.top': False, 'axes.spines.right': False,
'axes.unicode_minus': False, 'svg.fonttype': 'none', 'figure.facecolor': 'white'})
summary = json.loads((out / 'summary.json').read_bytes())
full = np.load(out / 'full/curves.npz')
startup = np.load(out / 'startup/curves.npz')
fig, axes = plt.subplots(4, 2, figsize=(13, 13), constrained_layout=True)
selected = [
(startup, summary['startup']['groups']['pressure']['worstAbsolute']['key'], '绝对压力', 'kPa', 1e-3, .002, 1000),
(startup, summary['startup']['groups']['temperature']['worstAbsolute']['key'], '温度', 'K', 1, .002, 1000),
(full, 'amesim_mecmas21_1.x', '1 号质量块位移', 'mm', 1000, 50, 1),
(full, summary['full']['groups']['force']['worstAbsolute']['key'], '接触力(最差曲线)', 'kN', .001, 50, 1),
]
for (data, key, title, unit, scale, end, ts), (left, right) in zip(selected, axes):
mask = data['time'] <= end
t = data['time'][mask] * ts
native, ame = data['platform|' + key][mask] * scale, data['amesim|' + key][mask] * scale
left.plot(t, native, color='#1368a8', linewidth=1.6, label='当前平台')
left.plot(t, ame, color='#e07832', linestyle='--', linewidth=1.2, label='本次 Amesim')
left.set_title(title + ' · ' + key, fontsize=9)
left.legend(fontsize=8)
right.plot(t, native - ame, color='#925034', linewidth=1.1)
right.axhline(0, color='#888888', linewidth=.6)
right.set_title('平台 − Amesim;切换点保留', fontsize=9)
for ax in (left, right):
ax.set_xlabel('时间 / ' + ('ms' if ts == 1000 else 's'))
ax.set_ylabel(unit)
ax.grid(alpha=.18)
fig.suptitle('八路模型正确性初评 · 当前代码与本次 Amesim 执行\n启动段采样 0.1 ms;循环全程采样 10 ms;曲线重合不等于事件输出一致', fontsize=13)
fig.savefig(out / 'comparison.png', dpi=160)
fig.savefig(out / 'comparison.svg')
plt.close(fig)
fig, axes = plt.subplots(3, 2, figsize=(13, 10), constrained_layout=True)
# Show each of the eight physical contacts; full curve metrics still include
# the large event difference. The second column localizes away from events.
for i in range(1, 9):
key = f'amesim_lstp00a_{i}.force'
t = full['time']
error = full['platform|' + key] - full['amesim|' + key]
axes[0, 0].plot(t, error / 1000, linewidth=.8, label=str(i))
quiet = (t >= .1)
for event in summary['full']['signalEvents']:
quiet &= np.abs(t - event) > .0200001
visible = np.where(quiet, error, np.nan)
axes[0, 1].plot(t, visible, linewidth=.8, label=str(i))
axes[0, 0].set(title='8 路接触力差:全部共同采样点', ylabel='差值 / kN')
axes[0, 1].set(title='诊断视图:t≥0.1 s,避开切换前后 0.02 s', ylabel='差值 / N')
axes[0, 0].legend(title='支路', ncol=4, fontsize=7)
for row, (quantity, label) in enumerate([('enthalpy_flow', '节点焓流 / W'), ('mass_flow', '节点质量流 / kg/s')], 1):
key = summary['default']['groups'][quantity]['worstRelative']['key']
for column in range(2):
ax = axes[row, column]
t = full['time']
mask = (t >= .1) & (t <= .5)
n, a = full['platform|' + key], full['amesim|' + key]
if column == 0:
ax.plot(t[mask], n[mask], color='#1368a8', label='当前平台')
ax.plot(t[mask], a[mask], '--', color='#e07832', label='本次 Amesim')
ax.legend(fontsize=8)
ax.set_ylabel(label)
ax.set_title(key, fontsize=9)
else:
ax.plot(t[mask], (n-a)[mask], color='#925034')
ax.set_ylabel('绝对差,沿用左侧单位')
ax.set_title('小量差异单列,避免峰值归一化掩盖')
for ax in axes.flat:
ax.grid(alpha=.18)
ax.set_xlabel('时间 / s')
fig.suptitle('问题定位 · 切换点误差与小流量差异分开评价\n右上筛选只用于诊断;全部采样点仍进入主报告统计', fontsize=13)
fig.savefig(out / 'diagnostics.png', dpi=160)
fig.savefig(out / 'diagnostics.svg')
plt.close(fig)
data = np.load(out / 'volume-startup/curves.npz')
fig, axes = plt.subplots(1, 2, figsize=(12, 4), constrained_layout=True)
for ax, key, label, scale in [(axes[0], 'amesim_pnch012_15.vol', '有效气室容积 / L', 1000),
(axes[1], 'amesim_pnch012_15.volume_work', '气室体积功率 / TW', 1e-12)]:
mask = data['time'] <= 8e-8
for prefix, name, color, style in [('platform', '当前平台', '#1368a8', '-'), ('amesim', '本次 Amesim', '#e07832', '--')]:
ax.plot(data['time'][mask]*1e9, data[prefix+'|'+key][mask]*scale, style, color=color, label=name)
ax.set(xlabel='时间 / ns', ylabel=label)
ax.grid(alpha=.18)
ax.legend()
fig.suptitle('最初 80 ns:容积下限与体积功率 · 采样间隔 2 ns', fontsize=13)
fig.savefig(out / 'volume-startup.png', dpi=160)
fig.savefig(out / 'volume-startup.svg')
plt.close(fig)
if __name__ == '__main__':
main()
@@ -0,0 +1,51 @@
"""Read-only numerical probe of current UD00 event/evaluation consistency."""
import argparse
from pathlib import Path
import subprocess
import sys
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from app.simulation.native_codegen.build import _command, toolchain
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', type=Path, required=True)
args = parser.parse_args()
out = args.output.resolve()
out.mkdir(parents=True, exist_ok=True)
source = out / 'signal-boundary-probe.c'
code = r'''
#include "SIGNAL_SOURCE"
#include <stdio.h>
int main(void) {
double d[24]={0};
d[0]=1e17; d[8]=1e17; d[1]=49000; d[9]=49000; d[16]=.8; d[17]=10;
double t=0;
puts("boundary,left,at,right,expected_after,at_matches_after");
for(int i=0;i<10;i++) {
t=native_signal_break(t,60,0,2,1,d);
double expected=i%2==0?49000:1e17;
printf("%.17g,%.17g,%.17g,%.17g,%.17g,%d\n",t,
native_signal(nextafter(t,-INFINITY),0,2,1,d),native_signal(t,0,2,1,d),
native_signal(nextafter(t,INFINITY),0,2,1,d),expected,
native_signal(t,0,2,1,d)==expected);
}
return 0;
}
'''
source.write_text(code.replace('SIGNAL_SOURCE', (ROOT / 'native/components/modules/signal.c').as_posix()), encoding='ascii')
compiler, _, version = toolchain()
exe = out / ('signal-boundary-probe.exe' if sys.platform == 'win32' else 'signal-boundary-probe')
command = [compiler, '-std=c11', '-O2', '-ffp-contract=off', '-fno-fast-math',
'-I' + str(ROOT / 'native/include'), str(source), '-lm', '-o', str(exe)]
_command(command, log=[], timeout=60)
data = subprocess.check_output([str(exe)], timeout=10)
(out / 'signal-boundaries.csv').write_bytes(data)
print(version)
print(data.decode('ascii'))
if __name__ == '__main__':
main()
+218
View File
@@ -0,0 +1,218 @@
"""Sampled, exclusive timing of a COPY of the local-probe performance worker.
Original numerical functions remain in the executable verbatim. Only sampled
Jacobian callbacks use timing clones; ordinary residuals never reach a clone.
No production source or existing experiment implementation is edited.
"""
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import argparse, hashlib, json, os, re, shutil, statistics, subprocess, time
import local_probe_experiment as experiment
ROOT=experiment.ROOT
SOURCE=ROOT/'test/local-probe-20260917/worker'
OUT=ROOT/'test/local-probe-profile-direct-20260917'
HERE=Path(__file__).parent
replace=experiment.replace
span=experiment.function_span
write=experiment.write
def function(source,name):
a,b,e=span(source,name)
return source[a:e]
def annotate_tail(tail):
"""Fail closed for the reviewed generated model, not a general C parser.
Stage ownership follows extended.py; declarations remain in original scope.
Pipe argument PH inversion and accumulation belong to diagnostics; writes
of diagnostic outputs belong to remaining_outputs.
"""
phase='PF_NODE';mode='node';pipe=False;result=['PF_PROBE(PF_NODE);']
counts={};transitions=[]
for i,line in enumerate(tail.splitlines()):
category=phase
if mode=='node' and re.match(r'w\[\d+\] = [phq]\[\d+\];$',line):mode='port'
if mode=='port' and not re.match(r'w\[\d+\] = [phq]\[\d+\];$',line):mode='mechanical'
if mode=='mechanical' and line.startswith('dy[0]='):mode='gas'
if line.startswith('for(int i=0;i<NSTATES;i++) if(!isfinite'):mode='finite'
if mode=='node':category='PF_NODE'
elif mode=='port':category='PF_PORT'
elif mode=='mechanical':category='PF_MECHANICAL'
elif mode=='finite':category='PF_FINITE'
elif mode=='gas':
if line.startswith('{ double d[4],acc[4]={0};'):pipe=True;category='PF_PIPE'
elif pipe:
if re.match(r'w\[\d+\] = ',line):category='PF_OUTPUTS'
elif line=='}':pipe=False
else:category='PF_PIPE'
elif line.startswith('dy['):category='PF_GAS_EQUATIONS'
elif re.match(r'w\[\d+\] = ',line):category='PF_OUTPUTS'
else:raise AssertionError(('unclassified gas-tail statement',i,line))
if category!=phase:
result.append(f'PF_PROBE({category});');transitions.append((i,category));phase=category
counts[category]=counts.get(category,0)+1;result.append(line)
assert mode=='finite' and not pipe
assert all(counts.get(x,0) for x in ['PF_NODE','PF_PORT','PF_MECHANICAL','PF_GAS_EQUATIONS','PF_OUTPUTS','PF_PIPE','PF_FINITE']),counts
return '\n'.join(result),dict(lineCounts=counts,transitions=transitions)
def instrument_model(source,coarse=False):
clone=function(source,'model_eval_local_internal')
# Keep the original function and append an instrumented duplicate.
clone=clone.replace('model_eval_local_internal(', 'pf_model_eval_local_internal(',1)
begin=clone.index('{')+1;clone=clone[:begin]+'\nPF_PROBE(PF_INIT);'+clone[begin:]
gas=clone.index('if(!(jacobian ? native_jacobian_gas(')
clone=clone[:gas]+'PF_PROBE(PF_GAS_PREP);\n'+clone[gas:]
clone=replace(clone,'if(lp_capture){','PF_PROBE(PF_DISPATCH);\nif(lp_capture){')
clone=clone.replace('lp_snapshot(','pf_snapshot(').replace('lp_save(','pf_save(').replace('lp_reuse(','pf_reuse(')
n=0
def case(m):
nonlocal n;n+=1
return m[0]+f'pf_operation({m[1]});'
clone=re.sub(r'case (\d+):\{',case,clone)
assert n==484 and clone.count('break;}')==484
clone=clone.replace('break;}','PF_MARK(PF_DISPATCH);break;}')
start=clone.index('double node_energy[')
# Leave return 1 and function closing outside stage classification.
end=clone.rindex('return 1;')
tail,meta=annotate_tail(clone[start:end].rstrip())
clone=clone[:start]+tail+'\nPF_PROBE(PF_OTHER);\n'+clone[end:]
wrapper=function(source,'lp_eval').replace('int lp_eval(','int pf_lp_eval(',1)
begin=wrapper.index('{')+1
wrapper=wrapper[:begin]+'\nPF_SCOPE(lp_color+2,lp_color<0?PF_BASELINE:PF_OTHER);pf_eval_count();'+wrapper[begin:]
wrapper=wrapper.replace('lp_begin(t,y);','PF_MARK(PF_SNAPSHOT);lp_begin(t,y);PF_MARK(PF_BASELINE);')
wrapper=wrapper.replace('model_eval_local_internal(t,y,dy,w,1,NULL,NULL,workspace)',
'(pf_coarse?model_eval_local_internal(t,y,dy,w,1,NULL,NULL,workspace):pf_model_eval_local_internal(t,y,dy,w,1,NULL,NULL,workspace))' if coarse
else 'pf_model_eval_local_internal(t,y,dy,w,1,NULL,NULL,workspace)')
# This diagnostic is limited to the all-groups valid local path. Count an
# unexpected full-probe fallback explicitly, rather than misattribute it.
wrapper=replace(wrapper,'int result=eligible && valid ?',
'if(lp_color>=0 && !(eligible && valid))PF_MARK(PF_FULL_FALLBACK);\n int result=eligible && valid ?')
return source+'\n'+clone+'\n'+wrapper+'\n',meta
def instrument_support(source):
reuse=function(source,'lp_reuse').replace('int lp_reuse(','int pf_reuse(',1)
reuse=replace(reuse,'int contextual=lp_before[region]!=lp_after[region];',
'int contextual=lp_before[region]!=lp_after[region];\n if(contextual)PF_MARK(PF_CONTEXT);')
assert reuse.count('{context_misses[lp_color]++;return 0;}')==2
reuse=reuse.replace('{context_misses[lp_color]++;return 0;}',
'{context_misses[lp_color]++;PF_MARK(PF_DISPATCH);return 0;}')
reuse=replace(reuse,'if(contextual){properties->count=after->count;',
'PF_MARK(PF_RESTORE);\n if(contextual){properties->count=after->count;')
reuse=replace(reuse,'return 1;','PF_MARK(PF_DISPATCH);return 1;')
wrappers='''
void pf_snapshot(int index,NativePropertyCache *p,NativePipeCache *c){
if(!needed[index] || captured[index]){lp_snapshot(index,p,c);return;}
PF_MARK(PF_SNAPSHOT);lp_snapshot(index,p,c);PF_MARK(PF_BASELINE);
}
void pf_save(double *p,double *h,double *q,double *w,double *fb){
PF_MARK(PF_SNAPSHOT);lp_save(p,h,q,w,fb);PF_MARK(PF_BASELINE);
}
void lp_jac_time_end(uint64_t start,uint64_t end){jac_ticks+=end-start;jac_calls++;}
'''
return source+'\n'+reuse+'\n'+wrappers
def instrument_cv(source):
rhs=function(source,'jac_rhs_reuse').replace('jac_rhs_reuse(', 'pf_jac_rhs_reuse(',1).replace('lp_eval(', 'pf_lp_eval(')
colored=function(source,'colored_difference').replace('colored_difference(', 'pf_colored_difference(',1).replace('jac_rhs_reuse(', 'pf_jac_rhs_reuse(')
colored=replace(colored,'if (SUNMatZero(matrix)) return -1;',
'PF_SCOPE(0,PF_ZERO);if (SUNMatZero(matrix)) return -1;PF_MARK(PF_OTHER);')
colored=replace(colored,'lp_color=color;memcpy(test,state,NSTATES*sizeof(double));',
'PF_SCOPE(0,PF_PERTURB);lp_color=color;memcpy(test,state,NSTATES*sizeof(double));')
colored=replace(colored,'#if MODEL_JACOBIAN_GAS_REUSE', 'PF_MARK(PF_OTHER);\n#if MODEL_JACOBIAN_GAS_REUSE')
colored=replace(colored,'if (flag) return flag;','PF_SCOPE(0,PF_ASSEMBLY);if (flag) return flag;')
colored=replace(colored,' }\n context->run->jacobian_colored_evals++;',
' PF_MARK(PF_OTHER);\n }\n context->run->jacobian_colored_evals++;')
jac=function(source,'cv_jacobian_original').replace('cv_jacobian_original(', 'pf_cv_jacobian_original(',1).replace('jac_rhs_reuse(', 'pf_jac_rhs_reuse(').replace('colored_difference(', 'pf_colored_difference(')
jac=replace(jac,'int flag=jac_increments(context,y,fy,increments);',
'PF_SCOPE(0,PF_INCREMENT);int flag=jac_increments(context,y,fy,increments);PF_MARK(PF_OTHER);')
jac=replace(jac,'fy=tmp3;', 'PF_SCOPE(0,PF_OTHER);fy=tmp3;')
a,b,e=span(source,'cv_jacobian');sig=source[a:b]
wrapper=sig+'''{
lp_color=-1;uint64_t start=lp_tick();int selected=pf_select();
if(selected)pf_open();
int result=selected?pf_cv_jacobian_original(t,y,fy,matrix,user,tmp1,tmp2,tmp3):cv_jacobian_original(t,y,fy,matrix,user,tmp1,tmp2,tmp3);
if(selected)pf_close();
uint64_t finish=lp_tick();lp_jac_time_end(start,finish);
if(selected)pf_flush(start,finish);
if(!result){lp_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));pf_validate_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));}
return result;
}'''
return source[:a]+rhs+'\n'+colored+'\n'+jac+'\n'+wrapper+source[e:]
def prepare(coarse=False):
OUT.mkdir(exist_ok=True);work=OUT/('coarse-worker' if coarse else 'worker');work.mkdir(exist_ok=True)
sources={p.name:p.read_text(encoding='utf-8') for p in SOURCE.glob('*.c')}
before={k:hashlib.sha256(v.encode()).hexdigest() for k,v in sources.items()}
sources['model.c'],boundaries=instrument_model(sources['model.c'],coarse)
sources['local_probe_support.c']=instrument_support(sources['local_probe_support.c'])
sources['cvode_solver.c']=instrument_cv(sources['cvode_solver.c'])
sources['common.c']=replace(sources['common.c'],'lp_start();','lp_start();pf_initialize();')
sources['common.c']=replace(sources['common.c'],'lp_finish();','lp_finish();pf_finish();')
plan=json.loads((SOURCE.parent/'plan.json').read_text(encoding='utf-8'))
affected=[[int(i in set(g['affectedOperations'])) for i in plan['order']] for g in plan['groups']]
tables='const unsigned char pf_affected[LP_NC][LP_NO]={'+','.join('{'+','.join(map(str,a))+'}' for a in affected)+'};'
sources['local_probe_profile.c']=(HERE/'local_probe_profile.c').read_text(encoding='utf-8').replace('/* PROFILE_TABLES */',tables)
for name in ('model.h','local_probe.h'):shutil.copyfile(SOURCE/name,work/name)
shutil.copyfile(HERE/'local_probe_profile.h',work/'local_probe_profile.h')
cc,sun,_=experiment.builder.toolchain();flags,libs,dlls,exe=experiment.builder.platform_build_inputs(sun);flags+=['-DLP_OBSERVE=0']
started=time.perf_counter()
def compile_one(item):
i,(name,src)=item;path=work/name;path.write_text('#include "local_probe_profile.h"\n'+src,encoding='utf-8',newline='\n');obj=work/f'profile-{i}.o';log=[]
experiment.builder._command([cc,*flags,'-I',str(work),'-I',str(experiment.builder.NATIVE/'include'),'-I',str(sun/'include'),'-c',str(path),'-o',str(obj)],log=log,timeout=180)
return obj,log
with ThreadPoolExecutor(max_workers=4) as pool:objects=list(pool.map(compile_one,enumerate(sources.items())))
log=[];experiment.builder._command([cc,*flags,*[str(o) for o,_ in objects],*experiment.builder.link_library_arguments(libs),'-lm','-o',str(work/exe)],log=log)
for dll in dlls:shutil.copyfile(dll,work/dll.name)
(work/'build.log').write_text('\n'.join(sum([x for _,x in objects],[])+log),encoding='utf-8')
write(OUT/('build-coarse.json' if coarse else 'build.json'),dict(sourceHashes=before,sourceDirectory=str(SOURCE),boundaries=boundaries,seconds=time.perf_counter()-started))
print('BUILT',time.perf_counter()-started,flush=True)
def run(label,stride=16,offset=0,control=False,matrices=False,coarse=False):
work=OUT/label;work.mkdir(exist_ok=True)
executable=(SOURCE if control else OUT/('coarse-worker' if coarse else 'worker'))/'model.exe'
env=os.environ.copy();env.update(LOCAL_PROBE_MASK='0x7ffffff',PROBE_PROFILE_STRIDE=str(stride),PROBE_PROFILE_OFFSET=str(offset),PROBE_PROFILE_MATRICES=str(int(matrices)),PROBE_PROFILE_COARSE=str(int(coarse)))
args=[str(executable),'--method','BDF','--start','0','--stop','10','--sample-step','.01','--max-step','1e30','--rtol','1e-8','--timeout','300',
'--sample-file',str(work/'states.bin'),'--output-block-file',str(work/'outputs.bin'),'--output',str(work/'result.json')]
start=time.perf_counter()
with (work/'stderr.log').open('wb') as f:
p=subprocess.run(args,cwd=work,env=env,stdout=subprocess.PIPE,stderr=f,timeout=330,creationflags=subprocess.CREATE_NO_WINDOW)
elapsed=time.perf_counter()-start
if p.returncode:raise RuntimeError((label,p.returncode,(work/'stderr.log').read_text()[-4000:]))
r=json.loads((work/'result.json').read_text());diag=json.loads((work/'probe.json').read_text())
fields=('success','finalState','final','propertyWarnings','acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu','solveSeconds','solveCpuSeconds')
record={k:r[k] for k in fields}
for name in ('states','outputs','events','jacobians'):
path=work/f'{name}.bin'
if path.exists():
with path.open('rb') as f:record[name+'Sha256']=hashlib.file_digest(f,'sha256').hexdigest()
record.update(label=label,stride=stride,offset=offset,control=control,matrices=matrices,coarse=coarse,processSeconds=elapsed,diagnostic=diag)
write(work/'measurement.json',record)
# Numerical gate on every run, including the sampling-density controls.
reference=json.loads((SOURCE.parent/'all-run-0/measurement.json').read_text(encoding='utf-8'))
exact=['statesSha256','outputsSha256','eventsSha256','finalState','final','propertyWarnings','acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu']
differences=[k for k in exact if record[k]!=reference[k]]
differences += [k for k in ('newtonIterations','newtonConvergenceFailures','contextComparedBytes','contextCopiedBytes','modelCalls','groups') if diag[k]!=reference['diagnostic'][k]]
assert not differences,(label,differences)
if matrices:
ref=SOURCE.parent/'all-audit/jacobians.bin'
with ref.open('rb') as f:assert record['jacobiansSha256']==hashlib.file_digest(f,'sha256').hexdigest()
print('RUN',label,'Jacobian',diag['jacobianSeconds'],'solve',r['solveSeconds'],'CPU',r['solveCpuSeconds'],'process',elapsed,'exact OK',flush=True)
return record
def batch():
run('control-warmup',control=True)
run('profile-warmup',16,101)
for i in range(5):
jobs=[(f'control-{i}',0,0,True),(f'profile-{i}',16,i+17,False)]
if i%2:jobs.reverse()
for label,stride,offset,control in jobs:run(label,stride,offset,control)
for i in range(2):
run(f'disabled-{i}',0,i)
run(f'density8-{i}',8,i+37)
run(f'density32-{i}',32,i+71)
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('action',choices=['prepare','run','batch']);p.add_argument('--label',default='sample');p.add_argument('--stride',type=int,default=16);p.add_argument('--offset',type=int,default=0);p.add_argument('--control',action='store_true');p.add_argument('--matrices',action='store_true');p.add_argument('--coarse',action='store_true');a=p.parse_args()
if a.action=='prepare':prepare(a.coarse)
elif a.action=='batch':batch()
else:run(a.label,a.stride,a.offset,a.control,a.matrices,a.coarse)
+137
View File
@@ -0,0 +1,137 @@
# R288 / position16 最小 real skip 实验
## 结论
1. **完整求解结果逐位一致。** 0–10 s 全轨迹,896 个 132×132 Jacobian(15,611,904 个元素)及每个 t/y、states、outputs、事件、最终状态与既有基线一致;没有使用数值容差。
2. **896/896 次真实 skip;自然 reject/fallback 为 0。** commit 896 次,原目标 native operation 实际执行 0 次,Reference 双路执行 0 次。
3. **当前 replay 更贵。** 7 轮中位数:原 operation 1.143 µs/次,validation + overlay/patch + commit 22.521 µs/次(19.70 倍)。目标完整路径 control 1.186 µs/次,real skip 22.913 µs/次。
4. **暂不把这一实现直接扩展到 position52。** 正确性门槛已满足,净收益门槛未满足。应先降低 metadata 采集、事务复制和解释执行成本;本轮不能据此推断 position52 或其他 interval 的收益。
## 范围与实现
- 全部改动仅在独立生成的实验 worker 及 `tests/manual`;生产路径、默认开关、property cache 语义、原 whole-context guard 未改动。
- 入口仅为 `lp_color == 6 && region == 288` 的 `case 16`,位于原 `lp_reuse` 失败之后。其他位置继续原执行。
- baseline position16 用单独命名空间的 kernels 采集必需的有序事件 metadata。其他 operation 使用原 kernels,没有全局访问插桩。
- guard 来自已验证的 shadow 源码,构建时校验其 SHA-256,并断言 guard 函数保持一致;唯一变量替换是将原事务入口 count 改为当前真实 probe 入口 count。
- 复制当前 probe 到私有 overlay;按当前 entries 验证 first-match/miss,重定位逻辑 slot,从当前 count 追加。通过后构造地址已转换的有序 write set,再 commit。已有 entries、未写字段和其他 pipe slots 保持原值。
- Observer、capacity/scratch、memo lifetime/value、消费字段、valid、pipe branch、未知副作用/非有限值等原保护条件保留;失败只回退原 operation。
- 性能版不执行 Reference、不导出访问日志或 Jacobian、不逐次比较完整 context。保留运行所需 metadata、严格 guard、overlay/patch、计时和累计计数。
## 正确性证据
| 检查 | 结果 |
|---|---:|
| Jacobian、t/y 与既有 all-audit 文件逐字节比较 | 896/896 一致 |
| 每个 Jacobian 的 baseline + 27 group evaluator 出口 | 25,088/25,088 一致 |
| 目标 operation 入口 / 出口 | 896 / 896 一致 |
| 返回状态、dy/w、active property entries 有序字段/valid、全部 pipe slots | 逐位一致 |
| memo 每次 Jacobian 的表内容、owner 绑定、recording 生命周期 | 一致 |
| 所有 probe 的 memo entries 只读检查 | 通过 |
| gas memo 内容、kernel 绑定及计数 | 一致 |
| errno、x87/SSE flags/rounding、warning/observer | 一致 |
| count 差异 / slot relocation 的目标 probe | 638 / 571 |
| 顺序 append | 1,658 |
| PT miss → PT miss / 近零流量无查询 | 829 / 67 |
Audit 对照来自**独立 control 进程真实执行当前 probe**的出口,约 1.88 GB 二进制数据。跨进程地址比较采用 owner/function 绑定身份,数值字段保持原始位模式;不把 C padding 当成数值。memo 表逐 Jacobian 比较,后续每个 probe 同时检查整表未变。目标入口、出口与所有 group 的完整 evaluator 出口均参与比较。audit 的 I/O 保留并恢复 errno、x87 和 SSE 环境。
| 求解器计数 | control / real skip |
|---|---:|
| accepted / rejected | 10840 / 918 |
| Newton iterations / convergence failures | 19371 / 798 |
| nfev / njev / nlu | 44467 / 896 / 3106 |
| solverStarts / stateTransitions | 4 / 1 |
额外负例:每 128 个目标 probe 注入一次晚期 nonfinite-output reject,发生在 overlay 已执行有序更新之后。7 次 reject 均完成无污染检查,fallback/native 原执行各 7 次,commit/skip 各 889 次;全轨迹仍与同一 control 和既有基线逐位一致。没有放宽 guard,也未改变容差。
## 性能方法与原始数据
Windows / MinGW GCC,原构建优化选项(`-O3 -ffp-contract=off -fno-fast-math`)。完成正确性和回退测试后单独编译性能 worker;control、skip 各预热一次,再交替串行运行 7 组。表中顺序就是实际顺序。每轮 states/outputs/events 指纹和全部指定求解器计数保持一致,skip 每轮均为 896、reject 0、原执行 0。
QPC 粗粒度计时;累计整数 tick,积分过程中不做浮点时间换算。validation 包括语义校验所必需的临时有序更新;overlay/patch 包括当前 context 复制及提交 write set 构造;commit 是真实字段写回。完整 target 路径另计,包含调度、计数和额外计时开销。未减去计时器自身成本,未使用 shadow/audit 时间推断性能。
| 运行顺序 | 原执行 µs/次 | validation µs/次 | overlay/patch µs/次 | commit µs/次 | fallback ms | target 总 ms | baseline pos16 总 ms | Jacobian s | 积分 CPU s | 积分 wall s | skip/reject |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| pair-1-control | 1.562 | 0.000 | 0.000 | 0.000 | 0.000 | 1.439 | 1.760 | 2.190858 | 6.375000 | 6.564619 | 0/0 |
| pair-1-skip | 0.000 | 6.172 | 15.011 | 0.434 | 0.000 | 19.785 | 19.239 | 2.007014 | 5.953125 | 6.076049 | 896/0 |
| pair-2-skip | 0.000 | 6.451 | 14.713 | 0.435 | 0.000 | 19.716 | 18.944 | 1.971936 | 5.921875 | 5.910387 | 896/0 |
| pair-2-control | 1.170 | 0.000 | 0.000 | 0.000 | 0.000 | 1.088 | 1.533 | 2.000030 | 6.046875 | 6.140446 | 0/0 |
| pair-3-control | 1.143 | 0.000 | 0.000 | 0.000 | 0.000 | 1.063 | 1.540 | 2.018777 | 6.015625 | 6.213678 | 0/0 |
| pair-3-skip | 0.000 | 6.831 | 15.331 | 0.436 | 0.000 | 20.563 | 19.928 | 2.014426 | 6.031250 | 6.120732 | 896/0 |
| pair-4-skip | 0.000 | 7.101 | 15.141 | 0.434 | 0.000 | 20.651 | 19.911 | 2.035451 | 6.015625 | 6.135601 | 896/0 |
| pair-4-control | 1.093 | 0.000 | 0.000 | 0.000 | 0.000 | 1.017 | 1.508 | 1.933127 | 5.921875 | 5.940958 | 0/0 |
| pair-5-control | 1.080 | 0.000 | 0.000 | 0.000 | 0.000 | 1.009 | 1.676 | 1.935641 | 5.906250 | 5.900561 | 0/0 |
| pair-5-skip | 0.000 | 6.334 | 15.975 | 0.451 | 0.000 | 20.720 | 19.466 | 2.038743 | 5.984375 | 6.166865 | 896/0 |
| pair-6-skip | 0.000 | 6.508 | 14.442 | 0.444 | 0.000 | 19.496 | 19.729 | 2.016491 | 5.906250 | 6.033967 | 896/0 |
| pair-6-control | 1.177 | 0.000 | 0.000 | 0.000 | 0.000 | 1.093 | 1.512 | 2.030009 | 6.250000 | 6.418474 | 0/0 |
| pair-7-control | 1.119 | 0.000 | 0.000 | 0.000 | 0.000 | 1.039 | 1.499 | 1.970795 | 5.953125 | 6.030361 | 0/0 |
| pair-7-skip | 0.000 | 6.166 | 15.893 | 0.462 | 0.000 | 20.530 | 19.112 | 2.008419 | 6.078125 | 6.126521 | 896/0 |
baseline pos16:control 为原 baseline operation;skip 包含原 baseline operation + 本次 replay 必需 metadata 采集,不能漏算这部分成本。性能各轮没有自然 reject,因此 fallback 总时间为 0;这不代表一次 fallback 的成本为零,本轮未估计该分支的单次性能。
### 中位数及 min/max
| 项目 | control 中位数 [min, max] | real skip 中位数 [min, max] |
|---|---:|---:|
| originalSeconds(s,896 次累计) | 0.001024400 [0.000967700, 0.001399500] | 0.000000000 [0.000000000, 0.000000000] |
| validationSeconds(s,896 次累计) | 0.000000000 [0.000000000, 0.000000000] | 0.005780000 [0.005525000, 0.006362100] |
| overlayPatchSeconds(s,896 次累计) | 0.000000000 [0.000000000, 0.000000000] | 0.013566200 [0.012939600, 0.014314000] |
| commitSeconds(s,896 次累计) | 0.000000000 [0.000000000, 0.000000000] | 0.000390700 [0.000388500, 0.000414100] |
| fallbackSeconds(s,896 次累计) | 0.000000000 [0.000000000, 0.000000000] | 0.000000000 [0.000000000, 0.000000000] |
| pathSeconds(s,896 次累计) | 0.001062600 [0.001009100, 0.001439000] | 0.020530400 [0.019496400, 0.020720200] |
| baselineRecordSeconds(s,896 次累计) | 0.001533400 [0.001499200, 0.001760000] | 0.019465900 [0.018944100, 0.019927500] |
| jacobianSeconds(s,896 次累计) | 2.000029500 [1.933127300, 2.190857900] | 2.014426400 [1.971935900, 2.038742900] |
| solveCpuSeconds(s,896 次累计) | 6.015625000 [5.906250000, 6.375000000] | 5.984375000 [5.906250000, 6.078125000] |
| solveSeconds(s,896 次累计) | 6.140445900 [5.900561100, 6.564618800] | 6.120731700 [5.910387000, 6.166865500] |
### 配对差值
| 组 | 原计算 − replay 三阶段 ms | target 完整路径净节省 ms | 新增 metadata 采集 ms | Jacobian Δ s | 积分 CPU Δ s | 积分 wall Δ s |
|---|---:|---:|---:|---:|---:|---:|
| 1 | -17.969 | -18.346 | 17.479 | -0.183844 | -0.421875 | -0.488570 |
| 2 | -18.304 | -18.628 | 17.411 | -0.028094 | -0.125000 | -0.230059 |
| 3 | -19.224 | -19.501 | 18.387 | -0.004351 | 0.015625 | -0.092946 |
| 4 | -19.338 | -19.633 | 18.403 | 0.102324 | 0.093750 | 0.194642 |
| 5 | -19.425 | -19.711 | 17.790 | 0.103102 | 0.078125 | 0.266304 |
| 6 | -18.114 | -18.404 | 18.217 | -0.013517 | -0.343750 | -0.384508 |
| 7 | -19.176 | -19.492 | 17.613 | 0.037623 | 0.125000 | 0.096160 |
净节省为正表示节省,Δ = skip − control。
- 原计算成本 − replay 三阶段成本:配对中位数 **-19.176 ms / 896 次**。
- target 完整路径净节省:配对中位数 **-19.492 ms / 896 次**;范围 [-19.711, -18.346] ms。
- 此外 baseline metadata 采集增加:配对中位数 **17.790 ms**。
- Jacobian callback 配对 Δ:中位数 -0.004351 s,范围 [-0.183844, 0.103102] s。
- 积分 wall 配对 Δ:中位数 -0.092946 s,范围 [-0.488570, 0.266304] s。
全局时间受调频、调度和系统负载影响,不能把某轮 Jacobian/积分变快归因于这个 operation。目标路径在全部配对中均更慢,已经足以否定当前实现的局部净收益;本轮不预测整个 local probe 的最终加速比例。
## 成本解释及下一阶段条件
原 operation 在现有 Jacobian memo 的只读复用环境下已经很便宜;当前严格 replay 仍要初始化/复制 156,816 B 的 overlay(含完整 memo),解释事件并构造 write set。运行所需有效 metadata 为 2,224–23,344 B,Plan 固定预留 90,224 B,patch 预留 16,384 B。这些是当前隔离实现的实际成本,不是机制理论上的下限。
因此,本轮证明了目标路径可以安全真实跳过,但没有证明性能优化成立。下一步应先针对事务存储和 metadata 表达做最小化,再重复同样的正确性与交替性能验收;不因 position16 正确就直接扩大到 position52/整个 R475/全部 reuse interval。
## 复现与证据位置
运行目录:`test/r288-real-skip-20260917/`。依赖上一轮生成的 local-probe worker、context-access worker 和 shadow-certified 源码;工具链复用原实验配置,不安装依赖。
```powershell
.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py prepare --audit
.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py prepare --audit --skip
.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py run --audit
.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py run --audit --skip
.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py run --audit --skip --label audit-forced-reject --force 128
.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py prepare
.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py prepare --skip
.venv-win/Scripts/python.exe tests/manual/real_skip_experiment.py benchmark --pairs 7
.venv-win/Scripts/python.exe tests/manual/analyze_real_skip.py
```
- `audit-{control,skip}-run/validation.json`:正确性、独立执行计数、完整求解器计数及文件指纹。
- `audit-forced-reject/validation.json`:真实回退及 rollback 检查。
- `byte-comparison.json`:全部 Jacobian/t/y、states、outputs、events 与原始基线的逐字节比较。
- `audit-control-run/audit.bin`:所有 group 和目标 operation 的实际执行对照出口。
- `performance.json`:14 次按实际顺序记录的原始数据;各轮目录保留独立日志和结果。
- `performance-summary.json`:中位数、min/max 和逐对差值。
- `{audit,perf}-{control,skip}/build.json`:源文件哈希、guard 一致性和构建记录。
+212
View File
@@ -0,0 +1,212 @@
# R288 / position16:typed semantic replay 成本下限实验
## 三个问题的答案
1. 去掉完整 overlay 与通用事件解释器后,三阶段 replay 的每轮均值中位数为 **7.109 µs/次**,各轮范围 **4.082–13.011 µs/次**。包含调度、计时及计数的完整目标路径中位数为 **7.575 µs/次**。这是本实现、工具链和机器的实测结果,不是理论最低成本。
2. 同批 A 的原 operation 为 **1.775 µs/次**;专用 replay **仍高于原计算**。与历史 1.143 µs 不直接跨批比较。
3. 计入 baseline 捕获新增成本,配对完整机制净节省中位数为 **-20.415 ms / 896 次**;含完整路径计时/统计开销的口径为 **-21.082 ms**。**当前实现对 R288 没有净收益;这种低成本 operation 应直接原计算。**
## 范围与事务语义
所有实验仅针对 group6 / R288 / position16,在独立 worker 中、原 whole-context guard 失败之后启用。没有扩展 position52、R475 或其他 interval,没有接入生产默认路径。已有工作区改动不属于本实验的修改范围。
- `fp_validate` 只读当前真实 probe;保留输入位比较、有限值、FP/errno、observer、count/capacity、memo owner/recording、全部 active entry 绑定、pipe branch 等保护。
- 两次 PT 查询依次按当前有序 entries 验证 miss;第二次查询还显式检查第一个 pending entry 的虚拟匹配。append 位置由当前 count 决定,不使用 baseline slot。
- memo key 的 hash 在 baseline 捕获时计算;probe 仍按相同容量、同一 bounded linear-probe 顺序查找,并逐位比较完整 key 和 value。没有复制 memo,也没有绕过 memo 验证。
- 只支持已验证的两个固定 schema:67 次近零流量无查询、829 次 PT miss → PT miss。不支持的路径直接 reject。
- 这两个 schema 中,物性计算消费的条目均是本 operation 新建的条目。初始化、字段写入、valid 测试和读取之间的条件由固定 schema 保证,并在正确性版对每个 baseline 的所有原始访问逐条证明;不是把这些 guard 删除。
- `fp_prepare` 最多构造两个 pending property entries、一个 pipe[0]、新 count、q[45] 和 density/pipe memo reuse 增量。所有真实写入仅在 `fp_commit` 发生。未被写入的 probe 数据保持原值。
专用版是固定 R288 schema 的 typed 原型,不是通用生产 codegen。静态 schema 的核对表从全轨迹真实 generic 记录自动生成;原 native 源文件受既有 SHA-256 约束,未知源代码变化会停止构建。
## Metadata 与临时存储
| 项目 | generic | specialized |
|---|---:|---:|
| 每次 baseline 有效 metadata | 2,224–23,344 B | 240 B |
| 持久 metadata 固定预留 | 90,224 B | 240 B |
| 捕获临时区 | 通用事件记录器 | 96 B |
| context/memo/pipe 完整 overlay | 156,816 B | 0 B |
| pending patch | 16,384 B write-set,另有 overlay | 456 B,总计且不重复计算 |
- **静态可确定**:medium 常量、字段布局、读取/写入顺序、valid 位演变、最多两个追加条目、memo 类型、pipe[0]、q[45]。
- **每个 baseline 动态捕获**:输入、输出、U/D 物性字段、pipe memo key/value、三个 memo hash、入口 count、owner/Jacobian 生命周期及 FP/errno 条件,共 240 B。
- **probe 才解析**:当前有序 entries、query miss、当前 count/capacity、memo 实际位置和值、owner/observer/pipe 条件;验证成功后构造 456 B pending。
## 正确性验收
C 验收版:同一次 baseline 真实执行同时生成 generic 事件与小型 metadata;核对 896 份完整访问 schema、896 次 generic/typed accept/reject 和最终 patch,包括逻辑 slot 映射。
D 验收版:使用性能版相同的最小 native 捕获 hooks,逐位核对先前真实 generic baseline 记录(跨进程 memo 地址转换为 owner 绑定身份),然后重复全部求解验收。该离线记录只用于 audit;性能 worker 不读取这些记录。
| 检查 | 结果 |
|---|---:|
| Jacobian 全元素与 t/y | 896/896 逐字节一致,15,611,904 个矩阵元素 |
| evaluator 返回状态、dy/w 和完整 context 出口 | 25,088/25,088 一致 |
| 目标 operation 入口/出口、property count/有序字段/valid、pipe cache | 全部一致 |
| memo entries、只读生命周期、owner/kernel 绑定及计数 | 全部一致 |
| states/outputs/events、最终状态、warning、FP/errno | 全部一致 |
| 正常目标 attempts / skip / reject / 原执行 | 896 / 896 / 0 / 0 |
| 负例 guard 判定及无写入检查 | C、D 各 20 项通过 |
| 晚期 forced reject | C、D 各 7 次,无污染;889 skip、7 原执行 |
| count 差异 / relocation / append | 638 / 571 / 1,658 |
负例包含 observer、无容量/第二次追加不足、pipe hit、memo recording/miss/value、输入改变、晚期非有限输出、过期生命周期、FP flags/rounding、entry owner、U/D 查询转 hit,以及已拒绝 metadata 的 consumed/valid/已有条目更新/未知副作用标记。后四项是错误记录传播检查;逐字段读写与 valid 演变的实质检查来自 896 份完整访问 contract 对照。
| accepted | rejected | Newton iterations | convergence failures | nfev | njev | nlu | solverStarts | stateTransitions |
|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|10840|918|19371|798|44467|896|3106|4|1|
全部使用位比较和逐字节比较,没有更改容差。性能版二进制符号检查确认无 generic interpreter、完整 overlay 或 audit oracle。
## Generic 成本归因
单独归因 worker 的总 replay 阶段为 42.544 µs/次;该数值含细粒度插桩,不与历史 22.521 µs 强行逐项相加。空 QPC bracket 均值 0.01622 µs,细粒度嵌套计时会额外放大短事件成本。
以下分类均为每个目标 probe 的平均原始计时;父项含子项,明确标记重叠,不能相加当作净成本。
| 分类 | µs/次 | 口径 |
|---|---:|---|
| baseline 原执行 + metadata 捕获 | 31.7568 | 新增成本由后面的同批 A/B 配对得出 |
| PT first-match / miss | 0.2964 | 真实顺序扫描;包含一组自身计时 |
| consumed-field 读取事件 | 5.3567 | 含 metadata 分派和嵌套字段比较 |
| 其中:字段值/绑定比较 | 1.6404 | 是上一行的子项 |
| valid 测试事件 | 0.1645 | 含事件分派 |
| memo 验证 | 0.3146 | 完整 key/value 与 bounded lookup;SCALAR 事件的子项 |
| 完整 overlay 初始化/复制/绑定 | 20.8996 | 包含 context、memo、全部 pipe |
| logical slot relocation | 0.4641 | 包含映射冲突检查;属于 ALLOCATE/QUERY 子项 |
| pending entry 字段写入 | 0.9435 | 原始 generic 实测,含自身计时 |
| pending valid 更新 | 0.2103 | 原始 generic 实测,含自身计时 |
| 追加与 count 更新 | 5.3371 | 原始 generic 实测,含自身计时 |
| pipe patch 写入 | 0.3973 | 原始 generic 实测,含自身计时 |
| write-set 构造 | 0.8175 | 原始 generic 实测,含自身计时 |
| commit | 0.5316 | 原始 generic 实测,含自身计时 |
| 公共 guard(映射初始化之前) | 3.9068 | 细分项;已经包含在对应父项内 |
| overlay 清零/poison 初始化 | 7.5767 | 细分项;已经包含在对应父项内 |
| property context/entries 复制 | 0.1152 | 细分项;已经包含在对应父项内 |
| 全部 pipe 复制 | 0.2791 | 细分项;已经包含在对应父项内 |
| memo header/entries 复制 | 12.2629 | 细分项;已经包含在对应父项内 |
| overlay 指针绑定 | 0.2096 | 细分项;已经包含在对应父项内 |
| 仅 event metadata 遍历/类型分派微基准 | 0.1790 | 每份真实 plan 重复 100 次;不做 guard/write,不能当作真实解释器的独立可加项 |
| 空计时 bracket | 0.01622 | 每组含首尾 QPC;累计统计另有开销,最终以低扰动 A/B/C 为准 |
通用解释器的实际成本分布在 metadata 访问、字段比较、slot 映射、写入分派和循环控制中,没有一个能独立相减的精确“dispatch 时间”。归因版保留原始嵌套数据,最终性能版移除这些细粒度计时。
## 低扰动 A/B/C 性能
A=原 operation,B=原 generic replay,C=typed/minimal replay。各预热一次,9 组按 ABC → BCA → CAB 循环,全部串行。无 Reference、完整 context 比较或详细访问日志;保留机制所需 metadata、guard、patch 和少量累计计时。每轮 B/C 均 skip 896、reject 0、原执行 0,并核对采样结果和全部指定计数。
### 中位数 [min, max]
| 项目 | A | B | C |
|---|---:|---:|---:|
| original (µs/次) | 1.775 [1.574, 2.779] | 0.000 [0.000, 0.000] | 0.000 [0.000, 0.000] |
| validation (µs/次) | 0.000 [0.000, 0.000] | 15.540 [11.031, 50.369] | 5.615 [3.718, 10.492] |
| patch (µs/次) | 0.000 [0.000, 0.000] | 36.240 [24.357, 60.464] | 0.308 [0.237, 1.084] |
| commit (µs/次) | 0.000 [0.000, 0.000] | 0.672 [0.548, 1.210] | 0.144 [0.128, 7.584] |
| stages (µs/次) | 0.000 [0.000, 0.000] | 50.367 [35.999, 92.380] | 7.109 [4.082, 13.011] |
| path (µs/次) | 1.829 [1.650, 2.836] | 51.429 [36.540, 93.061] | 7.575 [4.366, 13.597] |
| outsideStages (µs/次) | 0.053 [0.048, 0.151] | 0.681 [0.541, 1.062] | 0.377 [0.284, 1.001] |
| baseline (µs/次) | 3.812 [2.282, 8.872] | 46.228 [31.371, 67.965] | 22.120 [14.653, 44.772] |
| jacobian (s) | 4.657 [3.108, 5.589] | 4.564 [3.524, 5.353] | 4.275 [2.912, 4.820] |
| cpu (s) | 8.531 [7.469, 8.797] | 8.641 [7.969, 9.062] | 8.672 [7.562, 8.906] |
| wall (s) | 13.458 [9.312, 16.059] | 13.025 [10.581, 15.476] | 13.093 [8.982, 14.628] |
replay 三阶段先在每一轮内求和再取中位数,因此不必等于三个分项中位数之和。baseline 项包括该次 baseline operation 本身;捕获新增成本应减去 A 的 baseline 项。B 的 patch 项含全 overlay copy + write-set;C 的 patch 项仅小型 pending 构造。outsideStages 是完整路径减去被包围的阶段时间,包含外层计时、统计、调度与元数据 bookkeeping;它不是纯计时器成本。无自然 reject,fallback 总时间为 0,不代表单次 fallback 免费。
### 每轮原始数据(实际执行顺序)
| 轮 / 模式 | 原 op µs | validation µs | patch µs | commit µs | replay 三阶段 µs | 完整路径 µs | baseline µs | Jacobian s | CPU s | wall s |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| round-1 / A | 2.779 | 0.000 | 0.000 | 0.000 | 0.000 | 2.836 | 3.812 | 5.589474 | 8.765625 | 16.059343 |
| round-1 / B | 0.000 | 11.932 | 30.241 | 0.619 | 42.792 | 43.494 | 49.403 | 4.178009 | 8.640625 | 12.130358 |
| round-1 / C | 0.000 | 4.836 | 0.301 | 0.569 | 5.706 | 6.083 | 17.992 | 4.274571 | 8.687500 | 12.886425 |
| round-2 / B | 0.000 | 15.540 | 60.464 | 0.639 | 76.643 | 77.297 | 67.965 | 4.720768 | 8.812500 | 13.688767 |
| round-2 / C | 0.000 | 6.669 | 0.319 | 0.218 | 7.206 | 7.575 | 25.742 | 4.820219 | 8.625000 | 13.576249 |
| round-2 / A | 1.745 | 0.000 | 0.000 | 0.000 | 0.000 | 1.794 | 2.880 | 5.162364 | 8.468750 | 14.128570 |
| round-3 / C | 0.000 | 7.646 | 0.291 | 0.141 | 8.079 | 9.080 | 21.240 | 4.792801 | 8.734375 | 14.628191 |
| round-3 / A | 1.682 | 0.000 | 0.000 | 0.000 | 0.000 | 1.730 | 2.282 | 3.242320 | 7.531250 | 9.505273 |
| round-3 / B | 0.000 | 13.321 | 36.411 | 0.635 | 50.367 | 51.429 | 48.727 | 4.563694 | 8.609375 | 13.024813 |
| round-4 / A | 1.574 | 0.000 | 0.000 | 0.000 | 0.000 | 1.650 | 4.330 | 5.109714 | 8.671875 | 14.935839 |
| round-4 / B | 0.000 | 20.122 | 36.240 | 0.673 | 57.035 | 57.856 | 44.720 | 5.159674 | 9.046875 | 15.093424 |
| round-4 / C | 0.000 | 5.615 | 1.084 | 0.144 | 6.843 | 7.334 | 18.844 | 4.173146 | 8.437500 | 12.573423 |
| round-5 / B | 0.000 | 11.094 | 24.357 | 0.548 | 35.999 | 36.540 | 31.371 | 3.523633 | 7.968750 | 10.581341 |
| round-5 / C | 0.000 | 10.492 | 0.325 | 0.178 | 10.995 | 11.341 | 23.241 | 4.240836 | 8.671875 | 13.092565 |
| round-5 / A | 2.121 | 0.000 | 0.000 | 0.000 | 0.000 | 2.174 | 4.388 | 4.796397 | 8.531250 | 13.405962 |
| round-6 / C | 0.000 | 5.266 | 0.294 | 0.133 | 5.694 | 6.061 | 22.120 | 3.977201 | 8.203125 | 11.476165 |
| round-6 / A | 2.652 | 0.000 | 0.000 | 0.000 | 0.000 | 2.702 | 3.862 | 4.054356 | 8.125000 | 12.824942 |
| round-6 / B | 0.000 | 19.544 | 28.453 | 1.173 | 49.170 | 49.836 | 40.725 | 3.719785 | 8.500000 | 11.533035 |
| round-7 / A | 1.775 | 0.000 | 0.000 | 0.000 | 0.000 | 1.829 | 2.834 | 4.657222 | 8.578125 | 14.247693 |
| round-7 / B | 0.000 | 16.876 | 30.680 | 1.210 | 48.766 | 49.492 | 46.228 | 5.353380 | 8.687500 | 15.475863 |
| round-7 / C | 0.000 | 5.111 | 0.317 | 7.584 | 13.011 | 13.597 | 44.772 | 4.426264 | 8.906250 | 13.147138 |
| round-8 / B | 0.000 | 50.369 | 41.295 | 0.716 | 92.380 | 93.061 | 47.970 | 5.143477 | 9.062500 | 14.801028 |
| round-8 / C | 0.000 | 3.718 | 0.237 | 0.128 | 4.082 | 4.366 | 14.653 | 2.911920 | 7.562500 | 8.981533 |
| round-8 / A | 1.624 | 0.000 | 0.000 | 0.000 | 0.000 | 1.674 | 2.293 | 3.108083 | 7.468750 | 9.311726 |
| round-9 / C | 0.000 | 6.660 | 0.308 | 0.140 | 7.109 | 8.006 | 26.959 | 4.731763 | 8.687500 | 13.629807 |
| round-9 / A | 2.412 | 0.000 | 0.000 | 0.000 | 0.000 | 2.563 | 8.872 | 4.641674 | 8.796875 | 13.458097 |
| round-9 / B | 0.000 | 11.031 | 44.264 | 0.672 | 55.967 | 56.590 | 35.306 | 3.917557 | 8.140625 | 11.486951 |
### 两种收益口径
- probe 局部净节省 = A 原 operation − validation − patch − commit。
- 完整机制净节省 = 896 × probe 局部净节省 − (baseline 捕获总耗时 − A baseline 原执行耗时)。
- 再给出包含外层调度、计时与统计的保守口径:A 完整目标路径 − replay 完整目标路径 − baseline 新增成本。
- 正数为节省,负数为额外成本。时间均使用同组配对,不拿历史 1.143 µs 作分母。
| 轮 / 模式 | probe 净节省 µs/次 | baseline 新增 µs/次 | 完整机制净节省 ms/896 次 | 含外层开销净节省 ms | Jacobian Δ s | CPU Δ s | wall Δ s |
|---|---:|---:|---:|---:|---:|---:|---:|
| 1 / B | -40.013 | 45.591 | -76.701 | -77.279 | -1.411465 | -0.125000 | -3.928985 |
| 1 / C | -2.927 | 14.180 | -15.328 | -15.614 | -1.314904 | -0.078125 | -3.172919 |
| 2 / B | -74.899 | 65.085 | -125.425 | -125.966 | -0.441596 | 0.343750 | -0.439802 |
| 2 / C | -5.461 | 22.862 | -25.378 | -25.664 | -0.342145 | 0.156250 | -0.552320 |
| 3 / B | -48.684 | 46.446 | -85.237 | -86.146 | 1.321374 | 1.078125 | 3.519541 |
| 3 / C | -6.396 | 18.959 | -22.718 | -23.572 | 1.550480 | 1.203125 | 5.122919 |
| 4 / B | -55.461 | 40.390 | -85.882 | -86.551 | 0.049960 | 0.375000 | 0.157585 |
| 4 / C | -5.269 | 14.514 | -17.726 | -18.098 | -0.936568 | -0.234375 | -2.362415 |
| 5 / B | -33.879 | 26.983 | -54.532 | -54.969 | -1.272764 | -0.562500 | -2.824622 |
| 5 / C | -8.874 | 18.853 | -24.844 | -25.106 | -0.555561 | 0.140625 | -0.313398 |
| 6 / B | -46.518 | 36.863 | -74.709 | -75.261 | -0.334571 | 0.375000 | -1.291907 |
| 6 / C | -3.041 | 18.258 | -19.084 | -19.368 | -0.077155 | 0.078125 | -1.348777 |
| 7 / B | -46.991 | 43.394 | -80.985 | -81.587 | 0.696158 | 0.109375 | 1.228170 |
| 7 / C | -11.236 | 41.938 | -47.644 | -48.121 | -0.230959 | 0.328125 | -1.100554 |
| 8 / B | -90.755 | 45.677 | -122.243 | -122.809 | 2.035395 | 1.593750 | 5.489302 |
| 8 / C | -2.458 | 12.360 | -13.277 | -13.487 | -0.196162 | 0.093750 | -0.330193 |
| 9 / B | -53.555 | 26.434 | -71.670 | -72.093 | -0.724117 | -0.656250 | -1.971146 |
| 9 / C | -4.697 | 18.087 | -20.415 | -21.082 | 0.090088 | -0.109375 | 0.171710 |
| 配对统计:中位数 [min, max] | B | C |
|---|---:|---:|
| probeSavingUs | -48.684375 [-90.755246, -33.878683] | -5.268973 [-11.236272, -2.457924] |
| metadataIncrementUs | 43.393862 [26.434040, 65.084598] | 18.257701 [12.360156, 41.938281] |
| mechanismSavingMs | -80.985100 [-125.425100, -54.532400] | -20.414600 [-47.644400, -13.277000] |
| completePathSavingMs | -81.587000 [-125.966400, -54.969100] | -21.082500 [-48.120900, -13.486700] |
| jacobianDelta | -0.334571 [-1.411465, 2.035395] | -0.230959 [-1.314904, 1.550480] |
| cpuDelta | 0.343750 [-0.656250, 1.593750] | 0.093750 [-0.234375, 1.203125] |
| wallDelta | -0.439802 [-3.928985, 5.489302] | -0.552320 [-3.172919, 5.122919] |
所有 operation 阶段使用 QPC 墙钟计时,包含调度长尾;积分 CPU 时间单独来自求解器统计。系统负载和频率使本批数据存在较大波动(包括 commit 分项的极端值),因此报告同时保留中位数、min/max 和每组配对结果,不将归因插桩或调度延迟声称为精确的算法 CPU 成本。9 组局部与完整机制净收益均为负;不从总 Jacobian/积分墙钟的正负波动推断整个 local probe 的收益,也不声称已证明理论成本下限。计时未做不可靠的逐事件扣减。
## 停止条件与交付
当前实现对 R288 没有净收益;这种低成本 operation 应直接原计算。
本轮到此停止,不继续为 R288 寻找更多微优化,也不扩展到 position52。保留生产原计算路径和原 whole-context guard。
代码入口:`tests/manual/specialized_replay_experiment.py`。核心为 `typed_replay_core.inc`、`typed_replay_runtime.inc`;`typed_contract_check.inc` 与 `typed_oracle.inc` 只用于 audit。报告生成器为 `analyze_typed_replay.py`。
复现顺序(项目根目录,使用既有工具链,不安装依赖):
```powershell
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode P
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode P
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode C --audit
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode C --audit
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode C --audit --label forced-reject --force 128
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode D --audit
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode D --audit
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py run --mode D --audit --label forced-reject --force 128
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode A
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode B
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py prepare --mode C
.venv-win/Scripts/python.exe tests/manual/specialized_replay_experiment.py benchmark --rounds 9
.venv-win/Scripts/python.exe tests/manual/analyze_typed_replay.py
```
证据目录:`test/r288-typed-replay-20260917/`。`performance.json` 保存 27 次原始记录;`performance-summary.json` 保存统计和逐组净收益;`byte-comparison.json` 保存逐字节结果;C/D 的 `validation.json` 和 `typed-summary.json` 保存全部正确性计数。P 的 `attribution.json` 保存原始 tick、调用次数和计时器测量。
+28
View File
@@ -0,0 +1,28 @@
#ifndef R288_REAL_SKIP_H
#define R288_REAL_SKIP_H
#include "local_probe.h"
#include "context_shadow_replay.h"
#ifndef RR_SKIP
#define RR_SKIP 0
#endif
#ifndef RR_AUDIT
#define RR_AUDIT 0
#endif
extern int rr_original_scope,rr_replaying;
extern unsigned long long rr_native_calls;
void rr_start(void);
void rr_finish(void);
void rr_jacobian(void);
void rr_record_begin(NativePropertyCache*,NativePipeCache*,const double*);
void rr_record_end(double);
double rr_execute(NativePropertyCache*,NativePipeCache*,const double*,SROperation);
#if RR_AUDIT
void rr_eval_exit(double,const double*,const double*,const double*,int,NativePropertyCache*,NativePipeCache*,ModelJacobianWorkspace*);
void rr_matrix(double,const double*,const double*);
#else
#define rr_eval_exit(...) ((void)0)
#define rr_matrix(...) ((void)0)
#endif
double rrrec_native_pipe_flow_cached_context(NativePropertyCache*,NativePipeCache*,const NativeMedium*,double,double,double,double,double,double,int);
double rrrec_native_temperature_ph_context(NativePropertyCache*,const NativeMedium*,double,double);
#endif
+180
View File
@@ -0,0 +1,180 @@
"""Only group 6 / region 288 / position 16, independent real-skip workers."""
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import argparse,hashlib,json,os,re,shutil,subprocess,time
import diagnose_context_shadow as shadow
ROOT=shadow.ROOT;HERE=Path(__file__).parent
BASE=ROOT/'test/local-probe-20260917/worker'
ACCESS=ROOT/'test/context-access-20260917/worker'
OUT=ROOT/'test/r288-real-skip-20260917'
ex=shadow.access.dx.ex
replace=shadow.replace
def write(path,obj):ex.write(path,obj)
def fn(source,name):
if name=='ax_test':
start=source.index('unsigned ax_test(');end=source.index('\nvoid ax_scalar',start)
return source[start:end]
if name=='event':
start=source.index('static Event *event(');end=source.index('\nstatic int state_pointer',start)
return source[start:end]
return shadow.access.dx.function(source,name)
def core(audit):
source=(HERE/'context_shadow_replay.c').read_text(encoding='utf-8')
certified=json.loads((shadow.OUT/'worker/build.json').read_text(encoding='utf-8'))['instrumentedHashes']['context_shadow_replay.c']
assert hashlib.sha256(source.encode()).hexdigest()==certified, 'Use the shadow-certified source unchanged.'
prefix=source[:source.index('typedef struct {\n unsigned long long total')]
prefix=prefix.replace('static void save_environment','static __attribute__((unused)) void save_environment').replace('static void restore_environment','static __attribute__((unused)) void restore_environment')
globals='''
static Plan plan;
static int phase,position=16,group,configured=16;
static unsigned long long jac;
static double sim_time,op_inputs[4];
static NativePropertyCache *bound;
static NativePipeCache *bound_pipes;
static SRContext overlay;
static size_t probe_entry_count;
static void fatal(const char *s){fprintf(stderr,"real-skip fatal: %s\\n",s);abort();}
static void initialize(void){}
void sr_native_enter(const char *name){(void)name;if(phase==CANDIDATE)fatal("Candidate called physics");}
'''
names=['locate','event','state_pointer','floating_field','ax_access','sr_or','ax_bind','ax_begin','ax_query','ax_match','ax_new','ax_test','ax_scalar','path_of_plan','ax_end','ax_result','from_live','same_medium_key','first_match','map_slot','scalar_lookup','state_read_equal','replay_overlay']
parts=[prefix,globals]+[fn(source,n) for n in names]
result='\n'.join(parts).replace('(size_t)slot<entry.context.count','(size_t)slot<probe_entry_count')
assert fn(result,'replay_overlay')==fn(source,'replay_overlay').replace('(size_t)slot<entry.context.count','(size_t)slot<probe_entry_count')
if audit:
start=source.index('typedef struct {const char *name;size_t offset,size;} Field;')
end=source.index('static void dump_blob(',start)
comparison=source[start:end]
a,b,e=ex.function_span(comparison,'compare_frame');comparison=comparison[:a]+comparison[e:]
result+='\n'+comparison
return result
def model_source(source,plan):
a,b,e=ex.function_span(source,'model_eval_local_internal');body=source[a:e]
op=plan['code'][16][0]
assert body.count(op)==2 and plan['regions'][288]==[16,17]
expr=op.split('=',1)[1].rstrip(';')
for old,new in [('p[3]','x[3]'),('g[4].p','x[1]'),('g[4].T','x[0]'),('h[64]','x[2]')]:expr=expr.replace(old,new)
helper=f'static double rr_original16(NativePropertyCache *properties,NativePipeCache *pipe_cache,const double *x){{return {expr};}}\n'
ins='(double[]){g[4].T,g[4].p,h[64],p[3]}'
baseline='rr_record_begin(properties,pipe_cache,'+ins+');\n#if RR_SKIP\n'+op.replace('native_pipe_flow_cached_context','rrrec_native_pipe_flow_cached_context').replace('native_temperature_ph_context','rrrec_native_temperature_ph_context')+'\n#else\n'+op+'\n#endif\nrr_record_end(q[45]);'
body=body.replace(op,baseline,1)
# The case is reachable only after the original whole-context guard failed.
start=body.index('case 16:{');end=body.index('break;}',start)
case=body[start:end]
case=replace(case,op,'if(lp_color==6 && region==288){q[45]=rr_execute(properties,pipe_cache,'+ins+',rr_original16);}else{'+op+'}')
body=body[:start]+case+body[end:]
# Audit every Jacobian baseline/probe's exit, including all other groups.
body=replace(body,'return 1;}','rr_eval_exit(t,y,dy,w,1,properties,pipe_cache,jacobian);return 1;}')
return source[:a]+helper+body+source[e:]
def prepare(audit,skip):
if not audit:
gate=json.loads((OUT/'audit-skip-run/validation.json').read_text(encoding='utf-8'))
assert gate['exact'] and gate['counters']['realSkips']==896
OUT.mkdir(exist_ok=True)
label=('audit' if audit else 'perf')+('-skip' if skip else '-control')
work=OUT/label;work.mkdir(exist_ok=True)
sources={p.name:p.read_text(encoding='utf-8') for p in BASE.glob('*.c')}
original_hashes={k:hashlib.sha256(v.encode()).hexdigest() for k,v in sources.items()}
plan=json.loads((ROOT/'test/context-fallback-20260917/plan.json').read_text(encoding='utf-8'))
sources['model.c']=model_source(sources['model.c'],plan)
sources['common.c']=replace(sources['common.c'],'lp_start();','lp_start();rr_start();')
sources['common.c']=replace(sources['common.c'],'lp_finish();','lp_finish();rr_finish();')
sources['cvode_solver.c']=replace(sources['cvode_solver.c'],'{lp_color=-1;uint64_t start=lp_tick();','{lp_color=-1;rr_jacobian();uint64_t start=lp_tick();')
sources['cvode_solver.c']=replace(sources['cvode_solver.c'],'if(!result)lp_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));','if(!result){lp_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));rr_matrix(t,N_VGetArrayPointer(y),SUNDenseMatrix_Data(matrix));}')
sources['pipe.c']=shadow.access.change_function(sources['pipe.c'],'native_pipe_flow_cached_context',lambda body:'if(rr_replaying)abort();if(rr_original_scope)rr_native_calls++;\n'+body)
# Original physics files are NOT access-instrumented. A separate namespace
# is called only to record the baseline target operation's required IR.
recording={name:(ACCESS/name).read_text(encoding='utf-8') for name in ['properties.c','pipe.c']}
names=set()
for code in recording.values():
names.update(re.findall(r'(?m)^(?:static\s+|NATIVE_COMPONENT_INTERNAL\s+)?(?:int|double|void|NativePropertyState\s*\*|NativeGas|NativeJacobianScalarEntry\s*\*)\s*(\w+)\([^;{}]*\)\s*\{',code))
names.add('helium_medium')
aliases='\n'.join('#define '+n+' rrrec_'+n for n in sorted(names))+'\n'
for name,code in recording.items():sources['record_'+name]=aliases+code
sources['real_skip_runtime.c']=(HERE/'real_skip_runtime.c').read_text(encoding='utf-8')
for name in ['model.h','local_probe.h']:shutil.copyfile(BASE/name,work/name)
shutil.copyfile(ACCESS/'context_fallback_diag.h',work/'context_fallback_diag.h')
shutil.copyfile(HERE/'context_shadow_replay.h',work/'context_shadow_replay.h')
shutil.copyfile(HERE/'real_skip.h',work/'real_skip.h')
header=(ACCESS/'context_access_diag.h').read_text(encoding='utf-8').replace('#include "kernels.h"','#include "kernels.h"\n#include "context_shadow_replay.h"')
header=replace(header,'(x)|=(v); ax_access("write",&(x),sizeof(x),#x,__func__);','(x)|=(v); sr_or(&(x),(v),#x,__func__);')
(work/'context_access_diag.h').write_text(header,encoding='utf-8')
(work/'real_skip_core.inc').write_text(core(audit),encoding='utf-8',newline='\n')
cc,sun,_=ex.builder.toolchain();flags,libs,dlls,exe=ex.builder.platform_build_inputs(sun)
flags+=['-DLP_OBSERVE=0',f'-DRR_AUDIT={int(audit)}',f'-DRR_SKIP={int(skip)}'];started=time.perf_counter()
def compile_one(item):
name,code=item;path=work/name
prefix='#ifdef _WIN32\n#ifndef _WIN32_WINNT\n#define _WIN32_WINNT 0x0600\n#endif\n#endif\n#include "real_skip.h"\n#include <stdlib.h>\n'
if name.startswith('record_'):
code=code[len(aliases):];prefix=aliases+prefix
path.write_text(prefix+code,encoding='utf-8',newline='\n');obj=path.with_suffix('.o');log=[]
ex.builder._command([cc,*flags,'-I',str(work),'-I',str(ex.builder.NATIVE/'include'),'-I',str(sun/'include'),'-c',str(path),'-o',str(obj)],log=log,timeout=240)
return obj,log
with ThreadPoolExecutor(max_workers=4) as pool:objects=list(pool.map(compile_one,sources.items()))
log=[];ex.builder._command([cc,*flags,*[str(p) for p,_ in objects],*ex.builder.link_library_arguments(libs),'-lm','-o',str(work/exe)],log=log)
for dll in dlls:shutil.copyfile(dll,work/dll.name)
(work/'build.log').write_text('\n'.join(sum([v for _,v in objects],[])+log),encoding='utf-8')
assert sources['local_probe_support.c']==(BASE/'local_probe_support.c').read_text(encoding='utf-8')
write(work/'build.json',dict(audit=audit,skip=skip,seconds=time.perf_counter()-started,originalHashes=original_hashes,guardUnchanged=True,guardMatchesShadow=True,sourceHashes={p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in work.glob('*.c')},coreSha256=hashlib.sha256((work/'real_skip_core.inc').read_bytes()).hexdigest()))
print('BUILT',label,flush=True)
def run(audit,skip,label=None,force=0):
worker=('audit' if audit else 'perf')+('-skip' if skip else '-control')
label=label or worker+'-run';folder=OUT/label;folder.mkdir(exist_ok=True)
env=os.environ.copy();env['LOCAL_PROBE_MASK']='0x7ffffff'
if audit and skip:env['RR_EXPECTED']=str(OUT/'audit-control-run/audit.bin')
if force:env['RR_FORCE_REJECT_EVERY']=str(force)
args=[str(OUT/worker/'model.exe'),'--method','BDF','--start','0','--stop','10','--sample-step','.01','--max-step','1e30','--rtol','1e-8','--timeout','300','--sample-file',str(folder/'states.bin'),'--output-block-file',str(folder/'outputs.bin'),'--output',str(folder/'result.json')]
start=time.perf_counter()
with (folder/'stderr.log').open('wb') as f:p=subprocess.run(args,cwd=folder,env=env,stdout=subprocess.PIPE,stderr=f,timeout=330,creationflags=subprocess.CREATE_NO_WINDOW)
assert p.returncode==0,(label,p.returncode,(folder/'stderr.log').read_text()[-4000:])
r=json.loads((folder/'result.json').read_text());d=json.loads((folder/'probe.json').read_text());c=json.loads((folder/'real-skip.json').read_text())
ref=json.loads((BASE.parent/'all-run-0/measurement.json').read_text(encoding='utf-8'))
keys=['success','finalState','final','propertyWarnings','acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu']
assert all(r[k]==ref[k] for k in keys),[(k,r[k],ref[k]) for k in keys if r[k]!=ref[k]]
for key in ['newtonIterations','newtonConvergenceFailures','modelCalls','groups','contextCopiedBytes','contextComparedBytes']:assert d[key]==ref['diagnostic'][key],key
hashes={}
for name in ['states','outputs','events']+(['jacobians'] if audit else []):
with (folder/(name+'.bin')).open('rb') as f:hashes[name]=hashlib.file_digest(f,'sha256').hexdigest()
if name=='jacobians':
with (BASE.parent/'all-audit/jacobians.bin').open('rb') as f:assert hashes[name]==hashlib.file_digest(f,'sha256').hexdigest()
else:assert hashes[name]==ref[name+'Sha256'],name
expected_rejects=(896+force-1)//force if force else 0
assert c['targetVisits']==896 and c['referenceExecutions']==0 and c['mismatches']==0
assert c['candidateAttempts']==(896 if skip else 0)
assert c['realSkips']==(896-expected_rejects if skip else 0)
assert c['nativeOriginalExecutions']==(expected_rejects if skip else 896)
assert c['commits']==c['semanticSuccess']==c['realSkips']
assert c['fallbackExecutions']==c['rejects']==expected_rejects
if audit:assert c['evaluationAudits']==896*28 and c['targetEntryAudits']==c['targetExitAudits']==896
record=dict(label=label,audit=audit,skip=skip,force=force,exact=True,solveSeconds=r['solveSeconds'],solveCpuSeconds=r['solveCpuSeconds'],jacobianSeconds=d['jacobianSeconds'],processSeconds=time.perf_counter()-start,counters=c,hashes=hashes,solver={k:r[k] for k in ['acceptedSteps','rejectedSteps','nfev','njev','nlu','solverStarts','stateTransitions']},newtonIterations=d['newtonIterations'],newtonConvergenceFailures=d['newtonConvergenceFailures'])
write(folder/'validation.json',record)
print('RUN',label,'exact OK','skip',c['realSkips'],'original',c['nativeOriginalExecutions'],'target us',c['pathSeconds']/896*1e6,'Jac',d['jacobianSeconds'],flush=True)
return record
def benchmark(pairs):
run(False,False,'warm-control');run(False,True,'warm-skip')
results=[]
for i in range(pairs):
for skip in ([False,True] if i%2==0 else [True,False]):results.append(run(False,skip,f'pair-{i+1}-'+('skip' if skip else 'control')))
write(OUT/'performance.json',results)
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('action',choices=['prepare','run','benchmark']);p.add_argument('--audit',action='store_true');p.add_argument('--skip',action='store_true');p.add_argument('--label');p.add_argument('--force',type=int,default=0);p.add_argument('--pairs',type=int,default=7);a=p.parse_args()
if a.action=='prepare':prepare(a.audit,a.skip)
elif a.action=='run':run(a.audit,a.skip,a.label,a.force)
else:benchmark(a.pairs)
+223
View File
@@ -0,0 +1,223 @@
/* Standalone R288/position16 experiment. Never linked into production. */
#include "real_skip.h"
#include <windows.h>
#include "real_skip_core.inc"
int rr_original_scope,rr_replaying;
unsigned long long rr_native_calls;
static unsigned long long visits,attempts,successes,rejects,commits,fallbacks,reject_reason[NREASONS];
static unsigned long long eval_audits,entry_audits,exit_audits,rollback_checks;
static unsigned long long appends_total,relocations,count_different,paths[4];
static uint64_t frequency,path_ticks,original_ticks,validation_ticks,overlay_ticks,commit_ticks,fallback_ticks,baseline_ticks,baseline_start;
static size_t metadata_min=(size_t)-1,metadata_max;
static int force_every;
#if RR_AUDIT
/* Pointer identities are compared as owner bindings, never as cross-process addresses.
* Padding is excluded; every declared numerical field is copied byte-for-byte. */
typedef struct {
unsigned long long jacobian;
int color,stage,status,saved_errno,flags,rounding;
unsigned sse;
double t,output,y[NSTATES],dy[NSTATES],w[NOUTPUTS];
size_t count,capacity,memo_capacity;
int recording;
unsigned long evaluations[NATIVE_JACOBIAN_SCALAR_KINDS],reuses[NATIVE_JACOBIAN_SCALAR_KINDS];
NativePropertyState states[SR_STATES];
NativePipeCache pipes[SR_PIPES];
NativeJacobianGasMemo gases[MODEL_JACOBIAN_GAS_COUNT];
NativeJacobianGasStats gas_stats;
} Audit;
static FILE *audit_file,*matrix_file;
static Audit actual,expected;
static NativeJacobianScalarEntry baseline_entries[MODEL_JACOBIAN_SCALAR_COUNT],expected_entries[MODEL_JACOBIAN_SCALAR_COUNT];
static NativeJacobianScalars *memo_owner;
static SRContext before_reject,after_reject;
static void audit_fail(const char *where,const void *a,const void *b,size_t n){
size_t offset=0;while(offset<n && ((const unsigned char*)a)[offset]==((const unsigned char*)b)[offset])offset++;
FILE *f=fopen("first-mismatch.txt","wb");
if(f){fprintf(f,"Jacobian=%llu group=%d probe=%d position=%d stage=%d field=%s byte=%llu\n",jac,lp_color,lp_color,actual.stage==3?-1:16,actual.stage,where,(unsigned long long)offset);fclose(f);}
f=fopen("first-mismatch-context.bin","wb");
if(f){fwrite(a,1,n,f);fwrite(b,1,n,f);fwrite(&plan,1,sizeof(plan),f);fclose(f);}
fprintf(stderr,"Jacobian=%llu group=%d position=%d stage=%d %s byte=%llu\n",jac,lp_color,actual.stage==3?-1:16,actual.stage,where,(unsigned long long)offset);
fatal("bitwise mismatch");
}
static const char *audit_field(size_t off){
static char name[160];
#define AF(n) if(off>=offsetof(Audit,n)&&off<offsetof(Audit,n)+sizeof(actual.n)){snprintf(name,sizeof(name),#n "[%llu]",(unsigned long long)((off-offsetof(Audit,n))/8));return name;}
/* Explicit field decoding for the large context arrays. */
if(off>=offsetof(Audit,states)&&off<offsetof(Audit,pipes)){
size_t d=off-offsetof(Audit,states),slot=d/sizeof(NativePropertyState),local=d%sizeof(NativePropertyState);
for(size_t i=0;i<sizeof(state_fields)/sizeof(*state_fields);i++)if(local>=state_fields[i].offset && local<state_fields[i].offset+state_fields[i].size){snprintf(name,sizeof(name),"property[%llu].%s",(unsigned long long)slot,state_fields[i].name);return name;}
return "property_binding";
}
if(off>=offsetof(Audit,pipes)&&off<offsetof(Audit,gases)){
size_t d=off-offsetof(Audit,pipes),slot=d/sizeof(NativePipeCache),local=d%sizeof(NativePipeCache);
for(size_t i=0;i<sizeof(pipe_fields)/sizeof(*pipe_fields);i++)if(local>=pipe_fields[i].offset && local<pipe_fields[i].offset+pipe_fields[i].size){snprintf(name,sizeof(name),"pipe[%llu].%s",(unsigned long long)slot,pipe_fields[i].name);return name;}
}
AF(y) AF(dy) AF(w) AF(t) AF(output) AF(count) AF(capacity) AF(recording) AF(evaluations) AF(reuses) AF(gases) AF(gas_stats)
#undef AF
return "audit_header_or_memo_lifecycle";
}
static void audit_context(int stage,double t,const double *y,const double *dy,const double *w,int status,double output,NativePropertyCache *p,NativePipeCache *pipes,ModelJacobianWorkspace *workspace){
int saved_errno=errno;SREnvironment env;save_environment(&env);
memset(&actual,0,sizeof(actual));actual.jacobian=jac;actual.color=lp_color;actual.stage=stage;actual.status=status;
actual.saved_errno=saved_errno;actual.flags=fetestexcept(FE_ALL_EXCEPT);actual.rounding=env.rounding;actual.sse=env.sse;
actual.t=t;actual.output=output;
if(y)memcpy(actual.y,y,NSTATES*8);
if(dy)memcpy(actual.dy,dy,NSTATES*8);
if(w)memcpy(actual.w,w,NOUTPUTS*8);
if(p->count>SR_STATES || p->capacity!=SR_STATES || p->temperatures || !p->jacobian || p->jacobian->capacity!=MODEL_JACOBIAN_SCALAR_COUNT)fatal("audit context schema");
NativeJacobianScalars *m=p->jacobian;
if(stage==3 && lp_color<0)memo_owner=m;
else if(m!=memo_owner)fatal("memo owner lifetime");
if(m->recording!=(lp_color<0))fatal("memo recording lifetime");
if(workspace && (m!=&workspace->scalars || m->entries!=workspace->scalar_entries))fatal("workspace memo binding");
actual.count=p->count;actual.capacity=p->capacity;actual.memo_capacity=m->capacity;actual.recording=m->recording;
memcpy(actual.evaluations,m->evaluations,sizeof(actual.evaluations));memcpy(actual.reuses,m->reuses,sizeof(actual.reuses));
for(size_t s=0;s<p->count;s++){
if(p->states[s].jacobian!=m || p->states[s].temperatures)fatal("entry memo/observer binding");
for(size_t k=0;k<sizeof(state_fields)/sizeof(*state_fields);k++)memcpy((char*)&actual.states[s]+state_fields[k].offset,(char*)&p->states[s]+state_fields[k].offset,state_fields[k].size);
}
for(int s=0;s<SR_PIPES;s++)for(size_t k=0;k<sizeof(pipe_fields)/sizeof(*pipe_fields);k++)memcpy((char*)&actual.pipes[s]+pipe_fields[k].offset,(char*)&pipes[s]+pipe_fields[k].offset,pipe_fields[k].size);
if(workspace){
actual.gas_stats=workspace->stats;
for(int i=0;i<MODEL_JACOBIAN_GAS_COUNT;i++){
NativeJacobianGasMemo *src=&workspace->gases[i],*dst=&actual.gases[i];
uintptr_t id=src->kernel==NULL?0:src->kernel==native_medium_gas_context?1:src->kernel==native_polytropic_gas_context?2:999;
if(id==999)fatal("unknown gas memo kernel");
memcpy(&dst->kernel,&id,sizeof(id));memcpy(dst->inputs,src->inputs,sizeof(dst->inputs));dst->value=src->value;dst->medium_kind=src->medium_kind;dst->valid=src->valid;
}
}
if(stage==3 && lp_color<0){
memcpy(baseline_entries,m->entries,sizeof(baseline_entries));
if(RR_SKIP){if(fread(expected_entries,1,sizeof(expected_entries),audit_file)!=sizeof(expected_entries))fatal("memo audit EOF");if(memcmp(expected_entries,baseline_entries,sizeof(baseline_entries)))audit_fail("memo_entries",expected_entries,baseline_entries,sizeof(baseline_entries));}
else if(fwrite(baseline_entries,1,sizeof(baseline_entries),audit_file)!=sizeof(baseline_entries))fatal("memo audit write");
}else if(memcmp(baseline_entries,m->entries,sizeof(baseline_entries)))audit_fail("memo_readonly",baseline_entries,m->entries,sizeof(baseline_entries));
if(RR_SKIP){
if(fread(&expected,1,sizeof(expected),audit_file)!=sizeof(expected))fatal("audit EOF");
if(memcmp(&expected,&actual,sizeof(actual))){size_t i=0;while(((unsigned char*)&expected)[i]==((unsigned char*)&actual)[i])i++;audit_fail(audit_field(i),&expected,&actual,sizeof(actual));}
}else if(fwrite(&actual,1,sizeof(actual),audit_file)!=sizeof(actual))fatal("audit write");
restore_environment(&env);errno=saved_errno;
}
void rr_eval_exit(double t,const double *y,const double *dy,const double *w,int result,NativePropertyCache *p,NativePipeCache *pipes,ModelJacobianWorkspace *workspace){
audit_context(3,t,y,dy,w,result,0,p,pipes,workspace);eval_audits++;
}
void rr_matrix(double t,const double *y,const double *matrix){
int e=errno;SREnvironment env;save_environment(&env);
if(fwrite(&t,8,1,matrix_file)!=1 || fwrite(y,8,NSTATES,matrix_file)!=NSTATES || fwrite(matrix,8,NSTATES*NSTATES,matrix_file)!=NSTATES*NSTATES)fatal("matrix write");
restore_environment(&env);errno=e;
}
#endif
void rr_start(void){
LARGE_INTEGER f;QueryPerformanceFrequency(&f);frequency=(uint64_t)f.QuadPart;
#if RR_AUDIT
audit_file=fopen(RR_SKIP?getenv("RR_EXPECTED"):"audit.bin",RR_SKIP?"rb":"wb");matrix_file=fopen("jacobians.bin","wb");
if(!audit_file || !matrix_file)fatal("open audit files");
setvbuf(audit_file,NULL,_IOFBF,1024*1024);setvbuf(matrix_file,NULL,_IOFBF,1024*1024);
const char *s=getenv("RR_FORCE_REJECT_EVERY");force_every=s?atoi(s):0;
#endif
}
void rr_jacobian(void){jac++;plan.ready=0;}
void rr_record_begin(NativePropertyCache *p,NativePipeCache *pipes,const double *x){
baseline_start=lp_tick();
if(RR_SKIP){ax_bind(p,pipes);ax_begin(jac,-1,16,0,x,4);}
}
void rr_record_end(double output){if(RR_SKIP)ax_end(&output,1);baseline_ticks+=lp_tick()-baseline_start;}
/* The translated write set is prepared before touching live storage. */
typedef struct {void *destination;const void *source;size_t size;int kind;unsigned mask;} Patch;
static Patch patches[SR_EVENTS];
static NativeJacobianScalars *patch_owner;
static NativePropertyTemperatures *patch_observer;
static int build_patch(NativePropertyCache *p,NativePipeCache *pipes,const int *map){
int n=0;patch_owner=p->jacobian;patch_observer=NULL;
for(int i=0;i<plan.n;i++){
const Event *e=&plan.events[i];Patch item={0};
if(e->type==ALLOCATE){item.destination=&p->count;item.kind=1;}
else if(e->type==SCALAR){item.destination=&p->jacobian->reuses[e->slot];item.kind=2;}
else if(e->type==WRITE_PIPE){item.destination=(char*)&pipes[e->slot]+e->offset;item.source=e->data;item.size=e->size;}
else if(e->type==WRITE_STATE || e->type==WRITE_OR){
item.destination=(char*)&p->states[map[e->slot]]+e->offset;item.source=e->data;item.size=e->size;
if(e->type==WRITE_OR){item.kind=3;item.mask=e->mask;}
else if(e->offset==(int)offsetof(NativePropertyState,jacobian))item.source=&patch_owner;
else if(e->offset==(int)offsetof(NativePropertyState,temperatures))item.source=&patch_observer;
}else continue;
patches[n++]=item;
}
return n;
}
static void commit(int n){
for(int i=0;i<n;i++){
Patch *p=&patches[i];
if(p->kind==1)(*(size_t*)p->destination)++;
else if(p->kind==2)(*(unsigned long*)p->destination)++;
else if(p->kind==3)(*(unsigned*)p->destination)|=p->mask;
else memcpy(p->destination,p->source,p->size);
}
}
double rr_execute(NativePropertyCache *p,NativePipeCache *pipes,const double *x,SROperation original){
visits++;
#if RR_AUDIT
audit_context(1,0,NULL,NULL,NULL,1,0,p,pipes,NULL);entry_audits++;
#endif
uint64_t start=lp_tick(),tick;double output;
if(RR_SKIP){
attempts++;bound=p;bound_pipes=pipes;position=16;probe_entry_count=p->count;rr_replaying=1;
int reason=OK,mapping[SR_STATES],appends=0;
count_different+=(p->count!=plan.entry_count);
tick=lp_tick();
if(p->count>SR_STATES || p->capacity>SR_STATES || p->count>p->capacity)reason=CAPACITY;
else if(!p->jacobian || !p->jacobian->entries || p->jacobian->capacity>MODEL_JACOBIAN_SCALAR_COUNT)reason=MEMO_BINDING;
else from_live(&overlay,p,pipes);
overlay_ticks+=lp_tick()-tick;
#if RR_AUDIT
from_live(&before_reject,p,pipes);
#endif
tick=lp_tick();phase=CANDIDATE;
double saved_output=plan.output;
if(force_every && (attempts-1)%(unsigned)force_every==0)plan.output=NAN;
if(!reason)reason=replay_overlay(&overlay,x,mapping,&appends);
plan.output=saved_output;phase=OFF;validation_ticks+=lp_tick()-tick;
if(!reason){
tick=lp_tick();int n=build_patch(p,pipes,mapping);overlay_ticks+=lp_tick()-tick;
tick=lp_tick();commit(n);output=plan.output;commit_ticks+=lp_tick()-tick;
commits++;successes++;appends_total+=appends;paths[plan.path]++;
int relocated=0;for(int i=0;i<SR_STATES;i++)if(mapping[i]>=0 && mapping[i]!=i)relocated=1;
relocations+=relocated;
size_t bytes=sizeof(plan)-sizeof(plan.events)+(size_t)plan.n*sizeof(Event);
if(bytes<metadata_min)metadata_min=bytes;
if(bytes>metadata_max)metadata_max=bytes;
}else{
rejects++;reject_reason[reason]++;rr_replaying=0;
#if RR_AUDIT
from_live(&after_reject,p,pipes);
if(compare_contexts(&before_reject,&after_reject))fatal("reject altered live context");
rollback_checks++;
#endif
tick=lp_tick();rr_original_scope=1;output=original(p,pipes,x);rr_original_scope=0;fallback_ticks+=lp_tick()-tick;fallbacks++;
}
rr_replaying=0;
}else{
tick=lp_tick();rr_original_scope=1;output=original(p,pipes,x);rr_original_scope=0;original_ticks+=lp_tick()-tick;
}
path_ticks+=lp_tick()-start;
#if RR_AUDIT
audit_context(2,0,NULL,NULL,NULL,1,output,p,pipes,NULL);exit_audits++;
#endif
return output;
}
void rr_finish(void){
#if RR_AUDIT
if(RR_SKIP && fgetc(audit_file)!=EOF)fatal("unconsumed audit records");
fclose(audit_file);fclose(matrix_file);
#endif
FILE *f=fopen("real-skip.json","wb");if(!f)fatal("summary");
fprintf(f,"{\"targetVisits\":%llu,\"candidateAttempts\":%llu,\"semanticSuccess\":%llu,\"realSkips\":%llu,\"rejects\":%llu,\"fallbackExecutions\":%llu,\"nativeOriginalExecutions\":%llu,\"commits\":%llu,\"referenceExecutions\":0,\"mismatches\":0,\"evaluationAudits\":%llu,\"targetEntryAudits\":%llu,\"targetExitAudits\":%llu,\"rollbackChecks\":%llu,",visits,attempts,successes,successes,rejects,fallbacks,rr_native_calls,commits,eval_audits,entry_audits,exit_audits,rollback_checks);
fprintf(f,"\"pathSeconds\":%.12g,\"originalSeconds\":%.12g,\"validationSeconds\":%.12g,\"overlayPatchSeconds\":%.12g,\"commitSeconds\":%.12g,\"fallbackSeconds\":%.12g,\"baselineRecordSeconds\":%.12g,",(double)path_ticks/frequency,(double)original_ticks/frequency,(double)validation_ticks/frequency,(double)overlay_ticks/frequency,(double)commit_ticks/frequency,(double)fallback_ticks/frequency,(double)baseline_ticks/frequency);
fprintf(f,"\"countDifferent\":%llu,\"slotRelocationTrials\":%llu,\"appends\":%llu,\"paths\":[%llu,%llu,%llu,%llu],\"metadataMin\":%llu,\"metadataMax\":%llu,\"metadataReserved\":%llu,\"overlayBytes\":%llu,\"patchReserved\":%llu,\"rejectReasons\":{",count_different,relocations,appends_total,paths[0],paths[1],paths[2],paths[3],(unsigned long long)(metadata_min==(size_t)-1?0:metadata_min),(unsigned long long)metadata_max,(unsigned long long)sizeof(plan),(unsigned long long)sizeof(overlay),(unsigned long long)sizeof(patches));
for(int i=1;i<NREASONS;i++)fprintf(f,"%s\"%s\":%llu",i>1?",":"",reasons[i],reject_reason[i]);
fputs("}}\n",f);fclose(f);
}
@@ -0,0 +1,260 @@
"""R288-only typed replay cost-floor experiment; no production source changes."""
from pathlib import Path
import argparse,hashlib,json,os,re,shutil,subprocess,statistics,struct
import real_skip_experiment as base
ROOT=base.ROOT;HERE=Path(__file__).parent;OUT=ROOT/'test/r288-typed-replay-20260917'
OLD=base.OUT
original_core=base.core
def replace(s,a,b):return base.replace(s,a,b)
def body(s,name,change):return base.shadow.access.change_function(s,name,change)
def function_replace(s,name,new):
a,b,e=base.ex.function_span(s,name);return s[:a]+new+s[e:]
def profile_core():
s=original_core(False)
pro='''
static uint64_t prof_ticks[24],prof_calls[24];
typedef struct {uint64_t start;int category;} Prof;
static void prof_end(Prof *s){prof_ticks[s->category]+=lp_tick()-s->start;prof_calls[s->category]++;}
#define PROF(c) Prof ps __attribute__((cleanup(prof_end)))={lp_tick(),c}
'''
s=s.replace('static Plan plan;',pro+'\nstatic Plan plan;')
for name,index in [('first_match',10),('map_slot',11),('scalar_lookup',12),('state_read_equal',13),('from_live',14)]:s=body(s,name,lambda b,i=index:'PROF('+str(i)+');'+b)
s=s.replace('const Event *e=&plan.events[i];int slot=-1;','const Event *e=&plan.events[i];PROF(e->type);int slot=-1;')
s=body(s,'replay_overlay',lambda b:replace('uint64_t header_start=lp_tick();'+b,'for(int i=0;i<SR_STATES;i++)mapping[i]=-1;','prof_ticks[17]+=lp_tick()-header_start;prof_calls[17]++;\nfor(int i=0;i<SR_STATES;i++)mapping[i]=-1;'))
def split_copy(b):
portions=[
(18,'memset(dst,0,sizeof(*dst));memset(dst->states,0xa5,sizeof(dst->states));'),
(19,'dst->context=*src;memcpy(dst->states,src->states,src->count*sizeof(*src->states));'),
(20,'memcpy(dst->pipes,pipes,sizeof(dst->pipes));'),
(21,'dst->memo=*src->jacobian;memcpy(dst->entries,src->jacobian->entries,src->jacobian->capacity*sizeof(*dst->entries));')]
for tag,text in portions:b=replace(b,text,'{PROF('+str(tag)+');'+text+'}')
start=b.index('dst->memo.entries=');b=b[:start]+'{PROF(22);'+b[start:]+'}'
return b
s=body(s,'from_live',split_copy)
return s
def profile_runtime():
s=(HERE/'real_skip_runtime.c').read_text()
s=s.replace('static int force_every;','static int force_every;\nstatic FILE *plans_file;\nstatic uint64_t timer_ticks,dispatch_ticks;static volatile unsigned dispatch_sink;')
s=body(s,'rr_start',lambda b:b+'\nplans_file=fopen("plans.bin","wb");if(!plans_file)fatal("plans");\nfor(int i=0;i<100000;i++){uint64_t t=lp_tick();timer_ticks+=lp_tick()-t;}\n')
s=body(s,'rr_record_end',lambda b:b+'''
int saved=errno;SREnvironment env;save_environment(&env);
fwrite(&plan,1,sizeof(plan),plans_file);
uint64_t dispatch_start=lp_tick();unsigned checksum=0;
for(int repeat=0;repeat<100;repeat++)for(int i=0;i<plan.n;i++){
const Event *e=&plan.events[i];switch(e->type){
case QUERY:checksum+=(unsigned)e->slot;break;case SCALAR:checksum+=(unsigned)e->size;break;
case ALLOCATE:checksum++;break;case WRITE_STATE:case WRITE_PIPE:checksum+=(unsigned)e->offset;break;
case READ_STATE:case READ_PIPE:checksum+=(unsigned)e->size;break;case WRITE_OR:case VALID:checksum+=e->mask;break;default:checksum++;
}}
dispatch_ticks+=lp_tick()-dispatch_start;dispatch_sink=checksum;
restore_environment(&env);errno=saved;
''')
s=body(s,'build_patch',lambda b:'PROF(15);'+b)
s=body(s,'commit',lambda b:'PROF(16);'+b)
s=body(s,'rr_finish',lambda b:b+'''
fclose(plans_file);FILE *pf=fopen("attribution.json","wb");
fprintf(pf,"{\\"frequency\\":%llu,\\"emptyTimerTicks\\":%llu,\\"emptyTimerCalls\\":100000,\\"dispatchTicks\\":%llu,\\"dispatchRepeats\\":100,\\"categories\\":[",(unsigned long long)frequency,(unsigned long long)timer_ticks,(unsigned long long)dispatch_ticks);
for(int i=0;i<24;i++)fprintf(pf,"%s{\\"id\\":%d,\\"ticks\\":%llu,\\"calls\\":%llu}",i?",":"",i,(unsigned long long)prof_ticks[i],(unsigned long long)prof_calls[i]);
fputs("]}",pf);fclose(pf);
''')
return s
def minimal_sources(audit):
# The audit build adds the minimal capture hooks to the certified access
# source, allowing one physical baseline execution to feed BOTH schemas.
directory=base.ACCESS if audit else base.BASE
codes={n:(directory/n).read_text(encoding='utf-8') for n in ['properties.c','pipe.c']}
s=codes['properties.c']
if audit:
# Native access hooks feed the compact recorder through ax_* wrappers.
return codes
s=body(s,'property_pt',lambda b:replace(replace(b,'return s;','{fp_query(cache,m,p,T,s);return s;}'),'return property_new(cache,m,p,T,scratch);','fp_query(cache,m,p,T,NULL);return property_new(cache,m,p,T,scratch);'))
s=body(s,'property_new',lambda b:replace(b,'return s;','fp_allocate(cache,s,valid);return s;'))
s=body(s,'native_temperature_ph_context',lambda b:'fp_unknown();'+b)
s=body(s,'native_jacobian_scalar_get',lambda b:replace('fp_scalar_key(kind,medium_kind,inputs,count);'+b,'return 1;','fp_scalar_value(kind,medium_kind,inputs,count,*value);return 1;'))
s=body(s,'native_jacobian_scalar_put',lambda b:'fp_scalar_value(kind,medium_kind,inputs,count,value);'+b)
codes['properties.c']=s
return codes
def fast_core(audit):
if audit:
s=original_core(True)
s=s.replace('static SRContext overlay;','')
s=body(s,'ax_query',lambda b:b+'\nif(phase==RECORD && strcmp(kind,"PT"))fp_unknown();')
s=body(s,'ax_match',lambda b:b+'''
if(phase==RECORD && !strcmp(kind,"PT")){
Event *q=&plan.events[plan.query_index];double key[9];memcpy(key,q->data,sizeof(key));
NativeMedium m={q->offset,key[2],key[3],key[4],key[5],key[6],key[7],key[8]};fp_query(bound,&m,key[0],key[1],s);
}
''')
s=body(s,'ax_new',lambda b:'if(phase==RECORD)fp_allocate(cache,s,valid);'+b)
s=body(s,'ax_scalar',lambda b:b+'''
if(phase==RECORD){
if(!strcmp(action,"get")){fp_scalar_key(kind,medium,keys,n);if(hit && value)fp_scalar_value(kind,medium,keys,n,*value);}
else if(!strcmp(action,"put_attempt") && value)fp_scalar_value(kind,medium,keys,n,*value);
}
''')
return s
s=(HERE/'context_shadow_replay.c').read_text()
s=s[:s.index('typedef struct {\n int type,slot')]
s=s.replace('static void save_environment','static __attribute__((unused)) void save_environment').replace('static void restore_environment','static __attribute__((unused)) void restore_environment')
s+='''
static unsigned long long jac;
static NativePropertyCache *bound;
static NativePipeCache *bound_pipes;
static void fatal(const char *s){fprintf(stderr,"typed replay fatal: %s\\n",s);abort();}
'''
return s
def fast_runtime(audit,direct=False):
s=(HERE/'real_skip_runtime.c').read_text()
s=s.replace('static int force_every;','static __attribute__((unused)) int force_every;\n#include "fast_replay.inc"')
start=s.index('/* The translated write set');end=s.index('double rr_execute(',start)
s=s[:start]+s[end:]
execute=(HERE/'typed_replay_runtime.inc').read_text()
s=function_replace(s,'rr_execute',execute)
if audit:s=s.replace('void rr_start(void){','#include "typed_contract.inc"\n#include "typed_oracle.inc"\nvoid rr_start(void){')
s=function_replace(s,'rr_jacobian','void rr_jacobian(void){jac++;fast.ready=0;\n#if RR_AUDIT\nplan.ready=0;\n#endif\n}')
s=function_replace(s,'rr_record_begin','''void rr_record_begin(NativePropertyCache *p,NativePipeCache *pipes,const double *x){
baseline_start=lp_tick();bound=p;bound_pipes=pipes;fp_begin(p,pipes,x);
#if RR_AUDIT
ax_bind(p,pipes);ax_begin(jac,-1,16,0,x,4);
#endif
}''')
s=function_replace(s,'rr_record_end','''void rr_record_end(double output){
fp_end(output);
#if RR_AUDIT
ax_end(&output,1);fp_contract_check();
#endif
baseline_ticks+=lp_tick()-baseline_start;
}''')
if direct:
s=s.replace('static __attribute__((unused)) int force_every;','static FILE *archived_plans;\nstatic __attribute__((unused)) int force_every;')
s=body(s,'rr_start',lambda b:b+'\narchived_plans=fopen(getenv("RR_PLAN_ORACLE"),"rb");if(!archived_plans)fatal("archived plan oracle");\n')
s=s.replace('ax_end(&output,1);fp_contract_check();','''
int saved_errno=errno;SREnvironment saved_env;save_environment(&saved_env);
if(fread(&plan,1,sizeof(plan),archived_plans)!=sizeof(plan))fatal("archived plan EOF");
uintptr_t previous_owner=plan.memo_binding;
for(int i=0;i<plan.n;i++){
Event *e=&plan.events[i];
if((e->type==READ_STATE || e->type==WRITE_STATE) && e->offset==(int)offsetof(NativePropertyState,jacobian)){
uintptr_t pointer;memcpy(&pointer,e->data,sizeof(pointer));if(pointer!=previous_owner)fatal("archived memo binding");memcpy(e->data,&fast.owner,sizeof(fast.owner));
}}
plan.memo_binding=(uintptr_t)fast.owner;phase=OFF;
restore_environment(&saved_env);errno=saved_errno;fp_contract_check();
''')
s=body(s,'rr_finish',lambda b:b+'\nif(fgetc(archived_plans)!=EOF){fatal("remaining archived plans");}\nfclose(archived_plans);\n')
s=s.replace('(unsigned long long)sizeof(plan),(unsigned long long)sizeof(overlay),(unsigned long long)sizeof(patches)', '(unsigned long long)sizeof(fast),(unsigned long long)sizeof(Pending),(unsigned long long)sizeof(Pending)')
s=body(s,'rr_finish',lambda b:b+'''
FILE *fextra=fopen("typed-summary.json","wb");
fprintf(fextra,"{\\"metadataBytes\\":%llu,\\"captureScratchBytes\\":%llu,\\"pendingBytes\\":%llu,\\"contractChecks\\":%llu,\\"oracleChecks\\":%llu,\\"negativeChecks\\":%llu}",(unsigned long long)sizeof(fast),(unsigned long long)sizeof(capture),(unsigned long long)sizeof(Pending),
#if RR_AUDIT
contract_checks,oracle_checks,negative_checks
#else
0ULL,0ULL,0ULL
#endif
);fclose(fextra);
''')
return s
def contract_source():
data=(OUT/'P/perf-skip-run/plans.bin').read_bytes();schemas={}
for off in range(0,len(data),90224):
count=struct.unpack_from('<Q',data,off+90112+56)[0];n=struct.unpack_from('<i',data,off+90112+68)[0];path=struct.unpack_from('<i',data,off+90112+88)[0]
entries=[]
for i in range(n):
t,s,o,z,a,m=struct.unpack_from('<iiiiiI',data,off+i*176)
if t in (1,2,3,7,8):s-=count
entries.append((t,s,o,z,a,m))
if path in schemas:assert schemas[path]==entries
else:schemas[path]=entries
assert sorted(schemas)==[0,1] and len(schemas[0])==12 and len(schemas[1])==132
prefix='typedef struct {int type,slot,offset,size,aux;unsigned mask;} Contract;\n'
for path,events in schemas.items():prefix+='static const Contract schema'+str(path)+'[]={'+','.join('{'+','.join(map(str,e))+'}' for e in events)+'};\n'
return prefix+(HERE/'typed_contract_check.inc').read_text()
def prepare(mode,audit=False):
certified=json.loads((OLD/'perf-skip/build.json').read_text())['originalHashes']
for name,expected in certified.items():assert hashlib.sha256((base.BASE/name).read_text(encoding='utf-8').encode()).hexdigest()==expected,name
root=OUT/mode;root.mkdir(parents=True,exist_ok=True)
staging=root/'source';staging.mkdir(exist_ok=True)
for name in ['context_shadow_replay.c','context_shadow_replay.h','real_skip.h']:shutil.copyfile(HERE/name,staging/name)
runtime=fast_runtime(audit,mode=='D') if mode in 'CD' else profile_runtime() if mode=='P' else (HERE/'real_skip_runtime.c').read_text()
(staging/'real_skip_runtime.c').write_text(runtime,encoding='utf-8')
if mode in 'CD':
header=(staging/'real_skip.h').read_text();header+='\n'+(HERE/'typed_replay_hooks.h').read_text();(staging/'real_skip.h').write_text(header)
work=root/('audit-skip' if audit else 'perf-skip');work.mkdir(exist_ok=True)
shutil.copyfile(HERE/'typed_replay_core.inc',work/'fast_replay.inc')
if audit:
(work/'typed_contract.inc').write_text(contract_source(),encoding='utf-8')
shutil.copyfile(HERE/'typed_oracle.inc',work/'typed_oracle.inc')
access=staging/'capture';access.mkdir(exist_ok=True)
for n in ['context_access_diag.h','context_fallback_diag.h']:shutil.copyfile(base.ACCESS/n,access/n)
for n,code in minimal_sources(audit and mode!='D').items():(access/n).write_text(code,encoding='utf-8')
base.OUT=root;base.HERE=staging
if mode in 'CD':base.ACCESS=access
base.core=lambda enabled:fast_core(enabled) if mode in 'CD' else profile_core() if mode=='P' else original_core(enabled)
if not audit:
gate=root/'audit-skip-run';gate.mkdir(exist_ok=True)
if mode!='C':shutil.copyfile(OLD/'audit-skip-run/validation.json',gate/'validation.json')
else:assert json.loads((gate/'validation.json').read_text())['exact']
# original_core reads base.HERE; the source copies are byte-identical.
base.prepare(audit,mode!='A')
if mode in 'CD':
manifest=root/('audit-skip' if audit else 'perf-skip')/'build.json';info=json.loads(manifest.read_text())
info['guardMatchesShadow']=False;info['wholeContextGuardUnchanged']=True
info['typedGuardValidation']='See C/D full-trajectory contracts, generic oracle and forced-reject audits.'
base.write(manifest,info)
if mode=='C' and not audit:
cc,_,_=base.ex.builder.toolchain();nm=Path(cc).with_name('nm.exe' if os.name=='nt' else 'nm')
symbols=subprocess.check_output([str(nm),'--defined-only',str(root/'perf-skip/model.exe')],text=True)
assert not re.search(r'(?m)\b(?:plan|overlay|before_reject|after_reject|oracle_generic|oracle_typed|replay_overlay|ax_access)$',symbols)
base.write(root/'perf-skip/stripped-audit-proof.json',dict(noGenericInterpreter=True,noFullOverlay=True,noAuditOracle=True))
def run(mode,audit=False,label=None,force=0):
base.OUT=OUT/mode
if mode=='D':os.environ['RR_PLAN_ORACLE']=str(OUT/'P/perf-skip-run/plans.bin')
if audit:
expected=base.OUT/'audit-control-run';expected.mkdir(exist_ok=True)
target=expected/'audit.bin'
if not target.exists():os.link(OLD/'audit-control-run/audit.bin',target)
result=base.run(audit,mode!='A',label,force)
result['mode']=mode
if mode in 'CD':
extra=json.loads((base.OUT/result['label']/'typed-summary.json').read_text())
assert extra['metadataBytes']==240 and extra['captureScratchBytes']==96 and extra['pendingBytes']==456
assert extra['contractChecks']==extra['oracleChecks']==(896 if audit else 0)
assert extra['negativeChecks']==(20 if audit and not force else 0)
result['typed']=extra
base.write(base.OUT/result['label']/'validation.json',result)
return result
def benchmark(rounds):
for mode in 'CD':
for label in ['audit-skip-run','forced-reject']:assert json.loads((OUT/mode/label/'validation.json').read_text())['exact']
rows=[]
for mode in 'ABC':run(mode,label='warm')
for i in range(rounds):
order='ABC'[i%3:]+'ABC'[:i%3]
for mode in order:rows.append(run(mode,label=f'round-{i+1}'))
base.write(OUT/'performance.json',rows)
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('action',choices=['prepare','run','benchmark']);p.add_argument('--mode',choices=list('ABCPD'),default='C');p.add_argument('--audit',action='store_true');p.add_argument('--label');p.add_argument('--force',type=int,default=0);p.add_argument('--rounds',type=int,default=9);a=p.parse_args()
if a.action=='prepare':prepare(a.mode,a.audit)
elif a.action=='run':run(a.mode,a.audit,a.label,a.force)
else:benchmark(a.rounds)
+237
View File
@@ -0,0 +1,237 @@
# 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性能测试。
@@ -0,0 +1,63 @@
"""Summarize accuracy, contact phases and repeated cost for isolated variants."""
import argparse
import json
from pathlib import Path
import statistics
import numpy as np
def read(path):return json.loads(path.read_bytes())
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('directory',type=Path)
args=parser.parse_args();out=args.directory
runs=read(out/'summary.json');rows=[];branches=[];groups=[]
for profile,variants in runs.items():
baseline=variants['off']
base_force=baseline['groups']['force']['worstAbsolute']['maxAbsoluteError']
base_time=baseline['timing']['medianSeconds']
base_curves=np.load(out/profile/'off/curves.npz')
for mode,r in variants.items():
data=np.load(out/profile/mode/'curves.npz')
contacts=[]
for key in data.files:
if key.startswith('platform|') and key.endswith('.gap'):
expected='amesim|'+key.split('|',1)[1]
mismatch=data['phaseMatched'] & ((data[key]<0)!=(data[expected]<0))
contacts.extend(dict(key=key.split('|',1)[1],time=float(data['time'][i])) for i in np.flatnonzero(mismatch))
equal=all(np.array_equal(data[k],base_curves[k],equal_nan=True)
for k in data.files if k.startswith('platform|'))
worst=r['groups']['force']['worstAbsolute'];timing=r['timing'];native=r['nativeRun']
report=read(out/profile/mode/'comparison.json')
branches.extend(dict(profile=profile,mode=mode,**c) for c in report['curves'] if c['quantity']=='force')
groups.extend(dict(profile=profile,mode=mode,quantity=q,**g) for q,g in r['groups'].items())
counter_keys=('nfev','acceptedSteps','rejectedSteps','solverStarts','stateTransitions','njev','nlu','experimentalEvents')
counters=[{k:t.get(k) for k in counter_keys} for t in timing['runs']]
row=dict(profile=profile,mode=mode,medianSolveSeconds=timing['medianSeconds'],
minimumSolveSeconds=timing['minimumSeconds'],maximumSolveSeconds=timing['maximumSeconds'],
timingRepeats=timing['repeats'],repeatCountersStable=all(c==counters[0] for c in counters),
medianCpuSeconds=statistics.median(t['solveCpuSeconds'] for t in timing['runs']),
solveChangePercent=100*(timing['medianSeconds']/base_time-1),
forceMaxError=worst['maxAbsoluteError'],forceWorstTime=worst['worstTime'],forceWorstKey=worst['key'],
forceErrorChangePercent=100*(worst['maxAbsoluteError']/base_force-1),
forceQuietMax=max(c['quietMaxAbsolute'] for c in report['curves'] if c['quantity']=='force'),
pressureMaxError=r['groups']['pressure']['worstAbsolute']['maxAbsoluteError'],
temperatureMaxError=r['groups']['temperature']['worstAbsolute']['maxAbsoluteError'],
above5PercentCount=r['above5PercentCount'],phaseUnpairedCount=r['phaseUnpairedGridCount'],
contactPhaseMismatches=contacts,gridTrajectoryEqualsOff=equal,
rawPeakContactForce=r['extraEventPointMaxContactForce'],
acceptedSteps=native['acceptedSteps'],rejectedSteps=native['rejectedSteps'],
nfev=native['nfev'],solverStarts=native['solverStarts'],njev=native['njev'],nlu=native['nlu'],
stateTransitions=native['stateTransitions'],
experimentalEvents=native.get('experimentalEvents',{}),jacobianMode=native['jacobianMode'])
rows.append(row)
print(json.dumps(row,ensure_ascii=False))
(out/'effect-summary.json').write_text(json.dumps(rows,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
(out/'branch-force-summary.json').write_text(json.dumps(branches,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
(out/'group-summary.json').write_text(json.dumps(groups,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
if __name__=='__main__':main()
@@ -0,0 +1,71 @@
"""Audit the controlled output-only eight-branch experiment without hiding raw errors."""
import argparse
import json
from pathlib import Path
import numpy as np
def load(path):
return json.loads(path.read_bytes())
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('directory', type=Path)
args = parser.parse_args()
out, result = args.directory, {}
for name in ('full', 'noncyclic'):
before, after = (out / stage / name for stage in ('before', 'after'))
a, b = (load(p/'native/result.json') for p in (before, after))
at, bt = (np.asarray(r['series']['time']) for r in (a, b))
indices = np.searchsorted(bt, at)
assert np.array_equal(bt[indices], at), 'Every old sample must remain at its real timestamp'
differences = []
for key, values in a['series'].items():
old, new = np.asarray(values), np.asarray(b['series'][key])[indices]
if not np.array_equal(old, new):
differences.append(dict(key=key, maxAbsoluteDifference=float(np.max(np.abs(new-old)))))
counters = ('acceptedSteps', 'rejectedSteps', 'nfev', 'njev', 'nlu', 'solverStarts', 'stateTransitions')
counter_pairs = {key: [a[key], b[key]] for key in counters}
paired = np.load(after/'curves.npz')
contact_phase = {}
for key in paired.files:
if key.startswith('platform|') and key.endswith('.gap'):
other = 'amesim|' + key.split('|', 1)[1]
mismatch = paired['phaseMatched'] & ((paired[key]<0) != (paired[other]<0))
contact_phase[key.split('|', 1)[1]] = dict(mismatchCount=int(mismatch.sum()),
times=paired['time'][mismatch].tolist())
old_metrics = load(before/'raw-time-comparison.json')
new_raw = load(after/'raw-time-comparison.json')
aligned = load(after/'comparison.json')
quantities = {}
for quantity in ('signal', 'force', 'pressure', 'temperature', 'mass_flow', 'enthalpy_flow', 'velocity'):
groups = []
for rows in (old_metrics['curves'], new_raw['curves'], aligned['curves']):
rows = [r for r in rows if r['quantity'] == quantity]
worst = max(rows, key=lambda r: r['maxAbsoluteError'])
groups.append(dict(maxAbsoluteError=worst['maxAbsoluteError'], key=worst['key'],
time=worst['worstTime'], above5PercentCount=sum(r['above5PercentCount'] for r in rows)))
quantities[quantity] = dict(beforeRaw=groups[0], afterRaw=groups[1], afterSamePhase=groups[2])
result[name] = dict(finalStateExactlyEqual=a['finalState']==b['finalState'],
solverCountersEqual=all(x==y for x,y in counter_pairs.values()), counters=counter_pairs,
originalSampleCount=len(at), newSampleCount=len(bt), extraSamples=len(bt)-len(at),
allOriginalSamplesPreserved=True, changedOriginalOutputs=differences,
solveSeconds=[a['solveSeconds'], b['solveSeconds']],
processWallSeconds=[load(before/'native-summary.json')['processWallSeconds'],
load(after/'native-summary.json')['processWallSeconds']],
phasePairing=load(after/'phase-pairing.json'), contactPhase=contact_phase,
quantities=quantities,
above5PercentCount=[old_metrics['above5PercentCount'], new_raw['above5PercentCount'], aligned['above5PercentCount']],
above5PercentOutsideEvents=aligned['above5PercentOutsideEvents'],
rawPeakContactForce=[load(before/'comparison.json')['extraEventPointMaxContactForce'], aligned['extraEventPointMaxContactForce']],
sourceVerification={s: load(out/s/'source-verification.json') for s in ('before','after')})
(out/'effect-summary.json').write_text(json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False)+'\n', encoding='utf-8')
for name, row in result.items():
print(name, json.dumps({k:v for k,v in row.items() if k not in ('phasePairing','quantities','sourceVerification')}, ensure_ascii=False))
print(json.dumps(row['quantities'], ensure_ascii=False))
if __name__ == '__main__':
main()
+57
View File
@@ -0,0 +1,57 @@
/* Audit only: every observed primitive access is accounted for. Values read
* from newly created entries are proved against the ordered writes, rather
* than dropping the consumed-field or valid-test conditions in production. */
static void contract_failure(int event_index,const char *name){
fprintf(stderr,"contract Jacobian=%llu event=%d %s\n",jac,event_index,name);fatal("typed effect schema");
}
static void fp_contract_check(void){
if(plan.error || fast.error || plan.path!=fast.path || plan.entry_count!=fast.entry_count || plan.jac!=fast.jacobian || plan.memo_binding!=(uintptr_t)fast.owner || memcmp(plan.inputs,fast.inputs,32) || memcmp(&plan.output,&fast.output,8) || plan.rounding!=fast.rounding || plan.required_flags!=fast.flags || plan.required_sse_flags!=fast.sse_flags || plan.sse_mode!=fast.sse_mode || plan.baseline_errno!=fast.saved_errno){
fprintf(stderr,"generic error=%d fast error=%d\n",plan.error,fast.error);contract_failure(-1,"metadata");
}
const Contract *schema=fast.path?schema1:schema0;
int expected_n=fast.path?132:12;
if(plan.n!=expected_n)contract_failure(-1,"length");
NativePropertyState local[2],zero={0};NativePipeCache pipe={0};memset(local,0,sizeof(local));
Pending pending;
if(fp_prepare(bound,bound_pipes,&pending))contract_failure(-1,"pending construction");
int queries=0,scalars=0,allocations=0;
for(int i=0;i<plan.n;i++){
const Event *e=&plan.events[i];const Contract *c=&schema[i];int slot=e->slot;
if(e->type==READ_STATE || e->type==WRITE_STATE || e->type==WRITE_OR || e->type==VALID || e->type==ALLOCATE)slot-=(int)fast.entry_count;
if(e->type!=c->type || slot!=c->slot || e->offset!=c->offset || e->size!=c->size || e->aux!=c->aux || e->mask!=c->mask)contract_failure(i,"ordered access signature");
if(e->type==QUERY){
const double *v=queries++?fast.down:fast.up;double key[9]={v[0],v[1]};memcpy(key+2,fp_medium_key,56);
if(memcmp(e->data,key,72))contract_failure(i,"query key");
}else if(e->type==ALLOCATE){
if(slot!=allocations++)contract_failure(i,"append order");
}else if(e->type==SCALAR){
int k=scalars++;double key[9],value;
if(k<2){const double *v=k?fast.down:fast.up;memcpy(key,v,16);memcpy(key+2,fp_medium_key,56);value=v[2];}
else {memcpy(key,fast.pipe_key,24);value=fast.pipe_value;}
if(memcmp(e->data,key,e->size*8) || memcmp(&e->result,&value,8) || fp_hash(e->slot,e->offset,key,e->size)!=fast.hashes[k])contract_failure(i,"memo key/value");
}else if(e->type==WRITE_STATE){
unsigned char expected_data[sizeof(NativePropertyState)]={0};
if(e->size==(int)sizeof(NativePropertyState))memcpy(expected_data,&zero,sizeof(zero));
else if(e->offset==(int)offsetof(NativePropertyState,valid)){unsigned valid=NATIVE_PROPERTY_PT;memcpy(expected_data,&valid,sizeof(valid));}
else memcpy(expected_data,(char*)&pending.entries[slot]+e->offset,e->size);
if(memcmp(e->data,expected_data,e->size))contract_failure(i,"property store");
memcpy((char*)&local[slot]+e->offset,expected_data,e->size);
}else if(e->type==WRITE_OR){
local[slot].valid|=e->mask;
if(memcmp(e->data,&local[slot].valid,4))contract_failure(i,"valid update");
}else if(e->type==READ_STATE){
if(memcmp(e->data,(char*)&local[slot]+e->offset,e->size))contract_failure(i,"consumed field");
}else if(e->type==VALID){
unsigned value=local[slot].valid&e->mask;
if(memcmp(e->data,&value,4))contract_failure(i,"valid test");
}else if(e->type==READ_PIPE){
if(memcmp(e->data,(char*)&pipe+e->offset,e->size))contract_failure(i,"pipe read");
}else if(e->type==WRITE_PIPE){
if(e->offset==(int)offsetof(NativePipeCache,valid)){int valid=i==plan.n-1;pipe.valid=valid;}
else memcpy((char*)&pipe+e->offset,(char*)&pending.pipe+e->offset,e->size);
if(memcmp(e->data,(char*)&pipe+e->offset,e->size))contract_failure(i,"pipe store");
}else contract_failure(i,"uncovered effect");
}
if(memcmp(&pipe,&pending.pipe,sizeof(pipe)) || (fast.path && memcmp(local,pending.entries,sizeof(local))))contract_failure(-1,"final effects");
contract_checks++;
}
+62
View File
@@ -0,0 +1,62 @@
/* Audit only. Generic interpretation is an oracle, never a native Reference. */
static SRContext oracle_generic,oracle_typed,negative_context;
static void check_oracle(NativePropertyCache *p,NativePipeCache *pipes,const double *x,int typed_reason,const Pending *patch){
int saved_errno=errno;SREnvironment env;save_environment(&env);
NativePropertyCache *saved_bound=bound;bound=p;probe_entry_count=p->count;
from_live(&oracle_generic,p,pipes);from_live(&oracle_typed,p,pipes);
int mapping[SR_STATES],appends;phase=CANDIDATE;
int generic_reason=replay_overlay(&oracle_generic,x,mapping,&appends);phase=OFF;
if(generic_reason!=typed_reason){fprintf(stderr,"decision Jacobian=%llu generic=%s typed=%s\n",jac,reasons[generic_reason],reasons[typed_reason]);fatal("guard decisions differ");}
if(!generic_reason){
/* replay_overlay already applied the generic effects in its copy. */
Pending adjusted=*patch;
for(int i=0;i<adjusted.append_count;i++)adjusted.entries[i].jacobian=&oracle_typed.memo;
fp_commit(&oracle_typed.context,oracle_typed.pipes,&adjusted);
if(compare_contexts(&oracle_generic,&oracle_typed) || memcmp(&patch->output,&plan.output,8))fatal("generic/typed patch differs");
for(int i=0;i<appends;i++)if(mapping[plan.entry_count+i]!=(int)(p->count+i))fatal("logical relocation differs");
}
bound=saved_bound;restore_environment(&env);errno=saved_errno;
}
static void negative_test(int which,NativePropertyCache *live,NativePipeCache *pipes,const double *inputs){
FastMetadata saved=fast;Plan *saved_plan=malloc(sizeof(plan));if(!saved_plan)fatal("negative metadata allocation");*saved_plan=plan;
int saved_errno=errno;SREnvironment env;save_environment(&env);
from_live(&negative_context,live,pipes);NativePropertyCache *p=&negative_context.context;
fast.owner=p->jacobian;plan.memo_binding=(uintptr_t)p->jacobian;
double x[4];memcpy(x,inputs,sizeof(x));
switch(which){
case 0:p->temperatures=&negative_context.observer;break;
case 1:p->capacity=p->count;break;
case 2:p->capacity=p->count+1;break;
case 3:negative_context.pipes[0].valid=1;break;
case 4:p->jacobian->recording=1;break;
case 5:memset(p->jacobian->entries,0,sizeof(negative_context.entries));break;
case 6:for(size_t i=0;i<p->jacobian->capacity;i++)if(p->jacobian->entries[i].hash)p->jacobian->entries[i].value=nextafter(p->jacobian->entries[i].value,INFINITY);break;
case 7:x[0]=nextafter(x[0],INFINITY);break;
case 8:fast.output=NAN;plan.output=NAN;break;
case 9:fast.jacobian++;plan.jac++;break;
case 10:feclearexcept(FE_ALL_EXCEPT);break;
case 11:fesetround(fast.rounding==FE_DOWNWARD?FE_UPWARD:FE_DOWNWARD);break;
case 12:
#if defined(__SSE__)
_mm_setcsr(env.sse^0x2000u);
#endif
break;
case 13:if(p->count)p->states[0].jacobian=NULL;break;
case 14:if(p->count){p->states[0].medium=fp_medium;p->states[0].p=fast.up[0];p->states[0].T=fast.up[1];p->states[0].valid|=NATIVE_PROPERTY_PT;}break;
case 15:if(p->count){p->states[0].medium=fp_medium;p->states[0].p=fast.down[0];p->states[0].T=fast.down[1];p->states[0].valid|=NATIVE_PROPERTY_PT;}break;
case 16:fast.error=CONSUMED;plan.error=CONSUMED;break;
case 17:fast.error=VALID_BITS;plan.error=VALID_BITS;break;
case 18:fast.error=EXISTING_UPDATE;plan.error=EXISTING_UPDATE;break;
case 19:fast.error=UNKNOWN_EFFECT;plan.error=UNKNOWN_EFFECT;break;
}
static SRContext unchanged;memcpy(&unchanged,&negative_context,sizeof(unchanged));
Pending patch;int reason=fp_validate(p,negative_context.pipes,x);if(!reason)reason=fp_prepare(p,negative_context.pipes,&patch);
if(!reason)fatal("negative test accepted");
check_oracle(p,negative_context.pipes,x,reason,&patch);
if(memcmp(&unchanged,&negative_context,sizeof(unchanged)))fatal("negative validate modified context");
negative_checks++;fast=saved;plan=*saved_plan;free(saved_plan);restore_environment(&env);errno=saved_errno;
}
static void fp_oracle(NativePropertyCache *p,NativePipeCache *pipes,const double *x,int reason,const Pending *pending){
check_oracle(p,pipes,x,reason,pending);oracle_checks++;
if(jac==200 && !force_every)for(int i=0;i<20;i++)negative_test(i,p,pipes,x);
}
+184
View File
@@ -0,0 +1,184 @@
/* Static effect contract: R288, group6, position16 only. No native calls. */
static const NativeMedium fp_medium={1,2077.2643940499802,5193.1609851249505,293.15,0,1.96e-5,293.15,79.4};
static const double fp_medium_key[]={2077.2643940499802,5193.1609851249505,293.15,0,1.96e-5,293.15,79.4};
typedef struct {
unsigned long long jacobian;
NativeJacobianScalars *owner;
size_t entry_count;
double inputs[4],output;
double up[6]; /* p,T,rho,mu,isentropic_factor,isentropic_exponent */
double down[5]; /* p,T,rho,isentropic_factor,isentropic_exponent */
double pipe_key[3],pipe_value;
uint64_t hashes[3];
int flags,rounding,saved_errno,error,ready,path;
unsigned sse_flags,sse_mode;
} FastMetadata;
typedef struct {
double queries[2][2],density_keys[2][2],density_values[2];
int queries_count,allocations,scalars,values;
} Capture;
typedef struct {
NativePropertyState entries[2];
NativePipeCache pipe;
size_t count;
unsigned long reuse[3];
double output;
int append_count;
} Pending;
static FastMetadata fast;
static Capture capture;
static uint64_t fp_hash(int kind,int medium,const double *keys,size_t n){
uint64_t h=UINT64_C(14695981039346656037)^(unsigned)kind;
h=(h^(unsigned)medium)*UINT64_C(1099511628211);
for(size_t i=0;i<n;i++){uint64_t b;memcpy(&b,&keys[i],8);h=(h^b)*UINT64_C(1099511628211);h^=h>>32;}
return h?h:1;
}
static int fp_medium_equal(const NativeMedium *m){
return m->real_helium==fp_medium.real_helium && m->R==fp_medium.R && m->cp==fp_medium.cp &&
m->Tref==fp_medium.Tref && m->slope==fp_medium.slope && m->mu==fp_medium.mu && m->muT==fp_medium.muT && m->S==fp_medium.S;
}
void fp_unknown(void){fast.error=NONFINITE;}
static void fp_begin(NativePropertyCache *p,NativePipeCache *pipes,const double *x){
memset(&fast,0,sizeof(fast));memset(&capture,0,sizeof(capture));
fast.jacobian=jac;fast.owner=p->jacobian;fast.entry_count=p->count;memcpy(fast.inputs,x,32);
fast.rounding=fegetround();fast.sse_mode=sse_control()&~63u;
if(p->temperatures)fast.error=OBSERVER;
if(pipes[0].valid)fast.error=PIPE_BRANCH;
if(!p->jacobian || p->count>p->capacity || p->capacity>SR_STATES)fast.error=CAPACITY;
}
void fp_query(NativePropertyCache *p,const NativeMedium *m,double pressure,double temperature,NativePropertyState *hit){
int i=capture.queries_count++;
if(p!=bound || i>=2 || hit || !fp_medium_equal(m)){fast.error=QUERY_PATH;return;}
if(!isfinite(pressure) || !isfinite(temperature)){fast.error=NONFINITE;return;}
capture.queries[i][0]=pressure;capture.queries[i][1]=temperature;
}
void fp_allocate(NativePropertyCache *p,NativePropertyState *s,int valid){
int i=capture.allocations++;
if(i>=2 || !valid || p!=bound || capture.queries_count!=i+1 || p->count!=fast.entry_count+(size_t)i+1 || s!=p->states+fast.entry_count+i)fast.error=CAPACITY;
}
void fp_scalar_key(int kind,int medium,const double *keys,size_t n){
int i=capture.scalars++;
if(i>=3 || (i<2?(kind!=NATIVE_JACOBIAN_DENSITY || medium!=1 || n!=9):(kind!=NATIVE_JACOBIAN_PIPE || medium!=0 || n!=3))){fast.error=UNKNOWN_EFFECT;return;}
for(size_t k=0;k<n;k++)if(!isfinite(keys[k]))fast.error=NONFINITE;
if(i<2){
if(memcmp(keys+2,fp_medium_key,sizeof(fp_medium_key)))fast.error=UNKNOWN_EFFECT;
memcpy(capture.density_keys[i],keys,16);
}else memcpy(fast.pipe_key,keys,24);
fast.hashes[i]=fp_hash(kind,medium,keys,n);
}
void fp_scalar_value(int kind,int medium,const double *keys,size_t n,double value){
int i=capture.scalars-1;
if(i<0 || i>=3 || capture.values!=i || (i<2?(kind!=NATIVE_JACOBIAN_DENSITY || medium!=1 || n!=9):(kind!=NATIVE_JACOBIAN_PIPE || medium!=0 || n!=3))){fast.error=UNKNOWN_EFFECT;return;}
if((i<2 && (memcmp(keys,capture.density_keys[i],16) || memcmp(keys+2,fp_medium_key,56))) || (i==2 && memcmp(keys,fast.pipe_key,24)))fast.error=UNKNOWN_EFFECT;
if(!isfinite(value))fast.error=NONFINITE;
if(i<2)capture.density_values[i]=value;else fast.pipe_value=value;
capture.values++;
}
static void fp_end(double output){
fast.flags=fetestexcept(FE_ALL_EXCEPT);fast.sse_flags=sse_control()&63u;fast.saved_errno=errno;fast.output=output;
if(!isfinite(output))fast.error=NONFINITE;
if(capture.queries_count==0 && capture.allocations==0 && capture.scalars==0 && capture.values==0 && bound->count==fast.entry_count)fast.path=0;
else if(capture.queries_count==2 && capture.allocations==2 && capture.scalars==3 && capture.values==3 && bound->count==fast.entry_count+2){
fast.path=1;const NativePropertyState *u=&bound->states[fast.entry_count],*d=u+1;
if(u->valid!=29 || d->valid!=21 || u->temperatures || d->temperatures || u->jacobian!=fast.owner || d->jacobian!=fast.owner || !fp_medium_equal(&u->medium) || !fp_medium_equal(&d->medium))fast.error=UNKNOWN_EFFECT;
if(memcmp(&u->p,capture.queries[0],16) || memcmp(&d->p,capture.queries[1],16) || memcmp(&u->p,capture.density_keys[0],16) || memcmp(&d->p,capture.density_keys[1],16) || memcmp(&u->rho,&capture.density_values[0],8) || memcmp(&d->rho,&capture.density_values[1],8))fast.error=CONSUMED;
const double zero=0;
if(memcmp(&u->h,&zero,8) || memcmp(&d->h,&zero,8) || memcmp(&d->mu,&zero,8))fast.error=UNKNOWN_EFFECT;
double up[]={u->p,u->T,u->rho,u->mu,u->isentropic_factor,u->isentropic_exponent};
double down[]={d->p,d->T,d->rho,d->isentropic_factor,d->isentropic_exponent};
memcpy(fast.up,up,sizeof(up));memcpy(fast.down,down,sizeof(down));
for(int i=0;i<6;i++)if(!isfinite(up[i]))fast.error=NONFINITE;
for(int i=0;i<5;i++)if(!isfinite(down[i]))fast.error=NONFINITE;
}else fast.error=NONFINITE;
const NativePipeCache *pipe=&bound_pipes[0];
const double diameter=.014,length=1,roughness=.0032142857142857142;
if(pipe->valid!=1 || pipe->kind!=1 || !fp_medium_equal(&pipe->medium) || memcmp(&pipe->p1,&fast.inputs[3],8) || memcmp(&pipe->p2,&fast.inputs[1],8) || memcmp(&pipe->T,&fast.inputs[0],8) || memcmp(&pipe->diameter,&diameter,8) || memcmp(&pipe->length,&length,8) || memcmp(&pipe->roughness,&roughness,8) || memcmp(&pipe->flow,&output,8))fast.error=UNKNOWN_EFFECT;
fast.ready=1;
}
/* Exact ordered PT matching against the live prefix. The second query also
* sees the first virtual append; it may not silently ignore it. */
static int fp_match(const NativePropertyState *s,double p,double T){
return (s->valid&NATIVE_PROPERTY_PT) && s->p==p && s->T==T && fp_medium_equal(&s->medium);
}
static int fp_query_miss(const NativePropertyCache *p,double pressure,double temperature){
for(size_t i=0;i<p->count;i++)if(fp_match(&p->states[i],pressure,temperature))return 0;
return 1;
}
static int fp_memo(const NativeJacobianScalars *m,int index){
size_t capacity=m->capacity;
if(!capacity || capacity>MODEL_JACOBIAN_SCALAR_COUNT || (capacity&(capacity-1)) || m->recording || !m->entries)return MEMO_BINDING;
int kind=index<2?NATIVE_JACOBIAN_DENSITY:NATIVE_JACOBIAN_PIPE,medium=index<2?1:0;
size_t n=index<2?9:3,limit=capacity<32?capacity:32;
const double *key=index==0?fast.up:index==1?fast.down:fast.pipe_key;
double value=index==0?fast.up[2]:index==1?fast.down[2]:fast.pipe_value;
uint64_t hash=fast.hashes[index];
for(size_t i=0;i<limit;i++){
const NativeJacobianScalarEntry *e=&m->entries[(hash+i)&(capacity-1)];
if(!e->hash)return MEMO_MISS;
if(e->hash==hash && e->kind==kind && e->medium_kind==medium && e->input_count==n &&
(index<2?(!memcmp(e->inputs,key,16) && !memcmp(e->inputs+2,fp_medium_key,56)):!memcmp(e->inputs,key,24))){
if(!isfinite(e->value) || memcmp(&e->value,&value,8))return MEMO_VALUE;
return OK;
}
}
return MEMO_MISS;
}
static int fp_validate(NativePropertyCache *p,NativePipeCache *pipes,const double *inputs){
if(!fast.ready || fast.jacobian!=jac)return NO_RECORD;
if(fast.error)return fast.error;
if(fegetround()!=fast.rounding || (fetestexcept(FE_ALL_EXCEPT)&fast.flags)!=fast.flags ||
(fast.saved_errno && errno!=fast.saved_errno) || (sse_control()&~63u)!=fast.sse_mode ||
(sse_control()&fast.sse_flags)!=fast.sse_flags)return UNKNOWN_EFFECT;
if(memcmp(inputs,fast.inputs,32))return INPUTS;
for(int i=0;i<4;i++)if(!isfinite(inputs[i]))return NONFINITE;
if(p->temperatures)return OBSERVER;
if(p->capacity>SR_STATES || p->count>p->capacity)return CAPACITY;
if(!p->jacobian || p->jacobian!=fast.owner || !p->jacobian->entries || p->jacobian->recording || p->jacobian->capacity>MODEL_JACOBIAN_SCALAR_COUNT)return MEMO_BINDING;
for(size_t i=0;i<p->count;i++)if(p->states[i].temperatures || p->states[i].jacobian!=p->jacobian)return MEMO_BINDING;
if(pipes[0].valid)return PIPE_BRANCH;
if(fast.path==1){
if(!fp_query_miss(p,fast.up[0],fast.up[1]))return QUERY_PATH;
if(p->count>=p->capacity)return CAPACITY;
int reason=fp_memo(p->jacobian,0);if(reason)return reason;
if(!fp_query_miss(p,fast.down[0],fast.down[1]))return QUERY_PATH;
/* The virtual U entry already has PT at the time query D runs. */
if(fast.up[0]==fast.down[0] && fast.up[1]==fast.down[1])return QUERY_PATH;
if(p->count+1>=p->capacity)return CAPACITY;
reason=fp_memo(p->jacobian,1);if(reason)return reason;
reason=fp_memo(p->jacobian,2);if(reason)return reason;
}else if(fast.path!=0)return NONFINITE;
return OK;
}
static int fp_prepare(NativePropertyCache *p,NativePipeCache *pipes,Pending *pending){
pending->append_count=fast.path?2:0;pending->count=p->count+(size_t)pending->append_count;
pending->reuse[0]=0;pending->reuse[1]=fast.path?2:0;pending->reuse[2]=fast.path?1:0;
if(fast.path){
NativePropertyState *u=&pending->entries[0],*d=&pending->entries[1];
memset(pending->entries,0,sizeof(pending->entries));
u->medium=fp_medium;u->p=fast.up[0];u->T=fast.up[1];u->rho=fast.up[2];u->mu=fast.up[3];u->isentropic_factor=fast.up[4];u->isentropic_exponent=fast.up[5];u->valid=29;u->jacobian=p->jacobian;
d->medium=fp_medium;d->p=fast.down[0];d->T=fast.down[1];d->rho=fast.down[2];d->isentropic_factor=fast.down[3];d->isentropic_exponent=fast.down[4];d->valid=21;d->jacobian=p->jacobian;
}
/* Copy ONE pipe so even its unwritten padding is preserved. */
pending->pipe=pipes[0];pending->pipe.medium=fp_medium;
pending->pipe.p1=fast.inputs[3];pending->pipe.p2=fast.inputs[1];pending->pipe.T=fast.inputs[0];
pending->pipe.diameter=.014;pending->pipe.length=1;pending->pipe.roughness=.0032142857142857142;pending->pipe.kind=1;
pending->pipe.flow=fast.output;pending->pipe.valid=1;pending->output=fast.output;
if(!isfinite(fast.output))return NONFINITE;
return OK;
}
static void fp_commit(NativePropertyCache *p,NativePipeCache *pipes,const Pending *pending){
if(pending->append_count){
memcpy(&p->states[p->count],pending->entries,sizeof(pending->entries));p->count=pending->count;
p->jacobian->reuses[1]+=pending->reuse[1];p->jacobian->reuses[2]+=pending->reuse[2];
}
pipes[0].valid=0;pipes[0]=pending->pipe;
}
#if RR_AUDIT
/* Full generic guard and event contracts exist only in correctness builds. */
static unsigned long long contract_checks,oracle_checks,negative_checks;
static void fp_contract_check(void);
static void fp_oracle(NativePropertyCache*,NativePipeCache*,const double*,int,const Pending*);
#endif
+6
View File
@@ -0,0 +1,6 @@
/* Only the separately named baseline recording kernels call these hooks. */
void fp_unknown(void);
void fp_query(NativePropertyCache*,const NativeMedium*,double,double,NativePropertyState*);
void fp_allocate(NativePropertyCache*,NativePropertyState*,int);
void fp_scalar_key(int,int,const double*,size_t);
void fp_scalar_value(int,int,const double*,size_t,double);
+40
View File
@@ -0,0 +1,40 @@
double rr_execute(NativePropertyCache *p,NativePipeCache *pipes,const double *x,SROperation original){
visits++;
#if RR_AUDIT
audit_context(1,0,NULL,NULL,NULL,1,0,p,pipes,NULL);entry_audits++;
from_live(&before_reject,p,pipes);
#endif
uint64_t start=lp_tick(),tick;double output;Pending pending;size_t initial_count=p->count;
attempts++;rr_replaying=1;count_different+=(p->count!=fast.entry_count);
double saved_output=fast.output;
#if RR_AUDIT
double old_plan_output=plan.output;
if(force_every && (attempts-1)%(unsigned)force_every==0){fast.output=NAN;plan.output=NAN;}
#endif
tick=lp_tick();int reason=fp_validate(p,pipes,x);validation_ticks+=lp_tick()-tick;
if(!reason){tick=lp_tick();reason=fp_prepare(p,pipes,&pending);overlay_ticks+=lp_tick()-tick;}
#if RR_AUDIT
fp_oracle(p,pipes,x,reason,&pending);
plan.output=old_plan_output;
#endif
fast.output=saved_output;
if(!reason){
tick=lp_tick();fp_commit(p,pipes,&pending);output=pending.output;commit_ticks+=lp_tick()-tick;
commits++;successes++;appends_total+=pending.append_count;paths[fast.path]++;
relocations+=(fast.path && fast.entry_count!=initial_count);
metadata_min=metadata_max=sizeof(fast);
}else{
rejects++;reject_reason[reason]++;rr_replaying=0;
#if RR_AUDIT
from_live(&after_reject,p,pipes);
if(compare_contexts(&before_reject,&after_reject))fatal("reject altered live context");
rollback_checks++;
#endif
tick=lp_tick();rr_original_scope=1;output=original(p,pipes,x);rr_original_scope=0;fallback_ticks+=lp_tick()-tick;fallbacks++;
}
rr_replaying=0;path_ticks+=lp_tick()-start;
#if RR_AUDIT
audit_context(2,0,NULL,NULL,NULL,1,output,p,pipes,NULL);exit_audits++;
#endif
return output;
}
+63
View File
@@ -0,0 +1,63 @@
/* Diagnostic worker only: original tail executes on every visit. */
#include "valve_tail_diag.h"
#include <windows.h>
#include <cpuid.h>
#include <stdio.h>
#include <stdlib.h>
#include <fenv.h>
#include <math.h>
#include <stddef.h>
int kd_scope;
uint64_t kd_jac;
KdMemo kd_memo;
static KdRecord records[26000];
static size_t count;
static uint64_t entries,hits,executions,lookup_ticks,capture_ticks,reset_ticks,tail_ticks;
static uint64_t base_lookup_ticks,miss_lookup_ticks,hit_lookup_ticks;
static uint64_t qpc_frequency,tsc_start,qpc_start;
static FILE *matrix_file;
static int disabled;
static void fatal(const char *s){fprintf(stderr,"valve tail: %s\n",s);abort();}
void kd_eval_exit(double t,const double*y,const double*dy,const double*w,int r,NativePropertyCache*p,NativePipeCache*c,ModelJacobianWorkspace*m){(void)t;(void)y;(void)dy;(void)w;(void)r;(void)p;(void)c;(void)m;}
void kd_start(void){
unsigned a,b,c,d;if(!__get_cpuid(0x80000007,&a,&b,&c,&d) || !(d&(1u<<8)))fatal("invariant TSC required");
LARGE_INTEGER f;QueryPerformanceFrequency(&f);qpc_frequency=(uint64_t)f.QuadPart;
qpc_start=lp_tick();tsc_start=kd_clock();disabled=getenv("KD_DISABLED")!=NULL;
if(getenv("KD_MATRICES")){matrix_file=fopen("jacobians.bin","wb");if(!matrix_file)fatal("matrix file");setvbuf(matrix_file,NULL,_IOFBF,1024*1024);}
}
void kd_new_jac(void){uint64_t t=kd_clock();kd_memo.ready=0;reset_ticks+=kd_clock()-t;kd_jac++;}
void kd_before(KdRecord *r,double p,double T,double pd,double g,double rho){
double key[5]={p,T,pd,g,rho};memcpy(r->key,key,40);r->before=kd_env();
r->hit=lp_color>=0 && !disabled && kd_lookup(r->key,r->before,&r->cm,&r->vel);
r->jac=kd_jac;r->group=lp_color;
}
void kd_after(KdRecord *r,double cm,double vel,uint64_t elapsed,int executed){
uint64_t capture_start=lp_color<0?kd_clock():0;
r->after=kd_env();r->cm=cm;r->vel=vel;r->tail=elapsed;r->executed=executed;r->capture=0;r->pad=0;
if(lp_color<0){
memcpy(kd_memo.key,r->key,40);kd_memo.cm=cm;kd_memo.vel=vel;kd_memo.env=r->before;
kd_memo.ready=kd_finite(r->key) && isfinite(cm) && isfinite(vel) && kd_env_equal(r->before,r->after) && (r->before.cw&63)==63 && (r->before.mxcsr&0x1f80)==0x1f80;
r->capture=kd_clock()-capture_start;capture_ticks+=r->capture;
}
entries++;lookup_ticks+=r->lookup;if(lp_color<0)base_lookup_ticks+=r->lookup;else if(r->hit)hit_lookup_ticks+=r->lookup;else miss_lookup_ticks+=r->lookup;
hits+=r->hit;executions+=executed;tail_ticks+=elapsed;
#if KD_TRACE
if(count>=sizeof(records)/sizeof(*records)){fatal("record capacity");}
records[count++]=*r;
#endif
}
void kd_matrix(double t,const double*y,const double*m){
if(!matrix_file){return;}
int saved=errno;fenv_t env;fegetenv(&env);unsigned sse=_mm_getcsr();
fwrite(&t,8,1,matrix_file);fwrite(y,8,NSTATES,matrix_file);fwrite(m,8,NSTATES*NSTATES,matrix_file);
fesetenv(&env);_mm_setcsr(sse);errno=saved;
}
void kd_finish(void){
uint64_t t=kd_clock(),q=lp_tick();double frequency=(double)(t-tsc_start)/(q-qpc_start)*qpc_frequency;
uint64_t empty[10001];for(int i=0;i<10001;i++){uint64_t a=kd_clock();empty[i]=kd_clock()-a;}
FILE *f=fopen("tail-records.bin","wb");if(!f)fatal("records");fwrite(records,sizeof(KdRecord),count,f);fclose(f);
f=fopen("empty-clock.bin","wb");fwrite(empty,8,10001,f);fclose(f);
f=fopen("tail.json","wb");if(!f)fatal("summary");
fprintf(f,"{\"frequency\":%.9f,\"recordBytes\":%llu,\"memoBytes\":%llu,\"jacobians\":%llu,\"entries\":%llu,\"hits\":%llu,\"executions\":%llu,\"lookupTicks\":%llu,\"hitLookupTicks\":%llu,\"missLookupTicks\":%llu,\"baseLookupTicks\":%llu,\"captureTicks\":%llu,\"resetTicks\":%llu,\"tailTicks\":%llu,\"skipEnabled\":%d,\"disabled\":%d}\n",frequency,(unsigned long long)sizeof(KdRecord),(unsigned long long)sizeof(KdMemo),(unsigned long long)kd_jac,(unsigned long long)entries,(unsigned long long)hits,(unsigned long long)executions,(unsigned long long)lookup_ticks,(unsigned long long)hit_lookup_ticks,(unsigned long long)miss_lookup_ticks,(unsigned long long)base_lookup_ticks,(unsigned long long)capture_ticks,(unsigned long long)reset_ticks,(unsigned long long)tail_ticks,0,disabled);fclose(f);
if(matrix_file)fclose(matrix_file);
}
+35
View File
@@ -0,0 +1,35 @@
#ifndef VALVE_TAIL_DIAG_H
#define VALVE_TAIL_DIAG_H
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#include "local_probe.h"
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <xmmintrin.h>
#ifndef KD_TRACE
#define KD_TRACE 1
#endif
typedef struct {unsigned mxcsr;unsigned short cw,sw;int err;} KdEnv;
typedef struct {uint64_t key[5];double cm,vel;KdEnv env;int ready;} KdMemo;
typedef struct {uint64_t jac;int group,hit;uint64_t key[5];double cm,vel;KdEnv before,after;uint64_t tail,lookup,capture;int executed,pad;} KdRecord;
extern int kd_scope;
extern uint64_t kd_jac;
extern KdMemo kd_memo;
static inline uint64_t kd_clock(void){unsigned lo,hi;__asm__ __volatile__("lfence\n\trdtsc\n\tlfence":"=a"(lo),"=d"(hi)::"memory");return ((uint64_t)hi<<32)|lo;}
static inline KdEnv kd_env(void){KdEnv e;__asm__ __volatile__("stmxcsr %0;fnstcw %1;fnstsw %2":"=m"(e.mxcsr),"=m"(e.cw),"=m"(e.sw)::"memory");e.err=errno;return e;}
static inline int kd_env_equal(KdEnv a,KdEnv b){return a.mxcsr==b.mxcsr && a.cw==b.cw && a.sw==b.sw && a.err==b.err;}
static inline int kd_finite(const uint64_t *key){for(int i=0;i<5;i++)if((key[i]&UINT64_C(0x7ff0000000000000))==UINT64_C(0x7ff0000000000000))return 0;return 1;}
/* Diagnostic-only lookup: its answer NEVER bypasses the original tail.
* Only a baseline entry is considered. Probe misses never populate the slot. */
static inline int kd_lookup(const uint64_t *key,KdEnv e,double *cm,double *vel){
if(!kd_memo.ready || !kd_env_equal(e,kd_memo.env) || memcmp(key,kd_memo.key,40))return 0;
*cm=kd_memo.cm;*vel=kd_memo.vel;return 1;
}
void kd_start(void);void kd_finish(void);void kd_new_jac(void);
void kd_before(KdRecord*,double,double,double,double,double);
void kd_after(KdRecord*,double,double,uint64_t,int);
void kd_matrix(double,const double*,const double*);
void kd_eval_exit(double,const double*,const double*,const double*,int,NativePropertyCache*,NativePipeCache*,ModelJacobianWorkspace*);
#endif
@@ -0,0 +1,56 @@
"""Negative checks of the evidence validator (not a production reuse guard)."""
from copy import deepcopy
import json
import analyze_context_access as ax
def rejected(events):
try:
ax.analyze_operation(events)
except AssertionError:
return True
return False
def main():
examples=json.loads((ax.OUT/'jacobian-200.json').read_text(encoding='utf-8'))
original=next(x['events'] for x in examples if x['summary']['group']==6)
assert ax.analyze_operation(original)['replayExact']
results={}
events=deepcopy(original)
index=next(i for i,e in enumerate(events) if e['event']=='write' and e.get('domain')=='pipes' and e.get('field')=='cache->valid')
del events[index]
results['omitted_equal_value_pipe_valid_store']=rejected(events)
events=deepcopy(original)
event=next(e for e in events if e['event']=='read' and e.get('field')=='s->rho')
event['value']='0000000000000000'
results['wrong_consumed_rho']=rejected(events)
events=deepcopy(original)
event=next(e for e in events if e['event']=='allocate')
event['slot']-=1
results['baseline_slot_used_for_shifted_append']=rejected(events)
events=deepcopy(original)
event=next(e for e in events if e['event']=='write' and e.get('field')=='cache->count')
event['value']=(13).to_bytes(8,'little').hex()
results['restored_baseline_count']=rejected(events)
events=deepcopy(original)
event=next(e for e in events if e['event']=='match')
event.update(hit=True,slot=0)
results['wrong_first_match']=rejected(events)
events=deepcopy(original)
event=next(e for e in events if e['event']=='valid_test' and e['mask']==8)
event['value']=8
results['wrong_valid_bit_result']=rejected(events)
assert all(results.values()), results
(ax.OUT/'negative-checks.json').write_text(json.dumps(results,indent=2)+'\n',encoding='utf-8')
print(json.dumps(results,indent=2))
if __name__=='__main__':main()