preview-compare.mjs (2773B)
1 // Compare the same 128-node unary tree: no sharing/expansion advantage hidden 2 // in the baseline. Every iteration checks the complete shape in either format. 3 import assert from 'node:assert/strict'; 4 import {readFile,writeFile} from 'node:fs/promises'; 5 import {spawnSync} from 'node:child_process'; 6 import {createHash} from 'node:crypto'; 7 import {cpus} from 'node:os'; 8 import {WasmRuntime} from './runtime.mjs'; 9 import {Graph} from '../demo-site/dist/graph.js'; 10 const [wasmPath,output,mode,pressureArg]=process.argv.slice(2); 11 const tree='1'.repeat(127)+'0',iterations=100; 12 if(mode) { 13 const pressure=Number(pressureArg); 14 const g=mode==='js'?new Graph({fuel:Infinity}):await WasmRuntime.create(await readFile(wasmPath)); 15 const root=g.fromTernary(tree); 16 // Extra owned roots simulate a retained host session. JS has no root-table 17 // scan in this path, but retain equivalent host references for comparison. 18 const retained=Array.from({length:pressure},()=>mode==='js'?g.leaf:g.retain(g.leaf)); 19 const before=mode==='js'?null:g.stats().owned; 20 const start=performance.now(); 21 for(let i=0;i<iterations;i++) { 22 if(mode==='wasm-batch') { 23 const p=g.preview(root,128); 24 assert.equal(p.truncated,false);assert.equal(p.nodes.length,128); 25 for(let j=0;j<128;j++)assert.deepEqual(p.nodes[j],{id:j,kind:j===127?'leaf':'stem',children:j===127?[]:[j+1]}); 26 } else assert.equal(g.toTernary(root),tree); 27 } 28 const ms=performance.now()-start; 29 if(mode!=='js')assert.equal(g.stats().owned,before); 30 assert.equal(retained.length,pressure); 31 console.log(JSON.stringify({mode,pressure,iterations,ms,perSnapshotMs:ms/iterations,peakRssKiB:process.resourceUsage().maxRSS})); 32 g.dispose?.(); 33 } else { 34 const samples=[]; 35 for(const pressure of [0,2000])for(let round=0;round<3;round++) { 36 for(const mode of round%2?['wasm-batch','wasm-node','js']:['js','wasm-node','wasm-batch']) { 37 const child=spawnSync(process.execPath,[import.meta.filename,wasmPath,'-',mode,String(pressure)],{encoding:'utf8',timeout:60_000}); 38 if(child.error||child.status!==0)throw new Error(`${mode}: ${child.error??child.stderr}`); 39 samples.push({...JSON.parse(child.stdout),round}); 40 } 41 } 42 await writeFile(output,JSON.stringify({schema:1,node:process.version,cpu:cpus()[0]?.model, 43 wasmSha256:createHash('sha256').update(await readFile(wasmPath)).digest('hex'),tree, samples},null,2)+'\n'); 44 for(const pressure of [0,2000]) { 45 const median=mode=>samples.filter(s=>s.mode===mode&&s.pressure===pressure).map(s=>s.perSnapshotMs).sort((a,b)=>a-b)[1]; 46 console.log(`128-node inspection (${pressure} extra roots): JS ${median('js').toFixed(3)} ms; WASM/node ${median('wasm-node').toFixed(3)} ms; WASM/batch ${median('wasm-batch').toFixed(3)} ms`); 47 } 48 }