mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-11-14 08:17:26 +01:00
31 lines
6.0 KiB
JavaScript
31 lines
6.0 KiB
JavaScript
const fs=require('fs'),path=require('path');
|
||
const source=path.join(__dirname,'../reports/repository-runtime-comparison.json');
|
||
const r=JSON.parse(fs.readFileSync(source));
|
||
const zipSource=path.join(__dirname,'../reports/repository-zip-node-comparison.json');
|
||
if(fs.existsSync(zipSource)){const zip=JSON.parse(fs.readFileSync(zipSource));for(const run of r.results.filter(x=>x.runtime==='node')){const correction=zip.results.find(x=>x.repeat===run.repeat);if(correction)run.results=run.results.map(x=>x.name==='repository ZIP'?correction.results[0]:x);}}
|
||
const median=a=>{a=[...a].sort((a,b)=>a-b);return (a[Math.floor((a.length-1)/2)]+a[Math.floor(a.length/2)])/2};
|
||
const names=r.results[0].results.map(x=>x.name);
|
||
const summary=names.map(name=>{
|
||
const out={name};
|
||
for(const runtime of ['node','go']){
|
||
const runs=r.results.filter(x=>x.runtime===runtime).map(x=>x.results.find(y=>y.name===name));
|
||
out[runtime]={status:runs[0].output.status,output:runs[0].output,coldMs:median(runs.map(x=>x.cold.ms)),diagnostic:runs.find(x=>x.diagnostic)?.diagnostic,warm:[1,8].map(c=>{
|
||
const batches=runs.flatMap(x=>x.warm.filter(y=>y.concurrency===c));if(!batches.length)return null;
|
||
return {concurrency:c,p50Ms:median(batches.map(x=>x.p50Ms)),p95Ms:median(batches.map(x=>x.p95Ms)),firstBodyMs:median(batches.map(x=>x.p50FirstBodyMs)),requestsPerSecond:median(batches.map(x=>x.requestsPerSecond)),cpuMsPerRequest:median(batches.map(x=>x.cpuMs/x.requests)),peakRSSMiB:Math.max(...batches.map(x=>x.peakProcessTreeRssBytes))/1048576};
|
||
}).filter(Boolean)};
|
||
}
|
||
out.outputParity=out.node.status===200&&out.go.status===200&&out.node.output.sha256===out.go.output.sha256;
|
||
return out;
|
||
});
|
||
fs.writeFileSync(source.replace('.json','-summary.json'),JSON.stringify({fixture:r.fixture,summary},null,2));
|
||
const html=`<!doctype html><html><head><meta charset="utf-8"><style>
|
||
body{font-family:var(--font-sans,system-ui);color:var(--foreground,#222);line-height:1.5;margin:0}h2{font-size:23px;margin:0 0 8px}p{margin:8px 0 18px;color:var(--muted-foreground,#666)}select{font:inherit;padding:7px;background:var(--background,white);color:inherit;border:1px solid var(--border,#ddd);border-radius:6px}table{border-collapse:collapse;width:100%;font-size:14px;margin:16px 0}th,td{text-align:right;padding:11px 7px;border-bottom:1px solid var(--border,#ddd);font-variant-numeric:tabular-nums}th:first-child,td:first-child{text-align:left;padding-left:0}th{font-size:12px;color:var(--muted-foreground,#666)}small{font-size:12px}.note{font-size:13px}.better{color:var(--success,#23744c)}.worse{color:var(--destructive,#b34242)}details{font-size:13px;margin-top:16px}#scroll{overflow:auto}
|
||
</style></head><body><h2>Go vs TypeScript · same repository snapshot</h2><p>${r.fixture.originalFiles.toLocaleString()} repository files plus a synthetic 1,000-file directory. Two rounds with runtime order reversed.</p><label>Measurement <select id="mode"><option value="warm1">Warm, one request at a time</option><option value="warm8">Warm, eight concurrent requests</option><option value="p95">Warm p95, eight concurrent requests</option><option value="cold">First request after startup</option><option value="ttfb">Warm time to first body byte</option><option value="cpu">Warm CPU per request</option><option value="rss">Peak process-tree memory</option></select></label><div id="scroll"><table><thead><tr><th>Use case</th><th>TypeScript</th><th>Go</th><th>Go / TS</th></tr></thead><tbody id="rows"></tbody></table></div><p id="caption" class="note"></p><p class="note">Loopback HTTP, identical archive originals, filesystem storage, shared caches enabled, one anonymization worker, identity encoding. No CDN, GitHub round trips or browser rendering. TypeScript ZIP was retested with a temporary admin key and the same source ZIP served over loopback; its worker aborted both attempts. These are backend measurements, not beta page-load times.</p><details><summary>Validation and limits</summary><p id="parity"></p><p>Commit ${r.fixture.commit}. Node ${r.node}; ${r.go}. Warm results are medians of each round's p50, not pooled percentiles. Each round uses 32 requests per concurrency for normal endpoints and 3 sequential ZIP downloads. First requests can share dependencies warmed by earlier endpoints. RSS includes application workers but excludes MongoDB, Redis and the client. CPU includes background work. RSS includes caches retained from earlier cases. The OS page cache was not dropped. The selected small files contain no occurrence of the configured anonymization term; these timings do not measure replacement-heavy text.</p></details><script>
|
||
const data=${JSON.stringify(summary)};
|
||
function render(){const mode=document.getElementById('mode').value;const rows=document.getElementById('rows');rows.replaceChildren();let unit=mode==='rss'?' MiB':' ms';
|
||
function value(r){const w=r.warm.find(x=>x.concurrency===((mode==='warm8'||mode==='p95')?8:1));if(mode==='cold')return r.coldMs;if(!w)return null;return mode==='p95'?w.p95Ms:mode==='ttfb'?w.firstBodyMs:mode==='cpu'?w.cpuMsPerRequest:mode==='rss'?w.peakRSSMiB:w.p50Ms;}
|
||
for(const item of data){const tr=document.createElement('tr'),a=value(item.node),b=value(item.go);const values=[item.name,item.node.status!==200?(item.node.status===0?'Aborted':'HTTP '+item.node.status):a==null?'—':a.toFixed(1)+unit,item.go.status!==200?'HTTP '+item.go.status:b==null?'—':b.toFixed(1)+unit,item.outputParity&&a>0&&b!=null?(b/a).toFixed(2)+'×':'—'];values.forEach((text,i)=>{const td=document.createElement('td');td.textContent=text;if(i===3&&item.outputParity)td.className=b<a?'better':'worse';tr.append(td)});rows.append(tr)}document.getElementById('caption').textContent='Go / TS below 1.00× means Go used less time or memory. Ratios are shown only for matching successful outputs.';document.getElementById('parity').textContent=data.map(x=>x.name+': '+(x.outputParity?'matching output':x.node.diagnostic||'outputs differ or a runtime failed')).join('. ');}
|
||
document.getElementById('mode').onchange=render;render();
|
||
</script></body></html>`;
|
||
fs.writeFileSync(source.replace('.json','.html'),html);console.log(JSON.stringify(summary,null,2));
|