修复循环信号与事件采样并接入 LSTP 接触定位,补充八路验证及复用实验
相较上一版 Jacobian 确定性复用更新,本次补齐事件边界一致性、结果两侧采样及接触事件定位;保留已有物性复用和组件力学公式。 - 统一 UD00 信号求值与下一事件查询的绝对时间边界,修复循环边界浮点舍入导致的阶段错位、重复或漏报,并覆盖零时长、多阶段及长周期场景。 - 引入原生输出语义 v2:保留规则网格真实时间,补充内部时间事件和状态事件的左邻及事件后采样,按保存时间、状态和离散模式重放结果。 - 两条代码生成路径均发出 LSTP 接触描述,默认定位间隙过零及非负力模式的力截断;仅在接受事件时更新防重复记录,增加 contactEvents 诊断计数。 - 补充 MASS/LSTP 独立事件实验、八路全曲线与驱动阶段配对评估,以及 Amesim 不连续点输出对照和力差定位报告;MASS 新增释放机制仍保留为独立实验。 - 保存局部 probe、context 访问与回退、shadow replay、R288 real skip/typed replay 及阀门数值尾部诊断工具和报告;未证明净收益的实验不启用为生产默认优化。 - 更新原生运行说明和元件建模规范,补充信号边界、输出语义、接触事件和实验依赖回归测试。 验证:五组专项回归共 34 项全部通过;37 个待提交 Python 文件语法检查通过;git diff --cached --check 通过。
This commit is contained in:
1 parent
1aac220084
commit
7611f13208
85 files changed
+11358
-33
No files matched your search
@@ -0,0 +1,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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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。
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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/`,可与诊断脚本一起提交。
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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`
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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成本/正确性实验。**
|
||||
@@ -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。
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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`,
|
||||
用于保留计时方法改进的证据,不与最终主样本混用。
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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/` 的独立实验报告。
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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 一致性和构建记录。
|
||||
@@ -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、调用次数和计时器测量。
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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++;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user