相较上一版 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 通过。
224 lines
15 KiB
C
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);
|
|
}
|