diff options
Diffstat (limited to 'prototypes/workbench/evaluation-check.mjs')
| -rw-r--r-- | prototypes/workbench/evaluation-check.mjs | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/prototypes/workbench/evaluation-check.mjs b/prototypes/workbench/evaluation-check.mjs new file mode 100644 index 0000000..bb3d616 --- /dev/null +++ b/prototypes/workbench/evaluation-check.mjs @@ -0,0 +1,76 @@ +// A bounded adoption check on existing authored files, not a platform benchmark. +import assert from 'node:assert/strict'; +import { readFile, writeFile } from 'node:fs/promises'; +import { createHash } from 'node:crypto'; +import { platform, release, arch, cpus } from 'node:os'; +import { performance } from 'node:perf_hooks'; +import { createRuntime } from './web/runtime.mjs'; + +const args = process.argv.slice(2); +if (args.length !== 2 || args[0] !== '--output') throw new Error('Use --output NEW.json'); +const root = new URL('.', import.meta.url); +const wasm = await readFile(new URL('web/core.wasm', root)); +const hash = bytes => createHash('sha256').update(bytes).digest('hex'); +const evidence = { purpose: 'Choose whether to enable binding reuse by default', + utc: new Date().toISOString(), host: { platform: platform(), release: release(), + arch: arch(), cpu: cpus()[0]?.model, node: process.version, affinityControlled: false }, + sourceSha256: {}, cases: [], + timingScope: 'Event dispatch plus snapshot construction. Excludes loading, compilation, DOM, equality checks and statistics reads. Five batch samples are not latency percentiles.', +}; +for (const path of ['web/core.wasm', 'core.zig', 'evaluation.zig', 'web/runtime.mjs', + 'web/compiler.mjs', 'evaluation-check.mjs', 'evaluation.test.mjs']) + evidence.sourceSha256[path] = hash(await readFile(new URL(path, root))); + +for (const name of ['counter', 'toggle', 'charge']) { + const path = `examples/${name}.pure`; + const source = await readFile(new URL(path, root), 'utf8'); + evidence.sourceSha256[path] = hash(source); + const plain = await createRuntime(wasm), cached = await createRuntime(wasm, {cacheEntries:32}); + const program = plain.apply(source); cached.apply(source); + const input = program.inputs[0]?.name; + const trace = Array.from({length:100}, () => 'show'); + for (let i = 0; i < 600; i++) { + if (input && i % 60 === 0) trace.push('down'); + if (input && i % 60 === 15) trace.push('up'); + trace.push('frame'); + } + function action(runtime, kind) { + if (kind === 'frame') assert.equal(runtime.frame(), 0); + else if (kind !== 'show') assert.equal(runtime[kind](input, 'replay'), 0); + return runtime.snapshot(); + } + const totals = {plain:{work:0,instructions:0,cacheHits:0},cached:{work:0,instructions:0,cacheHits:0}}; + for (const kind of trace) { + const left = action(plain, kind), right = action(cached, kind); + assert.equal(left.lastError, 0); assert.deepEqual(right, left); + for (const [key, runtime] of Object.entries({plain,cached})) { + const stats = runtime.evaluationStats(); + for (const field of Object.keys(totals[key])) totals[key][field] += stats[field]; + } + } + function replay(runtime) { + runtime.reset(); + for (const kind of trace) { + if (kind === 'frame') runtime.frame(); + else if (kind !== 'show') runtime[kind](input, 'replay'); + runtime.snapshot(); + } + } + for (let warm = 0; warm < 3; warm++) { replay(plain); replay(cached); } + const samples = []; + for (let trial = 0; trial < 5; trial++) { + for (const key of trial % 2 ? ['cached','plain'] : ['plain','cached']) { + const runtime = key === 'plain' ? plain : cached; + const start = performance.now(); replay(runtime); + samples.push({trial,key,elapsedMs:performance.now()-start}); + } + assert.deepEqual(cached.snapshot(), plain.snapshot()); + } + const median = key => samples.filter(s=>s.key===key).map(s=>s.elapsedMs).sort((a,b)=>a-b)[2]; + evidence.cases.push({name,checkedSnapshots:trace.length, traceSha256:hash(JSON.stringify(trace)), + totals, memory:cached.snapshot().memory, evaluation:cached.evaluationStats(), samples, + medianMs:{plain:median('plain'),cached:median('cached')}, cachedOverPlain:median('cached')/median('plain')}); +} +await writeFile(args[1], JSON.stringify(evidence,null,2)+'\n', {flag:'wx'}); +for (const c of evidence.cases) console.log(JSON.stringify({name:c.name,checkedSnapshots:c.checkedSnapshots, + totals:c.totals,medianMs:c.medianMs,cachedOverPlain:c.cachedOverPlain})); |
