tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

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 }