backend-benchmark.mjs (4712B)
1 // Matched production Worker benchmark. No evaluator instrumentation or GC tuning. 2 export async function run(control,backend,memoryOnly=false){ 3 const base=new URL('../',document.querySelector('script[type=module][src]').src); 4 const [fixtures,catalogue,{Graph},{view}]=await Promise.all([ 5 fetch('/zig-fixtures.json').then(r=>r.json()),fetch(new URL('catalogue.json',base)).then(r=>r.json()), 6 import(new URL('dist/probe-api.js',base)).then(m=>m.graph),import(new URL('dist/probe-api.js',base)).then(m=>m.codecs), 7 ]); 8 const assert=(ok,message)=>{if(!ok)throw Error(message);}; 9 const mark=async name=>{await fetch(control+'/phase',{method:'POST',body:JSON.stringify({name})});}; 10 const pause=ms=>new Promise(r=>setTimeout(r,ms)); 11 const report={schema:1,backend,memoryOnly,userAgent:navigator.userAgent,packages:[],compilations:[],checks:['production Worker replies checked against independent Haskell goldens'],scope:'Fresh profile; localhost uncompressed/no-store HTTP; first package includes cold runtime startup; later Workers may benefit from engine module caches. Compilation includes initialization/authentication/environment loading. Runs include full selected-codec observation and Worker messaging. No user think-time or forced GC.'}; 12 function client(){ 13 const worker=new Worker(new URL('dist/wasm-worker.js?backend='+backend,base),{type:'module'}); 14 let pending; 15 worker.onerror=e=>{pending?.reject(Error(e.message));pending=undefined;}; 16 worker.onmessage=({data:m})=>{ 17 if(!pending)return; 18 if(m.type==='error'){pending.reject(Error(m.message));pending=undefined;return;} 19 if(m.type==='output')pending.terminal+=m.text; 20 if(m.type==='state'&&m.state==='input')worker.postMessage({type:'resume',id:m.id,value:pending.input}); 21 if(m.type===pending.expected){pending.resolve({message:m,terminal:pending.terminal,ms:performance.now()-pending.start});pending=undefined;} 22 }; 23 return {request(message,expected,input=''){assert(!pending,'Overlapping benchmark request');return new Promise((resolve,reject)=>{pending={resolve,reject,expected,input,terminal:'',start:performance.now()};worker.postMessage(message);});},dispose(){worker.terminate();}}; 24 } 25 const cases=Object.entries(fixtures.demos).map(([name,demo])=>{ 26 const entry=catalogue.find(e=>e.hash===demo.hash),c=demo.haskellCases[0];assert(entry,'Missing fixture catalogue entry'); 27 const output=entry.presets?.[c.export]?.output??entry.output; 28 const graph=new Graph(),expected=view(graph,graph.fromTernary(c.result),output); 29 assert(output!=='tree'&&output!=='list','Benchmark requires complete scalar/record observation'); 30 return {name,demo,entry,c,output,expected}; 31 }); 32 await mark('baseline');await pause(memoryOnly?3000:700); 33 for(const {name,demo,entry,c,output,expected} of cases){ 34 await mark('package:'+name);const start=performance.now(),w=client(); 35 try{ 36 await w.request({type:'load',hash:demo.hash,localId:name},'loaded');const readyMs=performance.now()-start; 37 const result=await w.request({type:'run',export:c.export,arguments:c.arguments.map(value=>({type:'tree',value})),output,mode:demo.mode},'done',c.input??''); 38 assert(JSON.stringify(result.message.result)===JSON.stringify(expected),'Published result '+name); 39 assert(result.terminal===c.terminal,'Published terminal '+name); 40 report.packages.push({name,readyMs,runMs:result.ms,linearBytes:result.message.stats.linearMemoryBytes}); 41 if(memoryOnly){await mark('live-package:'+name);await pause(1500);} 42 }finally{w.dispose();} 43 await mark('package-disposed:'+name);await pause(120); 44 } 45 for(const {name,demo,entry,c,output,expected} of cases){ 46 await mark('compile:'+name);const start=performance.now(),w=client(); 47 try{ 48 const compiled=await w.request({type:'compile',source:demo.source,environment:entry.environment},'compiled'); 49 const row={name,readyMs:performance.now()-start,workerCompileMs:compiled.message.milliseconds,compileLinearBytes:compiled.message.stats.linearMemoryBytes,runs:[]}; 50 await mark('runs:'+name); 51 for(let i=0;i<21;i++){ 52 const r=await w.request({type:'run',export:c.export,arguments:c.arguments.map(value=>({type:'tree',value})),output,mode:demo.mode},'done',c.input??''); 53 assert(JSON.stringify(r.message.result)===JSON.stringify(expected),'Compiled result '+name); 54 assert(r.terminal===c.terminal,'Compiled terminal '+name); 55 row.runs.push({ms:r.ms,linearBytes:r.message.stats.linearMemoryBytes}); 56 } 57 report.compilations.push(row); 58 if(memoryOnly){await mark('live-compiled:'+name);await pause(1500);} 59 }finally{w.dispose();} 60 await mark('compile-disposed:'+name);await pause(150); 61 } 62 await mark('final-idle');await pause(1500);await mark('finished'); 63 return report; 64 }