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

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

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

224 lines
15 KiB
C

/* 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);
}