修复循环信号与事件采样并接入 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,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()
|
||||
Reference in new issue
Block a user