ostia 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -3
- package/chunk-gsjgejr1.js +21 -0
- package/chunk-y1gkhb0y.js +4 -0
- package/cli.js +57 -32
- package/index.d.ts +85 -5
- package/index.js +1 -1
- package/package.json +1 -1
- package/runner.ts +4 -4
- package/chunk-qh19pbhc.js +0 -4
- package/chunk-v7vpm34a.js +0 -19
package/README.md
CHANGED
|
@@ -133,15 +133,49 @@ Heap snapshot - bun fixtures/allocate.ts (instrumented, 2518 objects, 0.12MB)
|
|
|
133
133
|
|
|
134
134
|
In-process microbenchmarks registered with `group()` / `task()`. Each task samples
|
|
135
135
|
for `--time-budget` (default 500ms). `--min-samples` is a hard floor kept even when it
|
|
136
|
-
overruns the budget
|
|
137
|
-
budget
|
|
138
|
-
|
|
136
|
+
overruns the budget. Left unset, the floor is cost-aware in both directions: as many
|
|
137
|
+
trials as fit in the budget (capped at 20) so one slow task can't blow the suite's total,
|
|
138
|
+
but never below the floor a task's per-trial cost earns it - 3 at ≤1ms, two more per
|
|
139
|
+
decade of cost, 10 from about 3s up. Cheap tasks are time-bound and collect thousands of
|
|
140
|
+
trials either way; only the few expensive tasks in a suite pay for the extra rigor, and
|
|
141
|
+
those are exactly where a 3-sample mean is shakiest. Fast calls are batched so a trial
|
|
142
|
+
spans at least 1µs and a full budget yields about 10k trials at most.
|
|
143
|
+
|
|
144
|
+
| per-trial cost | fits in 500ms | default floor |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| 30ns | thousands | 20 (time-bound; ends in the tens of thousands) |
|
|
147
|
+
| 30ms | 16 | 16 |
|
|
148
|
+
| 140ms | 3 | 7 |
|
|
149
|
+
| 2.4s | 0 | 10 |
|
|
150
|
+
|
|
151
|
+
A run that ends below its cost-class floor (only possible with an explicit
|
|
152
|
+
`--min-samples` or per-task `minSamples`) carries a `low-sample-count` warning with
|
|
153
|
+
`{ samples, target, trialCostNs }`, so a renderer or an agent can flag a thin number
|
|
154
|
+
without re-deriving the policy from the raw sample array.
|
|
139
155
|
|
|
140
156
|
```sh
|
|
141
157
|
ostia bench bench/*.ts
|
|
142
158
|
ostia bench --time-budget 500 --min-samples 50 bench/stats.ts
|
|
159
|
+
ostia bench bench/*.ts --jobs auto # suite files in parallel, see below
|
|
160
|
+
ostia bench bench/*.ts --format minimal # one compact JSON object per task
|
|
143
161
|
```
|
|
144
162
|
|
|
163
|
+
`--jobs N|auto` runs that many suite files at once, each still in its own child process.
|
|
164
|
+
Files are independent by design, so for a multi-file suite this is close to a linear
|
|
165
|
+
wall-clock win - but concurrent CPU-bound processes contend for cores, caches and turbo
|
|
166
|
+
headroom, so numbers taken at `--jobs > 1` are noisier and not like-for-like with a
|
|
167
|
+
baseline measured at 1. It defaults to 1 for that reason; opt in for exploratory runs,
|
|
168
|
+
keep 1 for anything you `compare` or `ci` against.
|
|
169
|
+
|
|
170
|
+
`--isolate` gives every task its own child process instead of sharing its suite file's,
|
|
171
|
+
isolating each task's JIT tier state, inline caches and heap shape from every other task
|
|
172
|
+
in the run - the same guarantee suite files already get from each other, at task
|
|
173
|
+
granularity. `task(name, fn, { isolate })` / `group(name, fn, { isolate })` override the
|
|
174
|
+
suite-wide default for mixed suites (e.g. a couple of outlier-prone tasks isolated, the
|
|
175
|
+
rest sharing a process). `--jobs` then pools across those per-task processes the same way
|
|
176
|
+
it pools across per-file ones, so pair a higher `--jobs` with `--isolate` deliberately -
|
|
177
|
+
overhead now scales with task count, not file count.
|
|
178
|
+
|
|
145
179
|
```
|
|
146
180
|
Command Mean [ms] Min…Max [ms] Relative
|
|
147
181
|
--------------------------------------------------------------------------------------
|
|
@@ -176,8 +210,22 @@ ostia report out.json # table (default)
|
|
|
176
210
|
ostia report out.json --format markdown
|
|
177
211
|
ostia report out.json --format json
|
|
178
212
|
ostia report out.json --format jsonl
|
|
213
|
+
ostia report out.json --format minimal
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Minimal format - one JSON object per timing run, no header, no raw sample array, no prose.
|
|
217
|
+
Built to pipe straight into an LLM agent's context: the full document carries every
|
|
218
|
+
sample (tens of thousands for a fast task), which is tokens a reviewer never reads.
|
|
219
|
+
Numbers stay in ns so they line up with `compare` deltas and the JSON document.
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
{"task":"diffText()/append at end","group":"diffText()","samples":9282,"mean":50213.4,"median":49871,"stddev":2104.7,"stddevPct":4.19,"min":48120,"max":81002,"relative":1,"warnings":[],"unit":"ns"}
|
|
223
|
+
{"task":"repaint/4000 chars","group":"repaint","description":"full repaint every keystroke","samples":3,"mean":2.61e9,"median":2.4e9,"stddevPct":15.3,"relative":47800,"warnings":[{"code":"low-sample-count","data":{"samples":3,"target":10}}],"unit":"ns"}
|
|
179
224
|
```
|
|
180
225
|
|
|
226
|
+
`ostia compare ... --format minimal` adds `delta: { medianPct, meanPct, verdict, pass }` to
|
|
227
|
+
each line, so "did this PR regress" is `lines.some(l => l.delta?.verdict === "regressed")`.
|
|
228
|
+
|
|
181
229
|
Markdown:
|
|
182
230
|
|
|
183
231
|
```
|
|
@@ -383,6 +431,27 @@ group("parse", () => {
|
|
|
383
431
|
})
|
|
384
432
|
```
|
|
385
433
|
|
|
434
|
+
That is the whole registration surface: `group()` and `task()`. Presentation lives in
|
|
435
|
+
the renderers (`--format`), not in the suite file.
|
|
436
|
+
|
|
437
|
+
Both take an optional `description` that flows into the document
|
|
438
|
+
(`Workload.description` / `Workload.groupDescription`) and into `--format minimal`, so
|
|
439
|
+
what a number measures and why travels with the data instead of living only in a
|
|
440
|
+
source comment a reader has to go find:
|
|
441
|
+
|
|
442
|
+
```ts
|
|
443
|
+
group(
|
|
444
|
+
"repaint",
|
|
445
|
+
() => {
|
|
446
|
+
task("1,000 chars", () => repaint(doc1k))
|
|
447
|
+
task("4,000 chars", () => repaint(doc4k), {
|
|
448
|
+
description: "worst case: full repaint every keystroke at the max document size",
|
|
449
|
+
})
|
|
450
|
+
},
|
|
451
|
+
{ description: "editor repaint cost as document size grows" },
|
|
452
|
+
)
|
|
453
|
+
```
|
|
454
|
+
|
|
386
455
|
Mark one task per group as the `Relative` reference with `{ baseline: true }`
|
|
387
456
|
(mirrors mitata's `baseline()`); otherwise `Relative` defaults to the fastest
|
|
388
457
|
task in the group:
|
|
@@ -402,6 +471,7 @@ const doc = await bench({
|
|
|
402
471
|
suites: ["suite.ts"],
|
|
403
472
|
timeBudgetMs: 500,
|
|
404
473
|
minSamples: 50,
|
|
474
|
+
jobs: 1, // suite files at once; > 1 trades fidelity for wall time
|
|
405
475
|
})
|
|
406
476
|
```
|
|
407
477
|
|
|
@@ -435,6 +505,7 @@ Pure functions of a `ProfileDocument`. Each returns `{ text? }` and/or `{ files?
|
|
|
435
505
|
| `markdown` | agent- and human-readable report |
|
|
436
506
|
| `json` | pretty JSON document |
|
|
437
507
|
| `jsonl` | one metadata line, then one line per run |
|
|
508
|
+
| `minimal` | one compact line per timing run, no sample array; for LLM/CI consumption |
|
|
438
509
|
| `collapsed` | folded stacks (`name;name;name count`) |
|
|
439
510
|
| `mermaid` | top-N call tree |
|
|
440
511
|
| `speedscope` | speedscope.app JSON |
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{h,e,r,u,R,i,b,T,s,k,t,l,m}from"./chunk-y1gkhb0y.js";function je(o){return o.startsWith("file://")?o.slice(7):o}function oe(o,D,c){let C=o.nodes,p=C.length,I=new Map,N=[],P=new Map,U=new Int32Array(p);for(let O=0;O<p;O++){let W=C[O],F=W.callFrame,L=je(F.url),K=I.get(F.functionName);if(K===void 0)K=new Map,I.set(F.functionName,K);let G=K.get(L);if(G===void 0)G=N.length,K.set(L,G),N.push({key:e("fr",F.functionName,L),name:F.functionName,url:L||void 0,line:F.lineNumber>=0?F.lineNumber:void 0,col:F.columnNumber>=0?F.columnNumber:void 0});U[O]=G,P.set(W.id,O)}let B=Array(p);for(let O=0;O<p;O++){let W=C[O];B[O]={id:W.id,frameIx:U[P.get(W.id)],children:W.children??[]}}let M=new Float64Array(p),S=new Float64Array(p),{samples:E,timeDeltas:V}=o;for(let O=0;O<E.length;O++){let W=P.get(E[O]);if(W===void 0)continue;M[W]+=V[O]??0,S[W]+=1}let z=new Int32Array(p).fill(-1);for(let O=0;O<p;O++){let W=C[O].children;if(!W)continue;for(let F of W){let L=P.get(F);if(L!==void 0)z[L]=O}}let A=[],H=[];for(let O=p-1;O>=0;O--)if(z[O]===-1)H.push(O);while(H.length>0){let O=H.pop();A.push(O);let W=C[O].children;if(!W)continue;for(let F of W){let L=P.get(F);if(L!==void 0&&z[L]===O)H.push(L)}}let _=new Float64Array(p);for(let O=A.length-1;O>=0;O--){let W=A[O];_[W]+=M[W];let F=z[W];if(F>=0)_[F]+=_[W]}let J=Array(N.length),j=[];for(let O=0;O<p;O++){let W=B[O].frameIx,F=J[W];if(F)F.selfUs+=M[O],F.totalUs+=_[O],F.samples+=S[O];else{let L={frameIx:W,selfUs:M[O],totalUs:_[O],samples:S[O]};J[W]=L,j.push(L)}}return{origin:D,samplingIntervalUs:c,frames:N,nodes:B,totals:j.sort((O,W)=>W.selfUs-O.selfUs),samples:{nodeIds:o.samples,timeDeltasUs:o.timeDeltas}}}function _e(o,D,c,C){let p=["--cpu-prof","--cpu-prof-dir",D,"--cpu-prof-name",c,"--cpu-prof-interval",String(C)],I=o[0];if(I==="bun"||I?.endsWith("/bun"))return[I,...p,...o.slice(1)];return o}async function de(o){let D=`${o.artifactDir}/${o.fileName}`,c=o.argv[0],C=c==="bun"||c?.endsWith("/bun"),p=_e(o.argv,o.artifactDir,o.fileName,o.intervalUs),I=C?o.env:{...process.env,...o.env,BUN_OPTIONS:`--cpu-prof --cpu-prof-dir ${o.artifactDir} --cpu-prof-name ${o.fileName} --cpu-prof-interval ${o.intervalUs}`},N=Bun.nanoseconds(),U=await Bun.spawn(p,{cwd:o.cwd,env:I,stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited,B=Bun.nanoseconds()-N,M=Bun.file(D);if(!await M.exists())return{diagnosticWallNs:B,exitCode:U,warnings:[{code:"artifact-missing",message:`Expected a .cpuprofile at ${D} after exit ${U}, found nothing. The workload's argv[0] must be a \`bun\` binary for CPU capture.`,data:{artifactPath:D,argv:o.argv}}]};let S=await M.json(),E=oe(S,"cpu-prof",o.intervalUs),V=S.samples.length===0?[{code:"empty-profile",message:"CPU capture produced zero samples."}]:[];return{diagnosticWallNs:B,exitCode:U,artifactPath:D,cpu:E,warnings:V}}function fe(o,D="heap-prof"){let{node_fields:c,node_types:C}=o.snapshot.meta,p=c.indexOf("type"),I=c.indexOf("self_size"),N=c.length,P=C[0];if(p===-1||I===-1||!Array.isArray(P))return{origin:D,typeCounts:[],objectCount:o.snapshot.node_count};let U=P.length,B=Array(U),M=new Map,S=[],E=0,V=o.nodes,z=V.length;for(let j=0;j<z;j+=N){let O=V[j+p],W=V[j+I]??0;E+=W;let F;if(O>=0&&O<U){if(F=B[O],F===void 0)F={type:P[O],count:0,bytes:0},B[O]=F,S.push(F)}else{let L=`unknown(${O})`;if(F=M.get(L),F===void 0)F={type:L,count:0,bytes:0},M.set(L,F),S.push(F)}F.count++,F.bytes+=W}let A=S.sort((j,O)=>O.count-j.count),H=A.slice(0,20),_=A.slice(20),J=H.map(({type:j,count:O,bytes:W})=>({type:j,count:O,retainedBytes:W}));if(_.length>0){let j=0,O=0;for(let W of _)j+=W.count,O+=W.bytes;J.push({type:"other",count:j,retainedBytes:O})}return{origin:D,heapSizeBytes:E,objectCount:o.snapshot.node_count,typeCounts:J}}function Le(o,D,c){let C=["--heap-prof","--heap-prof-dir",D,"--heap-prof-name",c],p=o[0];if(p==="bun"||p?.endsWith("/bun"))return[p,...C,...o.slice(1)];return o}async function ge(o){let D=`${o.artifactDir}/${o.fileName}`,c=o.argv[0],C=c==="bun"||c?.endsWith("/bun"),p=Le(o.argv,o.artifactDir,o.fileName),I=C?o.env:{...process.env,...o.env,BUN_OPTIONS:`--heap-prof --heap-prof-dir ${o.artifactDir} --heap-prof-name ${o.fileName}`},N=Bun.nanoseconds(),U=await Bun.spawn(p,{cwd:o.cwd,env:I,stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited,B=Bun.nanoseconds()-N,M=Bun.file(D);if(!await M.exists())return{diagnosticWallNs:B,exitCode:U,warnings:[{code:"artifact-missing",message:`Expected a heap snapshot at ${D} after exit ${U}, found nothing. The workload's argv[0] must be a \`bun\` binary for heap capture.`,data:{artifactPath:D,argv:o.argv}}]};let S=await M.json(),E=fe(S,"heap-prof");return{diagnosticWallNs:B,exitCode:U,artifactPath:D,heap:E,warnings:[]}}import{Session as He}from"inspector/promises";var Je=1000;async function he(o,D={}){let c=D.intervalUs??Je,C=new He;C.connect();let p=Bun.nanoseconds();try{await C.post("Profiler.enable"),await C.post("Profiler.setSamplingInterval",{interval:c}),await C.post("Profiler.start");let I=await o(),{profile:N}=await C.post("Profiler.stop"),P=Bun.nanoseconds()-p,U=oe(N,"inspector",c);return{result:I,cpu:U,diagnosticWallNs:P}}finally{C.disconnect()}}import{profile as Ve}from"bun:jsc";var ze=new Map([["LLInt","llint"],["Baseline","baseline"],["DFG","dfg"],["FTL","ftl"]]),be=4294967295;function ye(o,D){let c=D??o.interval*1e6,C=new Map,p=[];function I(F,L,K,G){let q=C.get(F);if(q===void 0)q=new Map,C.set(F,q);let Y=L??"",X=q.get(Y);if(X===void 0)X=p.length,q.set(Y,X),p.push({key:e("fr",F,Y),name:F,url:L,line:K,col:G});return X}function N(F){let L=F.line===be,K=L?void 0:F.line-1,G=L||F.column===be?void 0:F.column-1;return I(F.name,F.sourceURL,K,G)}let P=I("(root)",void 0,void 0,void 0),U=1,B={id:0,frameIx:P,children:new Map,selfUs:0,samples:0,totalUs:0},M=new Map([[0,B]]),S={llint:0,baseline:0,dfg:0,ftl:0},E=new Map,V=[],z=[];for(let F of o.traces){let L=F.frames,K=B;for(let Y=L.length-1;Y>=0;Y--){let X=N(L[Y]),ne=K.children.get(X);if(!ne)ne={id:U++,frameIx:X,children:new Map,selfUs:0,samples:0,totalUs:0},K.children.set(X,ne),M.set(ne.id,ne);K=ne}K.selfUs+=c,K.samples+=1,V.push(K.id),z.push(c);let G=L[0],q=G&&ze.get(G.category);if(q){S[q]++;let Y=E.get(q)??new Map;Y.set(K.frameIx,(Y.get(K.frameIx)??0)+1),E.set(q,Y)}}function A(F){let L=F.selfUs;for(let K of F.children.values())L+=A(K);return F.totalUs=L,L}A(B);let H=new Map;function _(F){let L=H.get(F.frameIx);if(L)L.selfUs+=F.selfUs,L.totalUs+=F.totalUs,L.samples+=F.samples;else H.set(F.frameIx,{frameIx:F.frameIx,selfUs:F.selfUs,totalUs:F.totalUs,samples:F.samples});for(let K of F.children.values())_(K)}_(B);let J=[...M.values()].map((F)=>({id:F.id,frameIx:F.frameIx,children:[...F.children.values()].map((L)=>L.id)})),j={origin:"jsc-profile",samplingIntervalUs:c,frames:p,nodes:J,totals:[...H.values()].sort((F,L)=>L.selfUs-F.selfUs),samples:{nodeIds:V,timeDeltasUs:z}},O=[...E.entries()].flatMap(([F,L])=>[...L.entries()].sort((K,G)=>G[1]-K[1]).slice(0,3).map(([K,G])=>({tier:F,frameKey:p[K].key,samples:G})));return{cpu:j,jit:{origin:"jsc-profile",tiers:S,topFramesByTier:O}}}var Ke=1000;async function we(o,D={}){let c=D.intervalUs??Ke,C,p=Bun.nanoseconds(),I=await Ve(async()=>(C=await o(),C),c),N=Bun.nanoseconds()-p,{cpu:P,jit:U}=ye(I.stackTraces,c);return{result:C,cpu:P,jit:U,diagnosticWallNs:N}}async function ue(o){let D=Bun.nanoseconds(),c=Bun.spawn(o.argv,{cwd:o.cwd,env:o.env,stdout:"ignore",stderr:"ignore",stdin:"ignore"}),C=await c.exited,p=Bun.nanoseconds(),I=c.resourceUsage?.();return{wallNs:p-D,exitCode:C,userNs:I?Number(I.cpuTime.user)*1000:void 0,systemNs:I?Number(I.cpuTime.system)*1000:void 0,maxRssBytes:I?.maxRSS}}function xe(o){return o.trim().split(/\s+/).filter(Boolean)}var Ge=10,Ye=3000000000,qe=3;async function g(o){let D=o.warmup??qe;for(let M=0;M<D;M++)await ue(o);let c=[],C=o.runs??o.minRuns??Ge,p=o.runs!==void 0?0:o.minTotalNs??Ye,I=0,N=0;while(N<C||I<p){let M=await ue(o);if(c.push({i:N,wallNs:M.wallNs,exitCode:M.exitCode,userNs:M.userNs,systemNs:M.systemNs,maxRssBytes:M.maxRssBytes}),I+=M.wallNs,N++,o.runs!==void 0&&N>=o.runs)break}let P=c.map((M)=>M.wallNs),U=l(P),B=m(U,c.map((M)=>M.exitCode));return{trials:c,timing:U,warnings:B}}var Re=new URL("./runner.ts",import.meta.url).pathname,Qe="node_modules/.cache/ostia";function x(){return Math.max(1,navigator.hardwareConcurrency||1)}async function d(o){let c=`${o.outDir??Qe}/bench-tmp`,C=o.cwd??process.cwd(),p=Math.max(1,Math.floor(o.jobs??1)),I={timeBudgetMs:o.timeBudgetMs,minSamples:o.minSamples,gc:o.gc},N=o.suites.map((U)=>U.startsWith("/")?U:`${C}/${U}`),P=async(U,B)=>{let M=new Set,S=0,E,V=async()=>{while(E===void 0&&S<U.length){let z=S++;try{let A=Bun.spawn(U[z],{cwd:C,stdout:"inherit",stderr:"inherit",stdin:"ignore"});M.add(A);let H=await A.exited;if(M.delete(A),H!==0)throw Error(`Bench suite failed: ${B(z)} (runner exited ${H})`)}catch(A){E??=A instanceof Error?A:Error(String(A));for(let H of M)H.kill()}}};if(await Promise.all(Array.from({length:Math.min(p,U.length)},V)),E)throw E};try{let U=N.map((_)=>`${c}/${e("bench-plan",_)}.json`),B=N.map((_,J)=>["bun",Re,_,U[J],JSON.stringify({...I,filter:o.filter,isolate:o.isolate,planOnly:!0})]);await P(B,(_)=>o.suites[_]);let M=await Promise.all(U.map(async(_)=>{let{tasks:J}=await Bun.file(_).json();return J})),S=[];for(let _=0;_<M.length;_++){let J=M[_].filter((j)=>!j.isolate).map((j)=>j.id);if(J.length>0)S.push({suiteIndex:_,taskIds:J,markIsolated:!1});for(let j of M[_])if(j.isolate)S.push({suiteIndex:_,taskIds:[j.id],markIsolated:!0})}let E=S.map((_,J)=>`${c}/${e("bench-item",N[_.suiteIndex],J)}.json`),V=S.map((_,J)=>["bun",Re,N[_.suiteIndex],E[J],JSON.stringify({...I,taskIds:_.taskIds,..._.markIsolated&&{markIsolated:!0}})]);await P(V,(_)=>o.suites[S[_].suiteIndex]);let z=await Promise.all(E.map(t)),A=[],H=[];for(let _=0;_<M.length;_++){let J=S.findIndex((F)=>F.suiteIndex===_&&!F.markIsolated),j=J>=0?z[J]:void 0,O=0,W=new Map;S.forEach((F,L)=>{if(F.suiteIndex===_&&F.markIsolated)W.set(F.taskIds[0],z[L])});for(let F of M[_])if(F.isolate){let L=W.get(F.id);A.push(L.workloads[0]),H.push(L.runs[0])}else A.push(j.workloads[O]),H.push(j.runs[O]),O++}return r(A,H)}finally{await Bun.spawn(["rm","-rf",c]).exited}}var a={timingPct:5,frameSelfPct:10,heapTypePct:10,minFrameSelfUs:1000};function ie(o,D){if(o===0)return D===0?0:1/0;return(D-o)/o*100}function te(o,D,c){return o.runs.find((C)=>C.workloadId===D&&C.phase===c)}function f(o,D,c=a){let C=new Set(D.workloads.map((I)=>I.id)),p=[];for(let I of o.workloads){if(!C.has(I.id))continue;let N=w(o,D,I.id,c);if(N)p.push(N)}return p}function w(o,D,c,C=a){let p=te(o,c,"timing"),I=te(D,c,"timing"),N=te(o,c,"cpu"),P=te(D,c,"cpu"),U=te(o,c,"heap"),B=te(D,c,"heap"),M=p?.id??N?.id??U?.id,S=I?.id??P?.id??B?.id;if(!M||!S)return;let E=!1,V;if(p?.timing&&I?.timing){let H=ie(p.timing.median,I.timing.median),_=ie(p.timing.mean,I.timing.mean),J=H>C.timingPct?"regressed":H<-C.timingPct?"improved":"unchanged";if(J==="regressed")E=!0;V={medianDeltaPct:H,meanDeltaPct:_,verdict:J}}let z;if(N?.cpu&&P?.cpu){let H=new Map(N.cpu.totals.map((W)=>[N.cpu.frames[W.frameIx].key,W])),_=new Map(P.cpu.totals.map((W)=>[P.cpu.frames[W.frameIx].key,W])),J=new Map(N.cpu.frames.map((W)=>[W.key,W.name])),j=new Map(P.cpu.frames.map((W)=>[W.key,W.name]));z=[...new Set([...H.keys(),..._.keys()])].map((W)=>{let F=H.get(W)?.selfUs??0,L=_.get(W)?.selfUs??0;return{frameKey:W,name:j.get(W)??J.get(W)??W,baseSelfUs:F,candSelfUs:L,deltaPct:ie(F,L)}}).sort((W,F)=>Math.abs(F.deltaPct)-Math.abs(W.deltaPct));for(let W of z)if((W.baseSelfUs>=C.minFrameSelfUs||W.candSelfUs>=C.minFrameSelfUs)&&W.deltaPct>C.frameSelfPct)E=!0}let A;if(U?.heap&&B?.heap){let H=new Map(U.heap.typeCounts.map((j)=>[j.type,j])),_=new Map(B.heap.typeCounts.map((j)=>[j.type,j]));A=[...new Set([...H.keys(),..._.keys()])].map((j)=>{let O=H.get(j),W=_.get(j);return{type:j,baseCount:O?.count??0,candCount:W?.count??0,baseBytes:O?.retainedBytes,candBytes:W?.retainedBytes,deltaPct:ie(O?.count??0,W?.count??0)}}).sort((j,O)=>Math.abs(O.deltaPct)-Math.abs(j.deltaPct));for(let j of A)if(j.deltaPct>C.heapTypePct)E=!0}return{id:e("cmp",M,S),baselineRunId:M,candidateRunId:S,timing:V,frames:z,heapTypes:A,thresholds:C,verdict:E?"fail":"pass"}}function Z(o,D){if(D){let c=o.runs.find((C)=>C.id===D);return c?.cpu?[c]:[]}return o.runs.filter((c)=>c.phase==="cpu"&&c.cpu)}function re(o){let D=o.nodes,c=D.length,C=Xe(o),p=new Int32Array(c).fill(-1);for(let U=0;U<c;U++)for(let B of D[U].children){let M=C(B);if(M!==-1)p[M]=U}let I=[];for(let U=0;U<c;U++)if(p[U]===-1)I.push(U);let N=[],P=[];for(let U=I.length-1;U>=0;U--)P.push(I[U]);while(P.length>0){let U=P.pop();N.push(U);for(let B of D[U].children){let M=C(B);if(M!==-1&&p[M]===U)P.push(M)}}return{count:c,indexOf:C,parentIx:p,roots:I,order:N}}function Xe(o){let D=o.nodes,c=D.length,C=1/0,p=-1/0,I=!0;for(let P=0;P<c;P++){let U=D[P].id;if(!Number.isInteger(U)){I=!1;break}if(U<C)C=U;if(U>p)p=U}if(I&&c>0&&p-C<c*4+64){let P=p-C+1,U=new Int32Array(P).fill(-1);for(let B=0;B<c;B++)U[D[B].id-C]=B;return(B)=>{let M=B-C;return M>=0&&M<P?U[M]:-1}}let N=new Map;for(let P=0;P<c;P++)N.set(D[P].id,P);return(P)=>N.get(P)??-1}function Pe(o,D){let{count:c,indexOf:C,parentIx:p,order:I}=D,N=new Float64Array(c),P=new Float64Array(c),U=o.samples?.nodeIds??[],B=o.samples?.timeDeltasUs??[];for(let S=0;S<U.length;S++){let E=C(U[S]);if(E===-1)continue;N[E]+=B[S]??0,P[E]+=1}let M=new Float64Array(c);for(let S=I.length-1;S>=0;S--){let E=I[S];M[E]+=N[E];let V=p[E];if(V>=0)M[V]+=M[E]}return{selfUs:N,totalUs:M,samples:P}}var ke={name:"collapsed",async render(o,D={}){return{files:Z(o,D.runId).map((p)=>{let I=p.cpu,{nodes:N,frames:P}=I,U=re(I),B=Array(U.count);for(let z of U.order){let A=P[N[z].frameIx].name||"(anonymous)",H=U.parentIx[z];B[z]=H===-1?A:`${B[H]};${A}`}let M=new Float64Array(U.count),S=[],E=I.samples?.nodeIds??[];for(let z=0;z<E.length;z++){let A=U.indexOf(E[z]);if(A===-1)continue;if(M[A]++===0)S.push(A)}let V=Array(S.length);for(let z=0;z<S.length;z++){let A=S[z];V[z]=`${B[A]} ${M[A]}`}return{path:`${p.id}.collapsed.txt`,content:V.join(`
|
|
3
|
+
`)+(V.length>0?`
|
|
4
|
+
`:"")}})}}};var Te={name:"cpuprofile",async render(o,D={}){let c=Z(o,D.runId),C=[],p=[];for(let I of c){if(I.cpu?.origin!=="cpu-prof"&&I.cpu?.origin!=="inspector"){p.push(`${I.id} (origin ${I.cpu?.origin??"unknown"} has no .cpuprofile artifact to pass through)`);continue}let N=I.artifacts.find((U)=>U.kind==="cpuprofile");if(!N){p.push(`${I.id} (no cpuprofile artifact recorded on this run)`);continue}let P=Bun.file(N.path);if(!await P.exists()){p.push(`${I.id} (artifact missing on disk: ${N.path})`);continue}C.push({path:`${I.id}.cpuprofile`,content:await P.text()})}if(C.length===0&&p.length>0)return{text:`No .cpuprofile artifacts available:
|
|
5
|
+
${p.map((I)=>` - ${I}`).join(`
|
|
6
|
+
`)}
|
|
7
|
+
`};return{files:C}}};var Ie={name:"json",async render(o){return{text:k(o)}}};var $e={name:"jsonl",async render(o){let{runs:D,...c}=o;return{text:`${[h(c),...D.map((p)=>h(p))].join(`
|
|
8
|
+
`)}
|
|
9
|
+
`}}};function ee(o){return(o/1e6).toFixed(3)}function ae(o){return o?.label??o?.command?.join(" ")??o?.entry?.task??o?.id??"unknown"}var ve=10,Ce=10,Ne={name:"markdown",async render(o){let D=new Map(o.workloads.map((p)=>[p.id,p])),c=[];c.push("# Profile Report",""),c.push(`Bun ${o.bunVersion} \xB7 ostia ${o.toolVersion} \xB7 ${o.platform.os}/${o.platform.arch} \xB7 ${o.createdAt}`,"");let C=o.runs.filter((p)=>p.phase==="timing"&&p.timing!==void 0);if(C.length>0){c.push("## Timing",""),c.push("| Command | Mean \xB1 SD (ms) | Min\u2026Max (ms) | Median (ms) |","|---|---|---|---|");for(let I of C){let N=ae(D.get(I.workloadId)),P=I.timing;c.push(`| ${N} | ${ee(P.mean)} \xB1 ${ee(P.stddev)} | ${ee(P.min)}\u2026${ee(P.max)} | ${ee(P.median)} |`)}c.push("");let p=C.filter((I)=>I.warnings.length>0);if(p.length>0){c.push("### Warnings","");for(let I of p){let N=ae(D.get(I.workloadId));for(let P of I.warnings)c.push(`- **${N}**: ${P.message} (\`${P.code}\`)`)}c.push("")}}for(let p of o.runs){if(p.phase!=="cpu"&&p.phase!=="heap")continue;let I=ae(D.get(p.workloadId));if(p.phase==="cpu"){if(c.push(`## CPU capture - ${I}`,""),c.push(`instrumented, diagnostic wall ${ee(p.diagnosticWallNs??0)}ms`,""),p.cpu){c.push(`origin: \`${p.cpu.origin}\`, interval: ${p.cpu.samplingIntervalUs}\xB5s`,""),c.push("| Self % | Self (ms) | Total (ms) | Frame |","|---|---|---|---|");let N=p.cpu.totals.reduce((P,U)=>P+U.selfUs,0)||1;for(let P of p.cpu.totals.slice(0,ve)){let U=p.cpu.frames[P.frameIx],B=(P.selfUs/N*100).toFixed(1);c.push(`| ${B}% | ${(P.selfUs/1000).toFixed(2)} | ${(P.totalUs/1000).toFixed(2)} | ${U?.name||"(anonymous)"} |`)}if(c.push(""),p.jit){let P=p.jit.tiers;c.push(`JIT tiers: LLInt ${P.llint} \xB7 Baseline ${P.baseline} \xB7 DFG ${P.dfg} \xB7 FTL ${P.ftl}`,"")}}}else if(c.push(`## Heap snapshot - ${I}`,""),c.push(`instrumented, diagnostic wall ${ee(p.diagnosticWallNs??0)}ms`,""),p.heap){c.push(`${p.heap.objectCount??"?"} objects, ${((p.heap.heapSizeBytes??0)/1e6).toFixed(2)}MB`,""),c.push("| Count | Type |","|---|---|");for(let N of p.heap.typeCounts.slice(0,Ce))c.push(`| ${N.count} | ${N.type} |`);c.push("")}for(let N of p.artifacts)c.push(`- artifact: \`${N.path}\``);for(let N of p.warnings)c.push(`- ! ${N.message} (\`${N.code}\`)`);if(p.artifacts.length>0||p.warnings.length>0)c.push("")}if(o.comparisons&&o.comparisons.length>0){c.push("## Comparisons","");for(let p of o.comparisons){let I=o.runs.find((P)=>P.id===p.candidateRunId),N=ae(I?D.get(I.workloadId):void 0);if(c.push(`### ${p.verdict==="pass"?"\u2713":"\u2717"} ${N}`,""),p.timing){let P=p.timing.medianDeltaPct>0?"+":"";c.push(`- timing: ${P}${p.timing.medianDeltaPct.toFixed(1)}% median (**${p.timing.verdict}**)`)}for(let P of p.frames?.slice(0,ve)??[]){if(Math.abs(P.deltaPct)<0.5)continue;let U=P.deltaPct>0?"+":"";c.push(`- frame \`${P.name}\`: ${U}${P.deltaPct.toFixed(1)}% self-time (${(P.baseSelfUs/1000).toFixed(2)}ms \u2192 ${(P.candSelfUs/1000).toFixed(2)}ms)`)}for(let P of p.heapTypes?.slice(0,Ce)??[]){if(Math.abs(P.deltaPct)<0.5)continue;let U=P.deltaPct>0?"+":"";c.push(`- heap \`${P.type}\`: ${U}${P.deltaPct.toFixed(1)}% count (${P.baseCount} \u2192 ${P.candCount})`)}c.push("")}}return{text:c.join(`
|
|
10
|
+
`)}}};var Ze=15;function le(o){return`n${o}`}function en(o,D,c){return`${(o||"(anonymous)").replace(/"/g,"'")} (self ${(D/1000).toFixed(2)}ms, total ${(c/1000).toFixed(2)}ms)`}function nn(o,D,c,C){let p=[];if(C<=0)return p;for(let I=0;I<D;I++){if(I===c)continue;let N=o[I];if(p.length===C&&N<=o[p[C-1]])continue;let P=p.length;while(P>0&&o[p[P-1]]<N)P--;if(p.splice(P,0,I),p.length>C)p.pop()}return p}var Ue={name:"mermaid",async render(o,D={}){let c=D.topN??Ze;return{files:Z(o,D.runId).map((I)=>{let N=I.cpu,{nodes:P,frames:U}=N,B=re(N),{selfUs:M,totalUs:S}=Pe(N,B),{parentIx:E}=B,V=B.roots[0]??-1,z=nn(M,B.count,V,c),A=new Set(V!==-1?[V]:[]),H=[];for(let J of z){H.length=0;for(let j=J;j!==-1;j=E[j])H.push(j);for(let j=H.length-1;j>=0;j--)A.add(H[j])}let _=["graph TD"];for(let J of A){let j=P[J].id;_.push(` ${le(j)}["${en(U[P[J].frameIx].name,M[J],S[J])}"]`)}for(let J of A){let j=E[J];if(j!==-1&&A.has(j))_.push(` ${le(P[j].id)} --> ${le(P[J].id)}`)}return{path:`${I.id}.mermaid.md`,content:`${_.join(`
|
|
11
|
+
`)}
|
|
12
|
+
`}})}}};function De(o){if(!o?.entry)return;if(o.entry.group!==void 0)return o.entry.group;let D=o.entry.task,c=D.lastIndexOf("/");return c===-1?void 0:D.slice(0,c)}function ce(o){let D=Math.min(...o.map((p)=>p.run.timing.median)),c=new Map;for(let p of o){let I=De(p.workload);if(I===void 0)continue;let N=c.get(I);if(N)N.push(p);else c.set(I,[p])}let C=new Map;for(let p of o){let I=De(p.workload);if(I===void 0){C.set(p,D);continue}let N=c.get(I)??[p],P=N.find((U)=>U.workload?.baseline);C.set(p,P?P.run.timing.median:Math.min(...N.map((U)=>U.run.timing.median)))}return C}function Q(o){return Number.isFinite(o)?Number(o.toPrecision(6)):o}function tn(o,D){return o?.entry?.task??o?.label??o?.command?.join(" ")??D.workloadId}function rn(o){let D=new Map(o.workloads.map((I)=>[I.id,I])),c=o.runs.filter((I)=>I.phase==="timing"&&I.timing!==void 0).map((I)=>({run:I,workload:D.get(I.workloadId)})),C=c.length>1?ce(c):void 0,p=new Map((o.comparisons??[]).map((I)=>[I.candidateRunId,I]));return c.map((I)=>{let{run:N,workload:P}=I,U=N.timing,B={task:tn(P,N),unit:"ns",samples:U.samples.length,mean:Q(U.mean),median:Q(U.median),stddev:Q(U.stddev),stddevPct:Q(U.mean===0?0:U.stddev/U.mean*100),min:Q(U.min),max:Q(U.max),warnings:N.warnings.map((S)=>S.data?{code:S.code,data:S.data}:{code:S.code})};if(P?.entry?.group!==void 0)B.group=P.entry.group;if(P?.description!==void 0)B.description=P.description;if(P?.groupDescription!==void 0)B.groupDescription=P.groupDescription;if(C)B.relative=Q(U.median/(C.get(I)??U.median));if(P?.baseline)B.baseline=!0;let M=p.get(N.id);if(M?.timing)B.delta={medianPct:Q(M.timing.medianDeltaPct),meanPct:Q(M.timing.meanDeltaPct),verdict:M.timing.verdict,pass:M.verdict==="pass"};return B})}var Fe={name:"minimal",async render(o){let D=rn(o).map((c)=>JSON.stringify(c));return{text:D.length>0?`${D.join(`
|
|
13
|
+
`)}
|
|
14
|
+
`:""}}};var sn="https://www.speedscope.app/file-format-schema.json";function Me(o){return o?.label??o?.command?.join(" ")??o?.entry?.task??"profile"}var Se={name:"speedscope",async render(o,D={}){let c=Z(o,D.runId),C=new Map(o.workloads.map((I)=>[I.id,I]));return{files:c.map((I)=>{let N=I.cpu,{nodes:P}=N,U=re(N),B=N.samples?.nodeIds??[],M=N.samples?.timeDeltasUs??[],S=Array(U.count);for(let A of U.order){let H=U.parentIx[A],_=P[A].frameIx;S[A]=H===-1?[_]:[...S[H],_]}let E=Array(B.length);for(let A=0;A<B.length;A++){let H=U.indexOf(B[A]);E[A]=H===-1?[]:S[H]}let V=0;for(let A=0;A<M.length;A++)V+=M[A];let z={$schema:sn,exporter:"ostia",name:Me(C.get(I.workloadId)),activeProfileIndex:0,shared:{frames:N.frames.map((A)=>({name:A.name||"(anonymous)",file:A.url,line:A.line!==void 0?A.line+1:void 0}))},profiles:[{type:"sampled",name:Me(C.get(I.workloadId)),unit:"microseconds",startValue:0,endValue:V,samples:E,weights:M}]};return{path:`${I.id}.speedscope.json`,content:`${JSON.stringify(z,null,2)}
|
|
15
|
+
`}})}}};function se(o){return(o/1e6).toFixed(3)}function pe(o){return o.label??o.command?.join(" ")??o.entry?.task??o.id}var Oe={name:"table",async render(o){let D=o.runs.filter((S)=>S.phase==="timing"&&S.timing!==void 0),c=new Map(o.workloads.map((S)=>[S.id,S]));if(D.length===0){let S=Be(o,c);return{text:S.length>0?`${S.join(`
|
|
16
|
+
`)}
|
|
17
|
+
`:`(no timing runs)
|
|
18
|
+
`}}let C=D.map((S)=>{let E=c.get(S.workloadId);return{run:S,workload:E,label:E?pe(E):S.workloadId}}),p=C.length>1,I=ce(C),N=[],P=Math.max(7,...C.map((S)=>S.label.length)),U=p?`${"Command".padEnd(P)} Mean [ms] Min\u2026Max [ms] Relative`:`${"Command".padEnd(P)} Mean [ms] Min\u2026Max [ms]`;N.push(U),N.push("-".repeat(U.length));for(let S of C){let{run:E,label:V,workload:z}=S,A=E.timing,H=`${se(A.mean)} \xB1 ${se(A.stddev)}`,_=`${se(A.min)}\u2026${se(A.max)}`,J=`${V.padEnd(P)} ${H.padEnd(15)} ${_.padEnd(18)}`;if(p){let j=A.median/(I.get(S)??A.median);if(j===1)J+=z?.baseline?" 1.00\xD7 (baseline)":" 1.00\xD7";else if(j>1)J+=` ${j.toFixed(2)}\xD7 slower`;else J+=` ${(1/j).toFixed(2)}\xD7 faster`}N.push(J);for(let j of E.warnings)N.push(` ! ${j.message}`)}let B=an(o,c);if(B.length>0)N.push(""),N.push(...B);let M=Be(o,c);if(M.length>0)N.push(""),N.push(...M);return{text:`${N.join(`
|
|
19
|
+
`)}
|
|
20
|
+
`}}};function on(o,D,c){let C=o.runs.find((I)=>I.id===c),p=C?D.get(C.workloadId):void 0;return p?pe(p):c}function Be(o,D){if(!o.comparisons||o.comparisons.length===0)return[];let c=[];for(let C of o.comparisons){let p=on(o,D,C.candidateRunId),I=C.verdict==="pass"?"\u2713":"\u2717";if(c.push(`${I} ${p}`),C.timing){let N=C.timing.medianDeltaPct>0?"+":"";c.push(` timing: ${N}${C.timing.medianDeltaPct.toFixed(1)}% median (${C.timing.verdict})`)}if(C.frames)for(let N of C.frames.slice(0,We)){if(Math.abs(N.deltaPct)<0.5)continue;let P=N.deltaPct>0?"+":"";c.push(` frame ${N.name}: ${P}${N.deltaPct.toFixed(1)}% self-time (${(N.baseSelfUs/1000).toFixed(2)}ms -> ${(N.candSelfUs/1000).toFixed(2)}ms)`)}if(C.heapTypes)for(let N of C.heapTypes.slice(0,Ee)){if(Math.abs(N.deltaPct)<0.5)continue;let P=N.deltaPct>0?"+":"";c.push(` heap ${N.type}: ${P}${N.deltaPct.toFixed(1)}% count (${N.baseCount} -> ${N.candCount})`)}}return c}var We=5,Ee=5;function an(o,D){let c=[];for(let C of o.runs){if(C.phase!=="cpu"&&C.phase!=="heap")continue;let p=D.get(C.workloadId),I=p?pe(p):C.workloadId;if(C.phase==="cpu")if(C.cpu){c.push(`CPU capture - ${I} (instrumented, ${C.cpu.samplingIntervalUs}\xB5s interval, diagnostic wall ${se(C.diagnosticWallNs??0)}ms)`);let N=C.cpu.totals.reduce((P,U)=>P+U.selfUs,0)||1;for(let P of C.cpu.totals.slice(0,We)){let U=C.cpu.frames[P.frameIx],B=(P.selfUs/N*100).toFixed(1);c.push(` ${B.padStart(5)}% ${(P.selfUs/1000).toFixed(2).padStart(8)}ms self ${U?.name??"?"}`)}}else c.push(`CPU capture - ${I} (instrumented, no evidence captured)`);else if(C.heap){let N=((C.heap.heapSizeBytes??0)/1e6).toFixed(2);c.push(`Heap snapshot - ${I} (instrumented, ${C.heap.objectCount??"?"} objects, ${N}MB)`);for(let P of C.heap.typeCounts.slice(0,Ee))c.push(` ${String(P.count).padStart(6)} ${P.type}`)}else c.push(`Heap snapshot - ${I} (instrumented, no evidence captured)`);for(let N of C.artifacts)c.push(` artifact: ${N.path}`);for(let N of C.warnings)c.push(` ! ${N.message}`)}return c}var n={table:Oe,json:Ie,markdown:Ne,jsonl:$e,minimal:Fe,collapsed:ke,mermaid:Ue,speedscope:Se,cpuprofile:Te};var cn="node_modules/.cache/ostia",me=1000;async function y(o){let D=s({runs:o.runs??null,warmup:o.warmup??null,cpu:o.cpu??!1,heap:o.heap??!1,cpuIntervalUs:o.cpuIntervalUs??me}),C=`${o.outDir??cn}/artifacts`,p=[],I=[];for(let N of o.commands){let P=Array.isArray(N)?N:xe(N),U=u(P,Array.isArray(N)?void 0:N);p.push(U);let B=await g({argv:P,cwd:o.cwd,env:o.env,runs:o.runs,warmup:o.warmup}),M=i({workload:U,configFingerprint:D,trials:B.trials,timing:B.timing,warnings:B.warnings});if(I.push(M),o.cpu){let S=`${M.id}-cpu.cpuprofile`,E=await de({argv:P,cwd:o.cwd,env:o.env,artifactDir:C,fileName:S,intervalUs:o.cpuIntervalUs??me});I.push(await Ae({workload:U,phase:"cpu",configFingerprint:D,diagnosticWallNs:E.diagnosticWallNs,exitCode:E.exitCode,cpu:E.cpu,artifactPath:E.artifactPath,artifactKind:"cpuprofile",warnings:E.warnings}))}if(o.heap){let S=`${M.id}-heap.heapsnapshot`,E=await ge({argv:P,cwd:o.cwd,env:o.env,artifactDir:C,fileName:S});I.push(await Ae({workload:U,phase:"heap",configFingerprint:D,diagnosticWallNs:E.diagnosticWallNs,exitCode:E.exitCode,heap:E.heap,artifactPath:E.artifactPath,artifactKind:"heapsnapshot",warnings:E.warnings}))}}return r(p,I)}async function Ae(o){let D=`${o.workload.id}-${o.phase}-${o.configFingerprint}`,c=o.artifactPath?[await T(D,o.artifactKind,o.artifactPath)]:[];return b({workload:o.workload,phase:o.phase,configFingerprint:o.configFingerprint,diagnosticWallNs:o.diagnosticWallNs,exitCode:o.exitCode,cpu:o.cpu,heap:o.heap,warnings:o.warnings,artifacts:c})}async function v(o,D={}){let c=R(o),C=s({intervalUs:D.intervalUs??me,origin:D.origin??"inspector"}),p=(B)=>B.samples?.nodeIds.length===0?[{code:"empty-profile",message:"In-process capture produced zero samples."}]:[];if(D.origin==="jsc"){let{result:B,cpu:M,jit:S,diagnosticWallNs:E}=await we(o,D),V=b({workload:c,phase:"cpu",configFingerprint:C,diagnosticWallNs:E,cpu:M,jit:S,warnings:p(M),artifacts:[]});return{result:B,run:V}}let{result:I,cpu:N,diagnosticWallNs:P}=await he(o,D),U=b({workload:c,phase:"cpu",configFingerprint:C,diagnosticWallNs:P,cpu:N,warnings:p(N),artifacts:[]});return{result:I,run:U}}
|
|
21
|
+
export{x,d,a,f,w,g,n,y,v};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
function h(n){return JSON.stringify(O(n))}function O(n){if(Array.isArray(n))return n.map(O);if(n!==null&&typeof n==="object"){let a={};for(let d of Object.keys(n).sort())a[d]=O(n[d]);return a}return n}function e(n,...a){let d=Bun.CryptoHasher.hash("sha256",h(a),"hex");return`${n}_${d.slice(0,16)}`}var c="0.1.0";function r(n,a){return{schemaVersion:1,toolVersion:c,bunVersion:Bun.version,platform:{os:process.platform,arch:process.arch},createdAt:new Date().toISOString(),workloads:n,runs:a}}function u(n,a){return{id:e("wl","subprocess",n,process.cwd()),kind:"subprocess",command:n,label:a}}function R(n,a){return{id:e("wl","inprocess",n.name,n.toString()),kind:"inprocess",label:a}}function P(n,a,d={}){return{id:e("wl","inprocess-entry",n,a),kind:"inprocess",entry:{file:n,task:a,...d.group!==void 0&&{group:d.group}},...d.label!==void 0&&{label:d.label},...d.baseline!==void 0&&{baseline:d.baseline},...d.description!==void 0&&{description:d.description},...d.groupDescription!==void 0&&{groupDescription:d.groupDescription},...d.isolated!==void 0&&{isolated:d.isolated}}}function i(n){return{id:e("run",n.workload.id,"timing",n.configFingerprint,Bun.version,c),workloadId:n.workload.id,phase:"timing",instrumented:!1,configFingerprint:n.configFingerprint,trials:n.trials,timing:n.timing,warnings:n.warnings,artifacts:[],memory:Z(n.trials)}}function Z(n){let a=n.map((d)=>d.maxRssBytes).filter((d)=>d!==void 0);if(a.length===0)return;return{origin:"resourceUsage",perTrial:n.map((d)=>({rssBytes:d.maxRssBytes})),maxRssBytes:Math.max(...a)}}function b(n){return{id:e("run",n.workload.id,n.phase,n.configFingerprint,Bun.version,c),workloadId:n.workload.id,phase:n.phase,instrumented:!0,configFingerprint:n.configFingerprint,trials:[{i:0,wallNs:n.diagnosticWallNs,exitCode:n.exitCode}],diagnosticWallNs:n.diagnosticWallNs,cpu:n.cpu,heap:n.heap,jit:n.jit,warnings:n.warnings,artifacts:n.artifacts}}async function T(n,a,d){let f=await Bun.file(d).arrayBuffer(),w=new Bun.CryptoHasher("sha256");return w.update(f),{id:e("art",n,a,d),kind:a,path:d,sha256:w.digest("hex"),bytes:f.byteLength}}function s(n){return e("cfg",n)}function k(n){return`${JSON.stringify(O(n),null,2)}
|
|
3
|
+
`}async function o(n,a){await Bun.write(a,k(n))}async function t(n){let a=await Bun.file(n).text();return JSON.parse(a)}var H=[],v;function F(n,a,d){let g=v;v={name:n,description:d?.description,isolate:d?.isolate};try{a()}finally{v=g}}function S(n,a,d){H.push({groupName:v?.name,groupDescription:v?.description,groupIsolate:v?.isolate,name:n,fn:a,baseline:d?.baseline,opts:d})}function I(){return H}function C(){H.length=0,v=void 0}function p(n){return n.groupName?`${n.groupName}/${n.name}`:n.name}function N(n,a){return n.opts?.isolate??n.groupIsolate??a}function D(n,a){if(!a)return[...n];let d=new RegExp(a);return n.filter((g)=>d.test(p(g)))}function l(n){if(n.length===0)throw Error("computeTimingStats: samples must be non-empty");let a=n.length,d=z(n),g=0;for(let y=0;y<a;y++)g+=n[y];let f=g/a,w=B(d,0.5),x=0;for(let y=0;y<a;y++){let W=n[y]-f;x+=W*W}let A=Math.sqrt(x/a),J=d[0],E=d[a-1],q=B(d,0.25),_=B(d,0.75),M=_-q,V=q-1.5*M,G=_+1.5*M,K=q-3*M,U=_+3*M,j=0,L=0;for(let y=0;y<a;y++){let W=n[y];if(W<K||W>U)L++;else if(W<V||W>G)j++}return{unit:"ns",samples:n,mean:f,median:w,stddev:A,min:J,max:E,outliers:{mild:j,severe:L}}}function z(n){let a=new Float64Array(n.length);return a.set(n),a.sort(),a}function B(n,a){let d=n.length;if(d===1)return n[0];let g=a*(d-1),f=Math.floor(g),w=Math.ceil(g);if(f===w)return n[f];let x=g-f;return n[f]*(1-x)+n[w]*x}var Q=5000000,X=200;function m(n,a,d="subprocess"){let g=[],f=n.samples[0];if(f!==void 0){let x=z(n.samples),A=B(x,0.25),E=B(x,0.75)-A;if(f>n.median+3*E&&E>0)g.push({code:"slow-first-run",message:`First run took ${(f/1e6).toFixed(2)}ms, much slower than the median ${(n.median/1e6).toFixed(2)}ms. Consider more warmup.`,data:{firstNs:f,medianNs:n.median}})}if(n.outliers.mild+n.outliers.severe>0)g.push({code:"outliers-detected",message:`${n.outliers.mild+n.outliers.severe} outlier(s) detected (${n.outliers.severe} severe, ${n.outliers.mild} mild).`,data:n.outliers});if(d==="subprocess"&&n.median<Q)g.push({code:"fast-command",message:`Median run time (${(n.median/1e6).toFixed(3)}ms) is very fast; results may be dominated by spawn overhead.`,data:{medianNs:n.median}});if(d==="inprocess"&&n.median<X)g.push({code:"below-timer-resolution",message:`Median run time (${n.median.toFixed(0)}ns) is close to timer resolution; consider a larger batch size or a coarser operation.`,data:{medianNs:n.median}});let w=a.filter((x)=>x!==void 0&&x!==0);if(w.length>0)g.push({code:"nonzero-exit",message:`${w.length} of ${a.length} trial(s) exited non-zero.`,data:{exitCodes:w}});return g}
|
|
4
|
+
export{h,e,c,r,u,R,P,i,b,T,s,k,o,t,l,m,F,S,I,C,p,N,D};
|
package/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
import{
|
|
4
|
-
`)}var
|
|
3
|
+
import{x,d,a,f,w,g,n,y}from"./chunk-gsjgejr1.js";import{e,c,r,u,i,s,o,t}from"./chunk-y1gkhb0y.js";function q(p){return e("cache",p.workloadId,p.phase,p.configFingerprint,p.bunVersion,p.toolVersion,p.instrumented,p.inputsDigest??null)}async function _(p,l=process.cwd()){if(p.length===0)return;let m=new Set;for(let b of p){let P=new Bun.Glob(b);for await(let j of P.scan({cwd:l,absolute:!1}))m.add(j)}let k=[...m].sort(),h=await Promise.all(k.map(async(b)=>{let P=await Bun.file(`${l}/${b}`).arrayBuffer();return{path:b,sha256:Bun.CryptoHasher.hash("sha256",P,"hex")}}));return e("inputs",h)}function L(p,l){return`${p}/cache/${l}.json`}async function M(p,l){let m=Bun.file(L(p,l));if(!await m.exists())return;return await m.json()}async function W(p,l,m){await Bun.write(L(p,l),`${JSON.stringify(m,null,2)}
|
|
4
|
+
`)}var X="node_modules/.cache/ostia",Q=".ostia/baselines",Y={runs:null,warmup:3,outDir:X,baselineDir:Q,baseline:"main",cpuIntervalUs:1000,thresholds:a,workloads:[]};async function V(p="ostia.config.json"){let l=Bun.file(p);if(!await l.exists())return;let m=await l.json();return{...Y,...m,thresholds:{...a,...m.thresholds??{}}}}function z(p,l){return`${p.baselineDir}/${l??p.baseline}.json`}class T extends Error{path;constructor(p){super(`No baseline document at ${p}. Create one with: ostia run --export-json ${p} <command...>`);this.path=p}}async function K(p){let{config:l}=p,m=z(l,p.baselineName);if(!await Bun.file(m).exists())throw new T(m);let h=await t(m),b=[],P=0,j=0,D=0;for(let C of l.workloads){let A=u(C.command,C.label),v=await _(C.inputs??[]),U=s({runs:l.runs,warmup:l.warmup}),H=q({workloadId:A.id,phase:"timing",configFingerprint:U,bunVersion:Bun.version,toolVersion:c,instrumented:!1,inputsDigest:v}),J=p.full?void 0:await M(l.outDir,H),S,B;if(J)S=J,B="cached",j++;else{P++;let O=await g({argv:C.command,runs:l.runs??void 0,warmup:l.warmup});S=i({workload:A,configFingerprint:U,trials:O.trials,timing:O.timing,warnings:O.warnings}),await W(l.outDir,H,S),B="executed",D++}b.push({workload:A,status:B,run:S})}let R=r(b.map((C)=>C.workload),b.map((C)=>C.run)),N=0,E=0,F=0;for(let C of b){let A=w(h,R,C.workload.id,l.thresholds);if(!A){F++;continue}if(C.comparison=A,A.verdict==="pass")N++;else E++}return R.comparisons=b.map((C)=>C.comparison).filter((C)=>C!==void 0),{document:R,summary:{total:l.workloads.length,affected:P,cached:j,executed:D,passed:N,regressed:E,missingBaseline:F,results:b}}}function G(p){let l=[];if(l.push(`${p.total} workloads`),l.push(`${p.affected} affected by this change`),l.push(`${p.cached} cached`),l.push(`${p.executed} executed`),p.missingBaseline>0)l.push(`${p.missingBaseline} skipped (no matching baseline workload)`);let m=p.results.filter((k)=>k.comparison?.verdict==="fail").map((k)=>{let h=k.comparison.timing,b=k.workload.label??k.workload.command?.join(" ")??k.workload.id;return h?`${h.medianDeltaPct>0?"+":""}${h.medianDeltaPct.toFixed(1)}% median on ${b}`:b});return l.push(`${p.passed} passed ${p.regressed} regressed${m.length>0?` (${m.join(", ")})`:""}`),l.push(""),l.push(`Profile CI: ${p.regressed>0?"\u2717":"\u2713"}`),`${l.join(`
|
|
5
5
|
`)}
|
|
6
|
-
`}async function
|
|
7
|
-
`)}else if(p.files.length===1)process.stdout.write(p.files[0].content);else for(let
|
|
8
|
-
${
|
|
9
|
-
`)}var
|
|
6
|
+
`}async function I(p,l){if(p.text)process.stdout.write(p.text);if(!p.files||p.files.length===0)return;if(l)for(let m of p.files){let k=m.path?`${l}/${m.path}`:l;await Bun.write(k,m.content),process.stdout.write(`wrote ${k}
|
|
7
|
+
`)}else if(p.files.length===1)process.stdout.write(p.files[0].content);else for(let m of p.files)process.stdout.write(`--- ${m.path??"(unnamed)"} ---
|
|
8
|
+
${m.content}
|
|
9
|
+
`)}var ee=`ostia run [flags] <command...>
|
|
10
10
|
|
|
11
11
|
Run one or more commands N times with warmup and report timing statistics.
|
|
12
12
|
|
|
@@ -18,7 +18,7 @@ Flags:
|
|
|
18
18
|
--cpu-interval USEC CPU sampling interval in microseconds (default: 1000)
|
|
19
19
|
--out-dir PATH directory for captured artifacts (default: node_modules/.cache/ostia)
|
|
20
20
|
--export-json PATH write the full ProfileDocument to PATH
|
|
21
|
-
--format FORMAT table | json (default: table)
|
|
21
|
+
--format FORMAT table | json | jsonl | markdown | minimal (default: table)
|
|
22
22
|
--quiet suppress the rendered report (still writes --export-json)
|
|
23
23
|
--help show this message
|
|
24
24
|
|
|
@@ -30,7 +30,7 @@ Examples:
|
|
|
30
30
|
ostia run --runs 25 --warmup 3 "bun a.ts" "bun b.ts"
|
|
31
31
|
ostia run --cpu --heap "bun src/server.ts"
|
|
32
32
|
ostia run --format json "bun a.ts"
|
|
33
|
-
`,
|
|
33
|
+
`,te=`ostia bench [flags] <suite.ts...>
|
|
34
34
|
|
|
35
35
|
Run in-process benchmark suites (registered via group()/task()). Each suite file runs
|
|
36
36
|
in its own spawned child process (isolated from CLI startup state).
|
|
@@ -38,14 +38,34 @@ in its own spawned child process (isolated from CLI startup state).
|
|
|
38
38
|
Flags:
|
|
39
39
|
--time-budget MS sampling budget per task; always runs at least this long (default: 500)
|
|
40
40
|
--min-samples N hard floor on samples per task, kept even when it overruns the
|
|
41
|
-
budget. Default: cost-aware - as many as fit in the budget,
|
|
42
|
-
|
|
41
|
+
budget. Default: cost-aware - as many as fit in the budget (max 20),
|
|
42
|
+
but never below the floor the task's per-trial cost earns it: 3 at
|
|
43
|
+
<=1ms, +2 per decade of cost, 10 from ~3s up. Cheap tasks are
|
|
44
|
+
time-bound and collect thousands either way; only the few expensive
|
|
45
|
+
tasks in a suite pay for the extra rigor. A run that ends below its
|
|
46
|
+
cost-class floor (only possible with an explicit --min-samples or
|
|
47
|
+
per-task minSamples) carries a "low-sample-count" warning.
|
|
48
|
+
--jobs N|auto suite files to run at once, each still in its own process (default: 1).
|
|
49
|
+
Concurrent CPU-bound processes contend for cores, caches and turbo
|
|
50
|
+
headroom, so numbers taken at --jobs > 1 are noisier and not
|
|
51
|
+
like-for-like with a baseline measured at 1. "auto" = CPU count.
|
|
43
52
|
--gc Bun.gc(true) between trials (default: off - hides allocation cost)
|
|
44
53
|
--filter REGEX only run tasks whose "group/name" id matches this regex (substring,
|
|
45
54
|
case-sensitive; unmatched tasks are skipped, not timed)
|
|
55
|
+
--isolate give every task its own subprocess instead of sharing its suite
|
|
56
|
+
file's, isolating JIT tier state and heap shape between tasks the
|
|
57
|
+
way suite files are already isolated from each other. Per-task
|
|
58
|
+
{ isolate } / per-group { isolate } override this default.
|
|
59
|
+
--jobs then pools across those per-task processes, so pair a
|
|
60
|
+
higher --jobs with --isolate deliberately: overhead now scales
|
|
61
|
+
with task count, not file count.
|
|
46
62
|
--out-dir PATH directory for scratch IPC files (default: node_modules/.cache/ostia)
|
|
47
63
|
--export-json PATH write the full ProfileDocument to PATH
|
|
48
|
-
--format FORMAT table | json (default: table)
|
|
64
|
+
--format FORMAT table | json | jsonl | markdown | minimal (default: table)
|
|
65
|
+
"minimal" is one compact JSON object per task with no raw sample
|
|
66
|
+
array: {task, group, description, samples, mean, median, stddevPct,
|
|
67
|
+
relative, warnings[{code,data}]} in ns - built to pipe into an LLM
|
|
68
|
+
agent's context.
|
|
49
69
|
--quiet suppress the rendered report (still writes --export-json)
|
|
50
70
|
--help show this message
|
|
51
71
|
|
|
@@ -54,31 +74,35 @@ Suite files register tasks like:
|
|
|
54
74
|
group("parse", () => {
|
|
55
75
|
task("small input", () => parse(smallBuf))
|
|
56
76
|
task("full pipeline", () => build(), { timeBudgetMs: 2000, minSamples: 10 })
|
|
57
|
-
})
|
|
77
|
+
}, { description: "parser throughput on representative inputs" })
|
|
58
78
|
Per-task options override --time-budget / --min-samples for that task only.
|
|
79
|
+
Optional { description } on group() and task() flows into the document (Workload.description
|
|
80
|
+
/ Workload.groupDescription) so the intent travels with the numbers.
|
|
59
81
|
|
|
60
82
|
Examples:
|
|
61
83
|
ostia bench benches/parse.ts
|
|
62
84
|
ostia bench --time-budget 1000 --min-samples 50 benches/*.ts
|
|
63
85
|
ostia bench benches/*.ts --filter parse
|
|
64
|
-
|
|
86
|
+
ostia bench benches/*.ts --jobs auto --format minimal
|
|
87
|
+
`,Z=`ostia compare <base.json> <candidate.json>
|
|
65
88
|
ostia compare <candidate.json> --baseline <path.json>
|
|
66
89
|
|
|
67
90
|
Compare two ProfileDocuments (matched by workload id) and rank timing/frame/heap deltas.
|
|
68
91
|
|
|
69
92
|
Flags:
|
|
70
93
|
--export-json PATH write the resulting document (with comparisons) to PATH
|
|
71
|
-
--format FORMAT table | json (default: table)
|
|
94
|
+
--format FORMAT table | json | jsonl | markdown | minimal (default: table)
|
|
95
|
+
"minimal" adds delta: {medianPct, verdict, pass} to each task line
|
|
72
96
|
--quiet suppress the rendered report (still writes --export-json)
|
|
73
97
|
--help show this message
|
|
74
98
|
|
|
75
99
|
Examples:
|
|
76
100
|
ostia compare before.json after.json
|
|
77
101
|
ostia compare after.json --baseline .ostia/baselines/main.json
|
|
78
|
-
`,
|
|
102
|
+
`,re=`ostia report <document.json> [--format table|json|markdown|jsonl|minimal]
|
|
79
103
|
|
|
80
104
|
Render a saved ProfileDocument.
|
|
81
|
-
`,
|
|
105
|
+
`,se=`ostia viz <document.json> --format FORMAT [--run <id>] [--out-dir PATH]
|
|
82
106
|
|
|
83
107
|
Render CPU evidence from a saved ProfileDocument as a visualization artifact. Files,
|
|
84
108
|
not a GUI - hand the output to speedscope.app, flamegraph.pl, or
|
|
@@ -99,7 +123,7 @@ Flags:
|
|
|
99
123
|
Examples:
|
|
100
124
|
ostia viz run.json --format speedscope --out-dir node_modules/.cache/ostia/viz
|
|
101
125
|
ostia viz run.json --format collapsed | flamegraph.pl > flame.svg
|
|
102
|
-
`,
|
|
126
|
+
`,ne=`ostia ci [--full] [--baseline NAME]
|
|
103
127
|
|
|
104
128
|
Load ostia.config.json, run configured workloads (reusing cached results when their
|
|
105
129
|
fingerprint is unchanged), compare against the named baseline, and gate on regressions.
|
|
@@ -112,21 +136,22 @@ Flags:
|
|
|
112
136
|
--help show this message
|
|
113
137
|
|
|
114
138
|
Exit codes: 0 pass, 1 regression, 2 harness error (missing config/baseline, spawn failure).
|
|
115
|
-
`;function
|
|
116
|
-
`),2;let
|
|
117
|
-
`),2}if(l.exportJson)await o(
|
|
118
|
-
`),2;
|
|
119
|
-
`),2
|
|
120
|
-
`),2}
|
|
121
|
-
`),2}let
|
|
122
|
-
`),2;let h;
|
|
123
|
-
`),2
|
|
139
|
+
`;function oe(p){let l=[],m,k,h=!1,b=!1,P,j,D,R="table",N=!1,E=!1;for(let F=0;F<p.length;F++){let C=p[F];switch(C){case"--runs":m=Number(p[++F]);break;case"--warmup":k=Number(p[++F]);break;case"--cpu":h=!0;break;case"--heap":b=!0;break;case"--cpu-interval":P=Number(p[++F]);break;case"--out-dir":j=p[++F];break;case"--export-json":D=p[++F];break;case"--format":R=p[++F];break;case"--quiet":N=!0;break;case"--help":case"-h":E=!0;break;default:l.push(C)}}return{commands:l,runs:m,warmup:k,cpu:h,heap:b,cpuIntervalUs:P,outDir:j,exportJson:D,format:R,quiet:N,help:E}}async function ae(p){let l=oe(p);if(l.help||l.commands.length===0)return process.stdout.write(ee),l.help?0:2;if(!(l.format in n))return process.stderr.write(`Unknown --format "${l.format}". Expected one of: ${Object.keys(n).join(", ")}
|
|
140
|
+
`),2;let m;try{m=await y({commands:l.commands,runs:l.runs,warmup:l.warmup,cpu:l.cpu,heap:l.heap,cpuIntervalUs:l.cpuIntervalUs,outDir:l.outDir})}catch(h){return process.stderr.write(`Run failed: ${h instanceof Error?h.message:String(h)}
|
|
141
|
+
`),2}if(l.exportJson)await o(m,l.exportJson);if(!l.quiet){let b=await n[l.format].render(m,{});await I(b)}return m.runs.some((h)=>h.trials.some((b)=>b.exitCode!==void 0&&b.exitCode!==0))?1:0}function ie(p){let l=[],m,k,h,b=!1,P,j=!1,D,R,N="table",E=!1,F=!1;for(let C=0;C<p.length;C++){let A=p[C];switch(A){case"--time-budget":m=Number(p[++C]);break;case"--min-samples":k=Number(p[++C]);break;case"--jobs":{let v=p[++C];h=v==="auto"?x():Number(v);break}case"--gc":b=!0;break;case"--filter":P=p[++C];break;case"--isolate":j=!0;break;case"--out-dir":D=p[++C];break;case"--export-json":R=p[++C];break;case"--format":N=p[++C];break;case"--quiet":E=!0;break;case"--help":case"-h":F=!0;break;default:l.push(A)}}return{suites:l,timeBudgetMs:m,minSamples:k,jobs:h,gc:b,filter:P,isolate:j,outDir:D,exportJson:R,format:N,quiet:E,help:F}}async function ue(p){let l=ie(p);if(l.help||l.suites.length===0)return process.stdout.write(te),l.help?0:2;if(!(l.format in n))return process.stderr.write(`Unknown --format "${l.format}". Expected one of: ${Object.keys(n).join(", ")}
|
|
142
|
+
`),2;if(l.jobs!==void 0&&!(l.jobs>=1))return process.stderr.write(`--jobs expects a positive integer or "auto".
|
|
143
|
+
`),2;let m;try{m=await d({suites:l.suites,timeBudgetMs:l.timeBudgetMs,minSamples:l.minSamples,jobs:l.jobs,gc:l.gc,filter:l.filter,isolate:l.isolate,outDir:l.outDir})}catch(k){return process.stderr.write(`Bench failed: ${k instanceof Error?k.message:String(k)}
|
|
144
|
+
`),2}if(l.exportJson)await o(m,l.exportJson);if(!l.quiet){let h=await n[l.format].render(m,{});await I(h)}return 0}function le(p){let l=[],m,k,h="table",b=!1,P=!1;for(let j=0;j<p.length;j++){let D=p[j];switch(D){case"--baseline":m=p[++j];break;case"--export-json":k=p[++j];break;case"--format":h=p[++j];break;case"--quiet":b=!0;break;case"--help":case"-h":P=!0;break;default:l.push(D)}}return{paths:l,baseline:m,exportJson:k,format:h,quiet:b,help:P}}async function ce(p){let l=le(p);if(l.help)return process.stdout.write(Z),0;let m,k;if(l.baseline)m=l.baseline,k=l.paths[0];else m=l.paths[0],k=l.paths[1];if(!m||!k)return process.stdout.write(Z),2;let h,b;try{[h,b]=await Promise.all([t(m),t(k)])}catch(R){return process.stderr.write(`Failed to load documents: ${R instanceof Error?R.message:String(R)}
|
|
145
|
+
`),2}let P=f(h,b),j={...b,comparisons:P};if(l.exportJson)await o(j,l.exportJson);if(!l.quiet){let N=await n[l.format].render(j,{});await I(N)}return P.some((R)=>R.verdict==="fail")?1:0}function de(p){let l,m="table",k=!1;for(let h=0;h<p.length;h++){let b=p[h];switch(b){case"--format":m=p[++h];break;case"--help":case"-h":k=!0;break;default:l=b}}return{path:l,format:m,help:k}}async function pe(p){let l=de(p);if(l.help||!l.path)return process.stdout.write(re),l.help?0:2;let m;try{m=await t(l.path)}catch(b){return process.stderr.write(`Failed to load ${l.path}: ${b instanceof Error?b.message:String(b)}
|
|
146
|
+
`),2}let h=await n[l.format].render(m,{});return await I(h),0}var me={ascii:"table"};function fe(p){let l,m,k,h,b=!1;for(let P=0;P<p.length;P++){let j=p[P];switch(j){case"--format":{let D=p[++P]??"";m=me[D]??D;break}case"--run":k=p[++P];break;case"--out-dir":h=p[++P];break;case"--help":case"-h":b=!0;break;default:l=j}}return{path:l,format:m,runId:k,outDir:h,help:b}}async function he(p){let l=fe(p);if(l.help||!l.path||!l.format)return process.stdout.write(se),l.help?0:2;if(!(l.format in n))return process.stderr.write(`Unknown --format "${l.format}". Expected one of: ${Object.keys(n).join(", ")}, ascii
|
|
147
|
+
`),2;let m;try{m=await t(l.path)}catch(b){return process.stderr.write(`Failed to load ${l.path}: ${b instanceof Error?b.message:String(b)}
|
|
148
|
+
`),2}let h=await n[l.format].render(m,{runId:l.runId});if(!h.text&&(!h.files||h.files.length===0))return process.stderr.write(l.runId?`No CPU evidence found for run "${l.runId}".
|
|
124
149
|
`:`No CPU evidence found in this document (no cpu-phase runs). Capture some with "ostia run --cpu ...".
|
|
125
|
-
`),2;return await
|
|
126
|
-
`),2;if(
|
|
127
|
-
`),2;let
|
|
128
|
-
`),2;return process.stderr.write(`CI run failed: ${
|
|
129
|
-
`),2}if(l.exportJson)await o(
|
|
150
|
+
`),2;return await I(h,l.outDir),0}function ge(p){let l=!1,m,k,h=!1,b=!1;for(let P=0;P<p.length;P++)switch(p[P]){case"--full":l=!0;break;case"--baseline":m=p[++P];break;case"--export-json":k=p[++P];break;case"--quiet":h=!0;break;case"--help":case"-h":b=!0;break}return{full:l,baseline:m,exportJson:k,quiet:h,help:b}}async function be(p){let l=ge(p);if(l.help)return process.stdout.write(ne),0;let m=await V();if(!m)return process.stderr.write(`No ostia.config.json found. "ostia ci" needs configured workloads.
|
|
151
|
+
`),2;if(m.workloads.length===0)return process.stderr.write(`ostia.config.json has no "workloads" configured.
|
|
152
|
+
`),2;let k;try{k=await K({config:m,full:l.full,baselineName:l.baseline})}catch(h){if(h instanceof T)return process.stderr.write(`${h.message}
|
|
153
|
+
`),2;return process.stderr.write(`CI run failed: ${h instanceof Error?h.message:String(h)}
|
|
154
|
+
`),2}if(l.exportJson)await o(k.document,l.exportJson);if(!l.quiet)process.stdout.write(G(k.summary));return k.summary.regressed>0?1:0}async function we(){let[p,...l]=process.argv.slice(2);switch(p){case"run":return ae(l);case"bench":return ue(l);case"compare":return ce(l);case"report":return pe(l);case"ci":return be(l);case"viz":return he(l);case void 0:case"--help":case"-h":return process.stdout.write(`ostia - Bun-native profile IR engine
|
|
130
155
|
|
|
131
156
|
Commands:
|
|
132
157
|
run Run commands N times and report timing/CPU/heap
|
|
@@ -138,4 +163,4 @@ Commands:
|
|
|
138
163
|
|
|
139
164
|
Run "ostia <command> --help" for details.
|
|
140
165
|
`),p===void 0?2:0;default:return process.stderr.write(`Unknown subcommand "${p}". Run "ostia --help".
|
|
141
|
-
`),2}}if(import.meta.main)
|
|
166
|
+
`),2}}if(import.meta.main)we().then((p)=>process.exit(p));
|
package/index.d.ts
CHANGED
|
@@ -38,20 +38,35 @@ export interface ProfileDocument {
|
|
|
38
38
|
comparisons?: Comparison[];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
interface Workload {
|
|
41
|
+
export interface Workload {
|
|
42
42
|
id: string;
|
|
43
43
|
kind: "subprocess" | "inprocess";
|
|
44
44
|
label?: string;
|
|
45
45
|
command?: string[];
|
|
46
46
|
shell?: string;
|
|
47
|
+
/** `task` is the "group/name" id the bench registry assigns; `group` is the
|
|
48
|
+
* enclosing `group()` name when there is one. Renderers prefer `group` over
|
|
49
|
+
* splitting `task` on "/", so task names may contain slashes. */
|
|
47
50
|
entry?: {
|
|
48
51
|
file: string;
|
|
49
52
|
task: string;
|
|
53
|
+
group?: string;
|
|
50
54
|
};
|
|
51
55
|
/** Marks this task as the in-run Relative reference for its group (see
|
|
52
56
|
* `task(name, fn, { baseline: true })`). At most one per group is
|
|
53
57
|
* meaningful; renderers use the first they encounter. */
|
|
54
58
|
baseline?: boolean;
|
|
59
|
+
/** What this task measures and why, from `task(name, fn, { description })`.
|
|
60
|
+
* Travels with the data so a reader of the document has intent, not just
|
|
61
|
+
* numbers. */
|
|
62
|
+
description?: string;
|
|
63
|
+
/** The enclosing group's `group(name, fn, { description })`. Repeated on every
|
|
64
|
+
* workload in the group so each record is self-contained. */
|
|
65
|
+
groupDescription?: string;
|
|
66
|
+
/** Whether this task ran in a subprocess dedicated to it alone (`isolate`
|
|
67
|
+
* on the task, its group, or the suite), vs. sharing its suite file's
|
|
68
|
+
* subprocess with other tasks. */
|
|
69
|
+
isolated?: boolean;
|
|
55
70
|
}
|
|
56
71
|
|
|
57
72
|
type Phase = "timing" | "cpu" | "heap" | "memstats";
|
|
@@ -168,9 +183,9 @@ interface JitTierBreakdown {
|
|
|
168
183
|
}[];
|
|
169
184
|
}
|
|
170
185
|
|
|
171
|
-
type WarningCode = "slow-first-run" | "outliers-detected" | "fast-command" | "nonzero-exit" | "instrumented-timing" | "artifact-missing" | "empty-profile" | "below-timer-resolution" | "cache-fallback-rerun";
|
|
186
|
+
export type WarningCode = "slow-first-run" | "outliers-detected" | "fast-command" | "nonzero-exit" | "instrumented-timing" | "artifact-missing" | "empty-profile" | "below-timer-resolution" | "cache-fallback-rerun" | "low-sample-count";
|
|
172
187
|
|
|
173
|
-
interface Warning {
|
|
188
|
+
export interface Warning {
|
|
174
189
|
code: WarningCode;
|
|
175
190
|
message: string;
|
|
176
191
|
data?: Record<string, unknown>;
|
|
@@ -223,8 +238,25 @@ interface BenchOptions {
|
|
|
223
238
|
minSamples?: number;
|
|
224
239
|
gc?: boolean;
|
|
225
240
|
filter?: string;
|
|
241
|
+
/** Suite files to run at once, each still in its own child process (default:
|
|
242
|
+
* 1). Files are independent by design, so this is a wall-clock win for
|
|
243
|
+
* multi-file suites, but concurrent CPU-bound processes contend for cores,
|
|
244
|
+
* caches, memory bandwidth and turbo headroom: timings taken under `jobs > 1`
|
|
245
|
+
* are noisier and not like-for-like with a baseline measured at 1. When
|
|
246
|
+
* `isolate` puts some tasks in their own subprocess, `jobs` pools across
|
|
247
|
+
* those per-task processes the same way - so the same noise/wall-clock
|
|
248
|
+
* tradeoff now scales with task count, not just file count. */
|
|
249
|
+
jobs?: number;
|
|
226
250
|
outDir?: string;
|
|
227
251
|
cwd?: string;
|
|
252
|
+
/** Give every task its own subprocess instead of sharing its suite file's,
|
|
253
|
+
* isolating each task's JIT tier state, inline caches and heap shape from
|
|
254
|
+
* every other task the way suite files are already isolated from each
|
|
255
|
+
* other. `TaskOptions.isolate` / `GroupOptions.isolate` override this per
|
|
256
|
+
* task or group for mixed suites (e.g. a few outlier-prone tasks isolated,
|
|
257
|
+
* many cheap ones sharing a process). Multiplies process-spawn overhead by
|
|
258
|
+
* task count instead of file count. */
|
|
259
|
+
isolate?: boolean;
|
|
228
260
|
}
|
|
229
261
|
|
|
230
262
|
export declare function bench(opts: BenchOptions): Promise<ProfileDocument>;
|
|
@@ -238,9 +270,26 @@ export interface TaskOptions {
|
|
|
238
270
|
/** Per-task hard floor on trials; overrides the suite-wide `--min-samples` /
|
|
239
271
|
* `minSamples`. */
|
|
240
272
|
minSamples?: number;
|
|
273
|
+
/** What this task measures and why. Flows into `Workload.description` so the
|
|
274
|
+
* intent travels with the numbers instead of living only in a source comment. */
|
|
275
|
+
description?: string;
|
|
276
|
+
/** Give this task its own subprocess instead of sharing its suite file's,
|
|
277
|
+
* isolating its JIT tier state and heap shape from every other task in the
|
|
278
|
+
* run. Overrides the group's and the suite-wide `bench({ isolate })` /
|
|
279
|
+
* `--isolate` default for this task only. */
|
|
280
|
+
isolate?: boolean;
|
|
241
281
|
}
|
|
242
282
|
|
|
243
|
-
export
|
|
283
|
+
export interface GroupOptions {
|
|
284
|
+
/** What this group measures and why. Flows into `Workload.groupDescription`
|
|
285
|
+
* on every task in the group. */
|
|
286
|
+
description?: string;
|
|
287
|
+
/** Default `isolate` for every task in this group, unless a task overrides
|
|
288
|
+
* it with its own `TaskOptions.isolate`. */
|
|
289
|
+
isolate?: boolean;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export declare function group(name: string, fn: () => void, opts?: GroupOptions): void;
|
|
244
293
|
|
|
245
294
|
export declare function task(name: string, fn: () => unknown | Promise<unknown>, opts?: TaskOptions): void;
|
|
246
295
|
|
|
@@ -259,7 +308,7 @@ export declare function loadDocument(path: string): Promise<ProfileDocument>;
|
|
|
259
308
|
|
|
260
309
|
export declare const renderers: Record<FormatName, Renderer<any>>;
|
|
261
310
|
|
|
262
|
-
type FormatName = "table" | "json" | "markdown" | "jsonl" | "collapsed" | "mermaid" | "speedscope" | "cpuprofile";
|
|
311
|
+
type FormatName = "table" | "json" | "markdown" | "jsonl" | "minimal" | "collapsed" | "mermaid" | "speedscope" | "cpuprofile";
|
|
263
312
|
|
|
264
313
|
interface RenderResult {
|
|
265
314
|
text?: string;
|
|
@@ -273,3 +322,34 @@ interface Renderer<O = unknown> {
|
|
|
273
322
|
name: FormatName;
|
|
274
323
|
render(doc: ProfileDocument, options: O): Promise<RenderResult>;
|
|
275
324
|
}
|
|
325
|
+
|
|
326
|
+
export interface MinimalLine {
|
|
327
|
+
task: string;
|
|
328
|
+
group?: string;
|
|
329
|
+
description?: string;
|
|
330
|
+
groupDescription?: string;
|
|
331
|
+
unit: "ns";
|
|
332
|
+
samples: number;
|
|
333
|
+
mean: number;
|
|
334
|
+
median: number;
|
|
335
|
+
stddev: number;
|
|
336
|
+
stddevPct: number;
|
|
337
|
+
min: number;
|
|
338
|
+
max: number;
|
|
339
|
+
/** Median over the group's reference median (its baseline task, else its
|
|
340
|
+
* fastest). Only present when the document has more than one timing run. */
|
|
341
|
+
relative?: number;
|
|
342
|
+
baseline?: true;
|
|
343
|
+
warnings: {
|
|
344
|
+
code: string;
|
|
345
|
+
data?: Record<string, unknown>;
|
|
346
|
+
}[];
|
|
347
|
+
/** From `comparisons` when present (ostia compare / ci): the change against
|
|
348
|
+
* the baseline document for this task. */
|
|
349
|
+
delta?: {
|
|
350
|
+
medianPct: number;
|
|
351
|
+
meanPct: number;
|
|
352
|
+
verdict: "improved" | "regressed" | "unchanged";
|
|
353
|
+
pass: boolean;
|
|
354
|
+
};
|
|
355
|
+
}
|
package/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
2
|
+
import{d,f,n,y,v}from"./chunk-gsjgejr1.js";import{o,t,F,S}from"./chunk-y1gkhb0y.js";export{d as bench,f as compareDocuments,F as group,t as loadDocument,v as profile,n as renderers,y as run,o as saveDocument,S as task};
|
package/package.json
CHANGED
package/runner.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
import{r,
|
|
4
|
-
`),2;let
|
|
5
|
-
`),2;let
|
|
6
|
-
`),2;let f=[],
|
|
3
|
+
import{r,P,i,s,o,l,m,I,C,p,N,D}from"./chunk-y1gkhb0y.js";var z=500,H=20,A=3,j=10,v=2,X=0.1,q=1000,K=1e4;function J(n){let e=Math.log10(Math.max(1,n)/1e6),a=Math.round(A+v*e);return Math.min(j,Math.max(A,a))}function Q(n,e){let a=Math.floor(e/n);return Math.min(H,Math.max(a,J(n)))}var L=0;function O(n){if(typeof n==="number")L+=n;else if(n!==void 0&&n!==null)L+=1}function B(n){return n!==null&&typeof n==="object"&&typeof n.then==="function"}function G(n,e){return Math.max(1,Math.ceil(q/n),Math.ceil(e/(n*K)))}async function U(n,e={}){let a=(e.timeBudgetMs??z)*1e6,d=a*(e.warmupFraction??X),T=Bun.nanoseconds(),f=0,b=0;while(b<d){let g=n();O(B(g)?await g:g),f++,b=Bun.nanoseconds()-T}let h;if(f>0)h=Math.max(1,b/f);else{let g=Bun.nanoseconds(),w=n();O(B(w)?await w:w),h=Math.max(1,Bun.nanoseconds()-g)}let t=G(h,a);if(t>1){let g=Bun.nanoseconds();for(let w=0;w<t;w++){let k=n();O(B(k)?await k:k)}h=Math.max(1,(Bun.nanoseconds()-g)/t),t=G(h,a)}let u=h*t,S=e.minSamples??Q(u,a),c=[],M=Bun.nanoseconds(),F=0,R=0;while(R<S||F<a){let g=Bun.nanoseconds();for(let k=0;k<t;k++){let _=n();O(B(_)?await _:_)}let w=Bun.nanoseconds();if(c.push({i:R,wallNs:(w-g)/t}),R++,F=Bun.nanoseconds()-M,e.gc)Bun.gc(!0)}let W=c.map((g)=>g.wallNs),y=l(W),E=m(y,[],"inprocess"),x=J(u);if(c.length<x)E.push({code:"low-sample-count",message:`Only ${c.length} sample(s) at ~${V(u)} per trial; ${x} is the floor for this cost class. Raise minSamples or the time budget for a steadier number.`,data:{samples:c.length,target:x,trialCostNs:u}});return{trials:c,timing:y,warnings:E}}function V(n){if(n>=1e9)return`${(n/1e9).toFixed(2)}s`;if(n>=1e6)return`${(n/1e6).toFixed(1)}ms`;if(n>=1000)return`${(n/1000).toFixed(1)}\xB5s`;return`${n.toFixed(0)}ns`}async function Y(){let[n,e,a]=process.argv.slice(2);if(!n||!e)return process.stderr.write(`bench runner: usage: runner.ts <suiteFile> <outputPath> [optsJson]
|
|
4
|
+
`),2;let d=a?JSON.parse(a):{};C(),await import(n);let T=I();if(T.length===0)return process.stderr.write(`bench runner: ${n} registered no tasks (no task() calls found).
|
|
5
|
+
`),2;let f=D(T,d.filter);if(d.taskIds){let t=new Set(d.taskIds);f=f.filter((u)=>t.has(p(u)))}if(f.length===0)return process.stderr.write(`bench runner: --filter ${JSON.stringify(d.filter)} matched zero of ${T.length} registered tasks in ${n}.
|
|
6
|
+
`),2;if(d.planOnly){let t=f.map((u)=>({id:p(u),isolate:N(u,d.isolate??!1)}));return await Bun.write(e,JSON.stringify({tasks:t})),0}let b=[],h=[];for(let t of f){let u=p(t),S=P(n,u,{label:u,baseline:t.baseline,group:t.groupName,description:t.opts?.description,groupDescription:t.groupDescription,isolated:d.markIsolated});b.push(S);let c={...d,...t.opts?.timeBudgetMs!==void 0&&{timeBudgetMs:t.opts.timeBudgetMs},...t.opts?.minSamples!==void 0&&{minSamples:t.opts.minSamples}},M=await U(t.fn,c);h.push(i({workload:S,configFingerprint:s({timeBudgetMs:c.timeBudgetMs??null,minSamples:c.minSamples??null,gc:c.gc??!1}),trials:M.trials,timing:M.timing,warnings:M.warnings}))}return await o(r(b,h),e),0}Y().then((n)=>process.exit(n));
|
package/chunk-qh19pbhc.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
function g(n){return JSON.stringify(W(n))}function W(n){if(Array.isArray(n))return n.map(W);if(n!==null&&typeof n==="object"){let a={};for(let d of Object.keys(n).sort())a[d]=W(n[d]);return a}return n}function e(n,...a){let d=Bun.CryptoHasher.hash("sha256",g(a),"hex");return`${n}_${d.slice(0,16)}`}var c="0.1.0";function r(n,a){return{schemaVersion:1,toolVersion:c,bunVersion:Bun.version,platform:{os:process.platform,arch:process.arch},createdAt:new Date().toISOString(),workloads:n,runs:a}}function u(n,a){return{id:e("wl","subprocess",n,process.cwd()),kind:"subprocess",command:n,label:a}}function k(n,a){return{id:e("wl","inprocess",n.name,n.toString()),kind:"inprocess",label:a}}function R(n,a,d,m){return{id:e("wl","inprocess-entry",n,a),kind:"inprocess",entry:{file:n,task:a},label:d,baseline:m}}function i(n){return{id:e("run",n.workload.id,"timing",n.configFingerprint,Bun.version,c),workloadId:n.workload.id,phase:"timing",instrumented:!1,configFingerprint:n.configFingerprint,trials:n.trials,timing:n.timing,warnings:n.warnings,artifacts:[],memory:G(n.trials)}}function G(n){let a=n.map((d)=>d.maxRssBytes).filter((d)=>d!==void 0);if(a.length===0)return;return{origin:"resourceUsage",perTrial:n.map((d)=>({rssBytes:d.maxRssBytes})),maxRssBytes:Math.max(...a)}}function h(n){return{id:e("run",n.workload.id,n.phase,n.configFingerprint,Bun.version,c),workloadId:n.workload.id,phase:n.phase,instrumented:!0,configFingerprint:n.configFingerprint,trials:[{i:0,wallNs:n.diagnosticWallNs,exitCode:n.exitCode}],diagnosticWallNs:n.diagnosticWallNs,cpu:n.cpu,heap:n.heap,jit:n.jit,warnings:n.warnings,artifacts:n.artifacts}}async function P(n,a,d){let f=await Bun.file(d).arrayBuffer(),w=new Bun.CryptoHasher("sha256");return w.update(f),{id:e("art",n,a,d),kind:a,path:d,sha256:w.digest("hex"),bytes:f.byteLength}}function s(n){return e("cfg",n)}function y(n){return`${JSON.stringify(W(n),null,2)}
|
|
3
|
-
`}async function o(n,a){await Bun.write(a,y(n))}async function t(n){let a=await Bun.file(n).text();return JSON.parse(a)}var _=[],B;function F(n,a){let d=B;B=n;try{a()}finally{B=d}}function D(n,a,d){_.push({groupName:B,name:n,fn:a,baseline:d?.baseline,opts:d})}function T(){return _}function C(){_.length=0,B=void 0}function x(n){return n.groupName?`${n.groupName}/${n.name}`:n.name}function N(n,a){if(!a)return[...n];let d=new RegExp(a);return n.filter((m)=>d.test(x(m)))}function l(n){if(n.length===0)throw Error("computeTimingStats: samples must be non-empty");let a=n.length,d=L(n),m=0;for(let v=0;v<a;v++)m+=n[v];let f=m/a,w=I(d,0.5),b=0;for(let v=0;v<a;v++){let S=n[v]-f;b+=S*S}let A=Math.sqrt(b/a),H=d[0],O=d[a-1],E=I(d,0.25),q=I(d,0.75),M=q-E,z=E-1.5*M,V=q+1.5*M,K=E-3*M,U=q+3*M,J=0,j=0;for(let v=0;v<a;v++){let S=n[v];if(S<K||S>U)j++;else if(S<z||S>V)J++}return{unit:"ns",samples:n,mean:f,median:w,stddev:A,min:H,max:O,outliers:{mild:J,severe:j}}}function L(n){let a=new Float64Array(n.length);return a.set(n),a.sort(),a}function I(n,a){let d=n.length;if(d===1)return n[0];let m=a*(d-1),f=Math.floor(m),w=Math.ceil(m);if(f===w)return n[f];let b=m-f;return n[f]*(1-b)+n[w]*b}var Z=5000000,Q=200;function p(n,a,d="subprocess"){let m=[],f=n.samples[0];if(f!==void 0){let b=L(n.samples),A=I(b,0.25),O=I(b,0.75)-A;if(f>n.median+3*O&&O>0)m.push({code:"slow-first-run",message:`First run took ${(f/1e6).toFixed(2)}ms, much slower than the median ${(n.median/1e6).toFixed(2)}ms. Consider more warmup.`,data:{firstNs:f,medianNs:n.median}})}if(n.outliers.mild+n.outliers.severe>0)m.push({code:"outliers-detected",message:`${n.outliers.mild+n.outliers.severe} outlier(s) detected (${n.outliers.severe} severe, ${n.outliers.mild} mild).`,data:n.outliers});if(d==="subprocess"&&n.median<Z)m.push({code:"fast-command",message:`Median run time (${(n.median/1e6).toFixed(3)}ms) is very fast; results may be dominated by spawn overhead.`,data:{medianNs:n.median}});if(d==="inprocess"&&n.median<Q)m.push({code:"below-timer-resolution",message:`Median run time (${n.median.toFixed(0)}ns) is close to timer resolution; consider a larger batch size or a coarser operation.`,data:{medianNs:n.median}});let w=a.filter((b)=>b!==void 0&&b!==0);if(w.length>0)m.push({code:"nonzero-exit",message:`${w.length} of ${a.length} trial(s) exited non-zero.`,data:{exitCodes:w}});return m}
|
|
4
|
-
export{g,e,c,r,u,k,R,i,h,P,s,y,o,t,l,p,F,D,T,C,x,N};
|
package/chunk-v7vpm34a.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{g,e,r,u,k,i,h,P,s,y,t,l,p}from"./chunk-qh19pbhc.js";function Be(o){return o.startsWith("file://")?o.slice(7):o}function re(o,v,c){let T=o.nodes,x=T.length,C=new Map,N=[],R=new Map,U=new Int32Array(x);for(let D=0;D<x;D++){let M=T[D],F=M.callFrame,j=Be(F.url),J=C.get(F.functionName);if(J===void 0)J=new Map,C.set(F.functionName,J);let K=J.get(j);if(K===void 0)K=N.length,J.set(j,K),N.push({key:e("fr",F.functionName,j),name:F.functionName,url:j||void 0,line:F.lineNumber>=0?F.lineNumber:void 0,col:F.columnNumber>=0?F.columnNumber:void 0});U[D]=K,R.set(M.id,D)}let B=Array(x);for(let D=0;D<x;D++){let M=T[D];B[D]={id:M.id,frameIx:U[R.get(M.id)],children:M.children??[]}}let S=new Float64Array(x),A=new Float64Array(x),{samples:O,timeDeltas:W}=o;for(let D=0;D<O.length;D++){let M=R.get(O[D]);if(M===void 0)continue;S[M]+=W[D]??0,A[M]+=1}let L=new Int32Array(x).fill(-1);for(let D=0;D<x;D++){let M=T[D].children;if(!M)continue;for(let F of M){let j=R.get(F);if(j!==void 0)L[j]=D}}let E=[],H=[];for(let D=x-1;D>=0;D--)if(L[D]===-1)H.push(D);while(H.length>0){let D=H.pop();E.push(D);let M=T[D].children;if(!M)continue;for(let F of M){let j=R.get(F);if(j!==void 0&&L[j]===D)H.push(j)}}let z=new Float64Array(x);for(let D=E.length-1;D>=0;D--){let M=E[D];z[M]+=S[M];let F=L[M];if(F>=0)z[F]+=z[M]}let V=Array(N.length),_=[];for(let D=0;D<x;D++){let M=B[D].frameIx,F=V[M];if(F)F.selfUs+=S[D],F.totalUs+=z[D],F.samples+=A[D];else{let j={frameIx:M,selfUs:S[D],totalUs:z[D],samples:A[D]};V[M]=j,_.push(j)}}return{origin:v,samplingIntervalUs:c,frames:N,nodes:B,totals:_.sort((D,M)=>M.selfUs-D.selfUs),samples:{nodeIds:o.samples,timeDeltasUs:o.timeDeltas}}}function Oe(o,v,c,T){let x=["--cpu-prof","--cpu-prof-dir",v,"--cpu-prof-name",c,"--cpu-prof-interval",String(T)],C=o[0];if(C==="bun"||C?.endsWith("/bun"))return[C,...x,...o.slice(1)];return o}async function pe(o){let v=`${o.artifactDir}/${o.fileName}`,c=o.argv[0],T=c==="bun"||c?.endsWith("/bun"),x=Oe(o.argv,o.artifactDir,o.fileName,o.intervalUs),C=T?o.env:{...process.env,...o.env,BUN_OPTIONS:`--cpu-prof --cpu-prof-dir ${o.artifactDir} --cpu-prof-name ${o.fileName} --cpu-prof-interval ${o.intervalUs}`},N=Bun.nanoseconds(),U=await Bun.spawn(x,{cwd:o.cwd,env:C,stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited,B=Bun.nanoseconds()-N,S=Bun.file(v);if(!await S.exists())return{diagnosticWallNs:B,exitCode:U,warnings:[{code:"artifact-missing",message:`Expected a .cpuprofile at ${v} after exit ${U}, found nothing. The workload's argv[0] must be a \`bun\` binary for CPU capture.`,data:{artifactPath:v,argv:o.argv}}]};let A=await S.json(),O=re(A,"cpu-prof",o.intervalUs),W=A.samples.length===0?[{code:"empty-profile",message:"CPU capture produced zero samples."}]:[];return{diagnosticWallNs:B,exitCode:U,artifactPath:v,cpu:O,warnings:W}}function le(o,v="heap-prof"){let{node_fields:c,node_types:T}=o.snapshot.meta,x=c.indexOf("type"),C=c.indexOf("self_size"),N=c.length,R=T[0];if(x===-1||C===-1||!Array.isArray(R))return{origin:v,typeCounts:[],objectCount:o.snapshot.node_count};let U=R.length,B=Array(U),S=new Map,A=[],O=0,W=o.nodes,L=W.length;for(let _=0;_<L;_+=N){let D=W[_+x],M=W[_+C]??0;O+=M;let F;if(D>=0&&D<U){if(F=B[D],F===void 0)F={type:R[D],count:0,bytes:0},B[D]=F,A.push(F)}else{let j=`unknown(${D})`;if(F=S.get(j),F===void 0)F={type:j,count:0,bytes:0},S.set(j,F),A.push(F)}F.count++,F.bytes+=M}let E=A.sort((_,D)=>D.count-_.count),H=E.slice(0,20),z=E.slice(20),V=H.map(({type:_,count:D,bytes:M})=>({type:_,count:D,retainedBytes:M}));if(z.length>0){let _=0,D=0;for(let M of z)_+=M.count,D+=M.bytes;V.push({type:"other",count:_,retainedBytes:D})}return{origin:v,heapSizeBytes:O,objectCount:o.snapshot.node_count,typeCounts:V}}function We(o,v,c){let T=["--heap-prof","--heap-prof-dir",v,"--heap-prof-name",c],x=o[0];if(x==="bun"||x?.endsWith("/bun"))return[x,...T,...o.slice(1)];return o}async function me(o){let v=`${o.artifactDir}/${o.fileName}`,c=o.argv[0],T=c==="bun"||c?.endsWith("/bun"),x=We(o.argv,o.artifactDir,o.fileName),C=T?o.env:{...process.env,...o.env,BUN_OPTIONS:`--heap-prof --heap-prof-dir ${o.artifactDir} --heap-prof-name ${o.fileName}`},N=Bun.nanoseconds(),U=await Bun.spawn(x,{cwd:o.cwd,env:C,stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited,B=Bun.nanoseconds()-N,S=Bun.file(v);if(!await S.exists())return{diagnosticWallNs:B,exitCode:U,warnings:[{code:"artifact-missing",message:`Expected a heap snapshot at ${v} after exit ${U}, found nothing. The workload's argv[0] must be a \`bun\` binary for heap capture.`,data:{artifactPath:v,argv:o.argv}}]};let A=await S.json(),O=le(A,"heap-prof");return{diagnosticWallNs:B,exitCode:U,artifactPath:v,heap:O,warnings:[]}}import{Session as Ee}from"inspector/promises";var Ae=1000;async function fe(o,v={}){let c=v.intervalUs??Ae,T=new Ee;T.connect();let x=Bun.nanoseconds();try{await T.post("Profiler.enable"),await T.post("Profiler.setSamplingInterval",{interval:c}),await T.post("Profiler.start");let C=await o(),{profile:N}=await T.post("Profiler.stop"),R=Bun.nanoseconds()-x,U=re(N,"inspector",c);return{result:C,cpu:U,diagnosticWallNs:R}}finally{T.disconnect()}}import{profile as je}from"bun:jsc";var _e=new Map([["LLInt","llint"],["Baseline","baseline"],["DFG","dfg"],["FTL","ftl"]]),de=4294967295;function ge(o,v){let c=v??o.interval*1e6,T=new Map,x=[];function C(F,j,J,K){let Y=T.get(F);if(Y===void 0)Y=new Map,T.set(F,Y);let G=j??"",q=Y.get(G);if(q===void 0)q=x.length,Y.set(G,q),x.push({key:e("fr",F,G),name:F,url:j,line:J,col:K});return q}function N(F){let j=F.line===de,J=j?void 0:F.line-1,K=j||F.column===de?void 0:F.column-1;return C(F.name,F.sourceURL,J,K)}let R=C("(root)",void 0,void 0,void 0),U=1,B={id:0,frameIx:R,children:new Map,selfUs:0,samples:0,totalUs:0},S=new Map([[0,B]]),A={llint:0,baseline:0,dfg:0,ftl:0},O=new Map,W=[],L=[];for(let F of o.traces){let j=F.frames,J=B;for(let G=j.length-1;G>=0;G--){let q=N(j[G]),Z=J.children.get(q);if(!Z)Z={id:U++,frameIx:q,children:new Map,selfUs:0,samples:0,totalUs:0},J.children.set(q,Z),S.set(Z.id,Z);J=Z}J.selfUs+=c,J.samples+=1,W.push(J.id),L.push(c);let K=j[0],Y=K&&_e.get(K.category);if(Y){A[Y]++;let G=O.get(Y)??new Map;G.set(J.frameIx,(G.get(J.frameIx)??0)+1),O.set(Y,G)}}function E(F){let j=F.selfUs;for(let J of F.children.values())j+=E(J);return F.totalUs=j,j}E(B);let H=new Map;function z(F){let j=H.get(F.frameIx);if(j)j.selfUs+=F.selfUs,j.totalUs+=F.totalUs,j.samples+=F.samples;else H.set(F.frameIx,{frameIx:F.frameIx,selfUs:F.selfUs,totalUs:F.totalUs,samples:F.samples});for(let J of F.children.values())z(J)}z(B);let V=[...S.values()].map((F)=>({id:F.id,frameIx:F.frameIx,children:[...F.children.values()].map((j)=>j.id)})),_={origin:"jsc-profile",samplingIntervalUs:c,frames:x,nodes:V,totals:[...H.values()].sort((F,j)=>j.selfUs-F.selfUs),samples:{nodeIds:W,timeDeltasUs:L}},D=[...O.entries()].flatMap(([F,j])=>[...j.entries()].sort((J,K)=>K[1]-J[1]).slice(0,3).map(([J,K])=>({tier:F,frameKey:x[J].key,samples:K})));return{cpu:_,jit:{origin:"jsc-profile",tiers:A,topFramesByTier:D}}}var Le=1000;async function he(o,v={}){let c=v.intervalUs??Le,T,x=Bun.nanoseconds(),C=await je(async()=>(T=await o(),T),c),N=Bun.nanoseconds()-x,{cpu:R,jit:U}=ge(C.stackTraces,c);return{result:T,cpu:R,jit:U,diagnosticWallNs:N}}async function ie(o){let v=Bun.nanoseconds(),c=Bun.spawn(o.argv,{cwd:o.cwd,env:o.env,stdout:"ignore",stderr:"ignore",stdin:"ignore"}),T=await c.exited,x=Bun.nanoseconds(),C=c.resourceUsage?.();return{wallNs:x-v,exitCode:T,userNs:C?Number(C.cpuTime.user)*1000:void 0,systemNs:C?Number(C.cpuTime.system)*1000:void 0,maxRssBytes:C?.maxRSS}}function be(o){return o.trim().split(/\s+/).filter(Boolean)}var He=10,ze=3000000000,Je=3;async function f(o){let v=o.warmup??Je;for(let S=0;S<v;S++)await ie(o);let c=[],T=o.runs??o.minRuns??He,x=o.runs!==void 0?0:o.minTotalNs??ze,C=0,N=0;while(N<T||C<x){let S=await ie(o);if(c.push({i:N,wallNs:S.wallNs,exitCode:S.exitCode,userNs:S.userNs,systemNs:S.systemNs,maxRssBytes:S.maxRssBytes}),C+=S.wallNs,N++,o.runs!==void 0&&N>=o.runs)break}let R=c.map((S)=>S.wallNs),U=l(R),B=p(U,c.map((S)=>S.exitCode));return{trials:c,timing:U,warnings:B}}var Ve=new URL("./runner.ts",import.meta.url).pathname,Ke="node_modules/.cache/ostia";async function m(o){let c=`${o.outDir??Ke}/bench-tmp`,T=o.cwd??process.cwd(),x={timeBudgetMs:o.timeBudgetMs,minSamples:o.minSamples,gc:o.gc,filter:o.filter},C=[],N=[];try{for(let R of o.suites){let U=R.startsWith("/")?R:`${T}/${R}`,B=`${c}/${e("bench-out",U)}.json`,A=await Bun.spawn(["bun",Ve,U,B,JSON.stringify(x)],{cwd:T,stdout:"inherit",stderr:"inherit",stdin:"ignore"}).exited;if(A!==0)throw Error(`Bench suite failed: ${R} (runner exited ${A})`);let O=await t(B);C.push(...O.workloads),N.push(...O.runs)}}finally{await Bun.spawn(["rm","-rf",c]).exited}return r(C,N)}var a={timingPct:5,frameSelfPct:10,heapTypePct:10,minFrameSelfUs:1000};function se(o,v){if(o===0)return v===0?0:1/0;return(v-o)/o*100}function ee(o,v,c){return o.runs.find((T)=>T.workloadId===v&&T.phase===c)}function d(o,v,c=a){let T=new Set(v.workloads.map((C)=>C.id)),x=[];for(let C of o.workloads){if(!T.has(C.id))continue;let N=b(o,v,C.id,c);if(N)x.push(N)}return x}function b(o,v,c,T=a){let x=ee(o,c,"timing"),C=ee(v,c,"timing"),N=ee(o,c,"cpu"),R=ee(v,c,"cpu"),U=ee(o,c,"heap"),B=ee(v,c,"heap"),S=x?.id??N?.id??U?.id,A=C?.id??R?.id??B?.id;if(!S||!A)return;let O=!1,W;if(x?.timing&&C?.timing){let H=se(x.timing.median,C.timing.median),z=se(x.timing.mean,C.timing.mean),V=H>T.timingPct?"regressed":H<-T.timingPct?"improved":"unchanged";if(V==="regressed")O=!0;W={medianDeltaPct:H,meanDeltaPct:z,verdict:V}}let L;if(N?.cpu&&R?.cpu){let H=new Map(N.cpu.totals.map((M)=>[N.cpu.frames[M.frameIx].key,M])),z=new Map(R.cpu.totals.map((M)=>[R.cpu.frames[M.frameIx].key,M])),V=new Map(N.cpu.frames.map((M)=>[M.key,M.name])),_=new Map(R.cpu.frames.map((M)=>[M.key,M.name]));L=[...new Set([...H.keys(),...z.keys()])].map((M)=>{let F=H.get(M)?.selfUs??0,j=z.get(M)?.selfUs??0;return{frameKey:M,name:_.get(M)??V.get(M)??M,baseSelfUs:F,candSelfUs:j,deltaPct:se(F,j)}}).sort((M,F)=>Math.abs(F.deltaPct)-Math.abs(M.deltaPct));for(let M of L)if((M.baseSelfUs>=T.minFrameSelfUs||M.candSelfUs>=T.minFrameSelfUs)&&M.deltaPct>T.frameSelfPct)O=!0}let E;if(U?.heap&&B?.heap){let H=new Map(U.heap.typeCounts.map((_)=>[_.type,_])),z=new Map(B.heap.typeCounts.map((_)=>[_.type,_]));E=[...new Set([...H.keys(),...z.keys()])].map((_)=>{let D=H.get(_),M=z.get(_);return{type:_,baseCount:D?.count??0,candCount:M?.count??0,baseBytes:D?.retainedBytes,candBytes:M?.retainedBytes,deltaPct:se(D?.count??0,M?.count??0)}}).sort((_,D)=>Math.abs(D.deltaPct)-Math.abs(_.deltaPct));for(let _ of E)if(_.deltaPct>T.heapTypePct)O=!0}return{id:e("cmp",S,A),baselineRunId:S,candidateRunId:A,timing:W,frames:L,heapTypes:E,thresholds:T,verdict:O?"fail":"pass"}}function Q(o,v){if(v){let c=o.runs.find((T)=>T.id===v);return c?.cpu?[c]:[]}return o.runs.filter((c)=>c.phase==="cpu"&&c.cpu)}function ne(o){let v=o.nodes,c=v.length,T=Ge(o),x=new Int32Array(c).fill(-1);for(let U=0;U<c;U++)for(let B of v[U].children){let S=T(B);if(S!==-1)x[S]=U}let C=[];for(let U=0;U<c;U++)if(x[U]===-1)C.push(U);let N=[],R=[];for(let U=C.length-1;U>=0;U--)R.push(C[U]);while(R.length>0){let U=R.pop();N.push(U);for(let B of v[U].children){let S=T(B);if(S!==-1&&x[S]===U)R.push(S)}}return{count:c,indexOf:T,parentIx:x,roots:C,order:N}}function Ge(o){let v=o.nodes,c=v.length,T=1/0,x=-1/0,C=!0;for(let R=0;R<c;R++){let U=v[R].id;if(!Number.isInteger(U)){C=!1;break}if(U<T)T=U;if(U>x)x=U}if(C&&c>0&&x-T<c*4+64){let R=x-T+1,U=new Int32Array(R).fill(-1);for(let B=0;B<c;B++)U[v[B].id-T]=B;return(B)=>{let S=B-T;return S>=0&&S<R?U[S]:-1}}let N=new Map;for(let R=0;R<c;R++)N.set(v[R].id,R);return(R)=>N.get(R)??-1}function ye(o,v){let{count:c,indexOf:T,parentIx:x,order:C}=v,N=new Float64Array(c),R=new Float64Array(c),U=o.samples?.nodeIds??[],B=o.samples?.timeDeltasUs??[];for(let A=0;A<U.length;A++){let O=T(U[A]);if(O===-1)continue;N[O]+=B[A]??0,R[O]+=1}let S=new Float64Array(c);for(let A=C.length-1;A>=0;A--){let O=C[A];S[O]+=N[O];let W=x[O];if(W>=0)S[W]+=S[O]}return{selfUs:N,totalUs:S,samples:R}}var we={name:"collapsed",async render(o,v={}){return{files:Q(o,v.runId).map((x)=>{let C=x.cpu,{nodes:N,frames:R}=C,U=ne(C),B=Array(U.count);for(let L of U.order){let E=R[N[L].frameIx].name||"(anonymous)",H=U.parentIx[L];B[L]=H===-1?E:`${B[H]};${E}`}let S=new Float64Array(U.count),A=[],O=C.samples?.nodeIds??[];for(let L=0;L<O.length;L++){let E=U.indexOf(O[L]);if(E===-1)continue;if(S[E]++===0)A.push(E)}let W=Array(A.length);for(let L=0;L<A.length;L++){let E=A[L];W[L]=`${B[E]} ${S[E]}`}return{path:`${x.id}.collapsed.txt`,content:W.join(`
|
|
3
|
-
`)+(W.length>0?`
|
|
4
|
-
`:"")}})}}};var xe={name:"cpuprofile",async render(o,v={}){let c=Q(o,v.runId),T=[],x=[];for(let C of c){if(C.cpu?.origin!=="cpu-prof"&&C.cpu?.origin!=="inspector"){x.push(`${C.id} (origin ${C.cpu?.origin??"unknown"} has no .cpuprofile artifact to pass through)`);continue}let N=C.artifacts.find((U)=>U.kind==="cpuprofile");if(!N){x.push(`${C.id} (no cpuprofile artifact recorded on this run)`);continue}let R=Bun.file(N.path);if(!await R.exists()){x.push(`${C.id} (artifact missing on disk: ${N.path})`);continue}T.push({path:`${C.id}.cpuprofile`,content:await R.text()})}if(T.length===0&&x.length>0)return{text:`No .cpuprofile artifacts available:
|
|
5
|
-
${x.map((C)=>` - ${C}`).join(`
|
|
6
|
-
`)}
|
|
7
|
-
`};return{files:T}}};var Re={name:"json",async render(o){return{text:y(o)}}};var Pe={name:"jsonl",async render(o){let{runs:v,...c}=o;return{text:`${[g(c),...v.map((x)=>g(x))].join(`
|
|
8
|
-
`)}
|
|
9
|
-
`}}};function X(o){return(o/1e6).toFixed(3)}function oe(o){return o?.label??o?.command?.join(" ")??o?.entry?.task??o?.id??"unknown"}var Te=10,$e=10,Ce={name:"markdown",async render(o){let v=new Map(o.workloads.map((x)=>[x.id,x])),c=[];c.push("# Profile Report",""),c.push(`Bun ${o.bunVersion} \xB7 ostia ${o.toolVersion} \xB7 ${o.platform.os}/${o.platform.arch} \xB7 ${o.createdAt}`,"");let T=o.runs.filter((x)=>x.phase==="timing"&&x.timing!==void 0);if(T.length>0){c.push("## Timing",""),c.push("| Command | Mean \xB1 SD (ms) | Min\u2026Max (ms) | Median (ms) |","|---|---|---|---|");for(let C of T){let N=oe(v.get(C.workloadId)),R=C.timing;c.push(`| ${N} | ${X(R.mean)} \xB1 ${X(R.stddev)} | ${X(R.min)}\u2026${X(R.max)} | ${X(R.median)} |`)}c.push("");let x=T.filter((C)=>C.warnings.length>0);if(x.length>0){c.push("### Warnings","");for(let C of x){let N=oe(v.get(C.workloadId));for(let R of C.warnings)c.push(`- **${N}**: ${R.message} (\`${R.code}\`)`)}c.push("")}}for(let x of o.runs){if(x.phase!=="cpu"&&x.phase!=="heap")continue;let C=oe(v.get(x.workloadId));if(x.phase==="cpu"){if(c.push(`## CPU capture - ${C}`,""),c.push(`instrumented, diagnostic wall ${X(x.diagnosticWallNs??0)}ms`,""),x.cpu){c.push(`origin: \`${x.cpu.origin}\`, interval: ${x.cpu.samplingIntervalUs}\xB5s`,""),c.push("| Self % | Self (ms) | Total (ms) | Frame |","|---|---|---|---|");let N=x.cpu.totals.reduce((R,U)=>R+U.selfUs,0)||1;for(let R of x.cpu.totals.slice(0,Te)){let U=x.cpu.frames[R.frameIx],B=(R.selfUs/N*100).toFixed(1);c.push(`| ${B}% | ${(R.selfUs/1000).toFixed(2)} | ${(R.totalUs/1000).toFixed(2)} | ${U?.name||"(anonymous)"} |`)}if(c.push(""),x.jit){let R=x.jit.tiers;c.push(`JIT tiers: LLInt ${R.llint} \xB7 Baseline ${R.baseline} \xB7 DFG ${R.dfg} \xB7 FTL ${R.ftl}`,"")}}}else if(c.push(`## Heap snapshot - ${C}`,""),c.push(`instrumented, diagnostic wall ${X(x.diagnosticWallNs??0)}ms`,""),x.heap){c.push(`${x.heap.objectCount??"?"} objects, ${((x.heap.heapSizeBytes??0)/1e6).toFixed(2)}MB`,""),c.push("| Count | Type |","|---|---|");for(let N of x.heap.typeCounts.slice(0,$e))c.push(`| ${N.count} | ${N.type} |`);c.push("")}for(let N of x.artifacts)c.push(`- artifact: \`${N.path}\``);for(let N of x.warnings)c.push(`- ! ${N.message} (\`${N.code}\`)`);if(x.artifacts.length>0||x.warnings.length>0)c.push("")}if(o.comparisons&&o.comparisons.length>0){c.push("## Comparisons","");for(let x of o.comparisons){let C=o.runs.find((R)=>R.id===x.candidateRunId),N=oe(C?v.get(C.workloadId):void 0);if(c.push(`### ${x.verdict==="pass"?"\u2713":"\u2717"} ${N}`,""),x.timing){let R=x.timing.medianDeltaPct>0?"+":"";c.push(`- timing: ${R}${x.timing.medianDeltaPct.toFixed(1)}% median (**${x.timing.verdict}**)`)}for(let R of x.frames?.slice(0,Te)??[]){if(Math.abs(R.deltaPct)<0.5)continue;let U=R.deltaPct>0?"+":"";c.push(`- frame \`${R.name}\`: ${U}${R.deltaPct.toFixed(1)}% self-time (${(R.baseSelfUs/1000).toFixed(2)}ms \u2192 ${(R.candSelfUs/1000).toFixed(2)}ms)`)}for(let R of x.heapTypes?.slice(0,$e)??[]){if(Math.abs(R.deltaPct)<0.5)continue;let U=R.deltaPct>0?"+":"";c.push(`- heap \`${R.type}\`: ${U}${R.deltaPct.toFixed(1)}% count (${R.baseCount} \u2192 ${R.candCount})`)}c.push("")}}return{text:c.join(`
|
|
10
|
-
`)}}};var Ye=15;function ae(o){return`n${o}`}function qe(o,v,c){return`${(o||"(anonymous)").replace(/"/g,"'")} (self ${(v/1000).toFixed(2)}ms, total ${(c/1000).toFixed(2)}ms)`}function Qe(o,v,c,T){let x=[];if(T<=0)return x;for(let C=0;C<v;C++){if(C===c)continue;let N=o[C];if(x.length===T&&N<=o[x[T-1]])continue;let R=x.length;while(R>0&&o[x[R-1]]<N)R--;if(x.splice(R,0,C),x.length>T)x.pop()}return x}var Ie={name:"mermaid",async render(o,v={}){let c=v.topN??Ye;return{files:Q(o,v.runId).map((C)=>{let N=C.cpu,{nodes:R,frames:U}=N,B=ne(N),{selfUs:S,totalUs:A}=ye(N,B),{parentIx:O}=B,W=B.roots[0]??-1,L=Qe(S,B.count,W,c),E=new Set(W!==-1?[W]:[]),H=[];for(let V of L){H.length=0;for(let _=V;_!==-1;_=O[_])H.push(_);for(let _=H.length-1;_>=0;_--)E.add(H[_])}let z=["graph TD"];for(let V of E){let _=R[V].id;z.push(` ${ae(_)}["${qe(U[R[V].frameIx].name,S[V],A[V])}"]`)}for(let V of E){let _=O[V];if(_!==-1&&E.has(_))z.push(` ${ae(R[_].id)} --> ${ae(R[V].id)}`)}return{path:`${C.id}.mermaid.md`,content:`${z.join(`
|
|
11
|
-
`)}
|
|
12
|
-
`}})}}};var Xe="https://www.speedscope.app/file-format-schema.json";function Ne(o){return o?.label??o?.command?.join(" ")??o?.entry?.task??"profile"}var ke={name:"speedscope",async render(o,v={}){let c=Q(o,v.runId),T=new Map(o.workloads.map((C)=>[C.id,C]));return{files:c.map((C)=>{let N=C.cpu,{nodes:R}=N,U=ne(N),B=N.samples?.nodeIds??[],S=N.samples?.timeDeltasUs??[],A=Array(U.count);for(let E of U.order){let H=U.parentIx[E],z=R[E].frameIx;A[E]=H===-1?[z]:[...A[H],z]}let O=Array(B.length);for(let E=0;E<B.length;E++){let H=U.indexOf(B[E]);O[E]=H===-1?[]:A[H]}let W=0;for(let E=0;E<S.length;E++)W+=S[E];let L={$schema:Xe,exporter:"ostia",name:Ne(T.get(C.workloadId)),activeProfileIndex:0,shared:{frames:N.frames.map((E)=>({name:E.name||"(anonymous)",file:E.url,line:E.line!==void 0?E.line+1:void 0}))},profiles:[{type:"sampled",name:Ne(T.get(C.workloadId)),unit:"microseconds",startValue:0,endValue:W,samples:O,weights:S}]};return{path:`${C.id}.speedscope.json`,content:`${JSON.stringify(L,null,2)}
|
|
13
|
-
`}})}}};function te(o){return(o/1e6).toFixed(3)}function ce(o){return o.label??o.command?.join(" ")??o.entry?.task??o.id}function Ue(o){let v=o?.entry?.task;if(!v)return;let c=v.lastIndexOf("/");return c===-1?void 0:v.slice(0,c)}var Fe={name:"table",async render(o){let v=o.runs.filter((W)=>W.phase==="timing"&&W.timing!==void 0),c=new Map(o.workloads.map((W)=>[W.id,W]));if(v.length===0){let W=ve(o,c);return{text:W.length>0?`${W.join(`
|
|
14
|
-
`)}
|
|
15
|
-
`:`(no timing runs)
|
|
16
|
-
`}}let T=v.map((W)=>{let L=c.get(W.workloadId);return{run:W,workload:L,label:L?ce(L):W.workloadId}}),x=Math.min(...T.map((W)=>W.run.timing.median)),C=T.length>1,N=new Map;for(let W of T){let L=Ue(W.workload);if(L===void 0)continue;let E=N.get(L);if(E)E.push(W);else N.set(L,[W])}let R=(W)=>{let L=Ue(W.workload);if(L===void 0)return x;let E=N.get(L)??[W],H=E.find((z)=>z.workload?.baseline);return H?H.run.timing.median:Math.min(...E.map((z)=>z.run.timing.median))},U=[],B=Math.max(7,...T.map((W)=>W.label.length)),S=C?`${"Command".padEnd(B)} Mean [ms] Min\u2026Max [ms] Relative`:`${"Command".padEnd(B)} Mean [ms] Min\u2026Max [ms]`;U.push(S),U.push("-".repeat(S.length));for(let W of T){let{run:L,label:E,workload:H}=W,z=L.timing,V=`${te(z.mean)} \xB1 ${te(z.stddev)}`,_=`${te(z.min)}\u2026${te(z.max)}`,D=`${E.padEnd(B)} ${V.padEnd(15)} ${_.padEnd(18)}`;if(C){let M=z.median/R(W);if(M===1)D+=H?.baseline?" 1.00\xD7 (baseline)":" 1.00\xD7";else if(M>1)D+=` ${M.toFixed(2)}\xD7 slower`;else D+=` ${(1/M).toFixed(2)}\xD7 faster`}U.push(D);for(let M of L.warnings)U.push(` ! ${M.message}`)}let A=en(o,c);if(A.length>0)U.push(""),U.push(...A);let O=ve(o,c);if(O.length>0)U.push(""),U.push(...O);return{text:`${U.join(`
|
|
17
|
-
`)}
|
|
18
|
-
`}}};function Ze(o,v,c){let T=o.runs.find((C)=>C.id===c),x=T?v.get(T.workloadId):void 0;return x?ce(x):c}function ve(o,v){if(!o.comparisons||o.comparisons.length===0)return[];let c=[];for(let T of o.comparisons){let x=Ze(o,v,T.candidateRunId),C=T.verdict==="pass"?"\u2713":"\u2717";if(c.push(`${C} ${x}`),T.timing){let N=T.timing.medianDeltaPct>0?"+":"";c.push(` timing: ${N}${T.timing.medianDeltaPct.toFixed(1)}% median (${T.timing.verdict})`)}if(T.frames)for(let N of T.frames.slice(0,De)){if(Math.abs(N.deltaPct)<0.5)continue;let R=N.deltaPct>0?"+":"";c.push(` frame ${N.name}: ${R}${N.deltaPct.toFixed(1)}% self-time (${(N.baseSelfUs/1000).toFixed(2)}ms -> ${(N.candSelfUs/1000).toFixed(2)}ms)`)}if(T.heapTypes)for(let N of T.heapTypes.slice(0,Me)){if(Math.abs(N.deltaPct)<0.5)continue;let R=N.deltaPct>0?"+":"";c.push(` heap ${N.type}: ${R}${N.deltaPct.toFixed(1)}% count (${N.baseCount} -> ${N.candCount})`)}}return c}var De=5,Me=5;function en(o,v){let c=[];for(let T of o.runs){if(T.phase!=="cpu"&&T.phase!=="heap")continue;let x=v.get(T.workloadId),C=x?ce(x):T.workloadId;if(T.phase==="cpu")if(T.cpu){c.push(`CPU capture - ${C} (instrumented, ${T.cpu.samplingIntervalUs}\xB5s interval, diagnostic wall ${te(T.diagnosticWallNs??0)}ms)`);let N=T.cpu.totals.reduce((R,U)=>R+U.selfUs,0)||1;for(let R of T.cpu.totals.slice(0,De)){let U=T.cpu.frames[R.frameIx],B=(R.selfUs/N*100).toFixed(1);c.push(` ${B.padStart(5)}% ${(R.selfUs/1000).toFixed(2).padStart(8)}ms self ${U?.name??"?"}`)}}else c.push(`CPU capture - ${C} (instrumented, no evidence captured)`);else if(T.heap){let N=((T.heap.heapSizeBytes??0)/1e6).toFixed(2);c.push(`Heap snapshot - ${C} (instrumented, ${T.heap.objectCount??"?"} objects, ${N}MB)`);for(let R of T.heap.typeCounts.slice(0,Me))c.push(` ${String(R.count).padStart(6)} ${R.type}`)}else c.push(`Heap snapshot - ${C} (instrumented, no evidence captured)`);for(let N of T.artifacts)c.push(` artifact: ${N.path}`);for(let N of T.warnings)c.push(` ! ${N.message}`)}return c}var n={table:Fe,json:Re,markdown:Ce,jsonl:Pe,collapsed:we,mermaid:Ie,speedscope:ke,cpuprofile:xe};var nn="node_modules/.cache/ostia",ue=1000;async function w(o){let v=s({runs:o.runs??null,warmup:o.warmup??null,cpu:o.cpu??!1,heap:o.heap??!1,cpuIntervalUs:o.cpuIntervalUs??ue}),T=`${o.outDir??nn}/artifacts`,x=[],C=[];for(let N of o.commands){let R=Array.isArray(N)?N:be(N),U=u(R,Array.isArray(N)?void 0:N);x.push(U);let B=await f({argv:R,cwd:o.cwd,env:o.env,runs:o.runs,warmup:o.warmup}),S=i({workload:U,configFingerprint:v,trials:B.trials,timing:B.timing,warnings:B.warnings});if(C.push(S),o.cpu){let A=`${S.id}-cpu.cpuprofile`,O=await pe({argv:R,cwd:o.cwd,env:o.env,artifactDir:T,fileName:A,intervalUs:o.cpuIntervalUs??ue});C.push(await Se({workload:U,phase:"cpu",configFingerprint:v,diagnosticWallNs:O.diagnosticWallNs,exitCode:O.exitCode,cpu:O.cpu,artifactPath:O.artifactPath,artifactKind:"cpuprofile",warnings:O.warnings}))}if(o.heap){let A=`${S.id}-heap.heapsnapshot`,O=await me({argv:R,cwd:o.cwd,env:o.env,artifactDir:T,fileName:A});C.push(await Se({workload:U,phase:"heap",configFingerprint:v,diagnosticWallNs:O.diagnosticWallNs,exitCode:O.exitCode,heap:O.heap,artifactPath:O.artifactPath,artifactKind:"heapsnapshot",warnings:O.warnings}))}}return r(x,C)}async function Se(o){let v=`${o.workload.id}-${o.phase}-${o.configFingerprint}`,c=o.artifactPath?[await P(v,o.artifactKind,o.artifactPath)]:[];return h({workload:o.workload,phase:o.phase,configFingerprint:o.configFingerprint,diagnosticWallNs:o.diagnosticWallNs,exitCode:o.exitCode,cpu:o.cpu,heap:o.heap,warnings:o.warnings,artifacts:c})}async function I(o,v={}){let c=k(o),T=s({intervalUs:v.intervalUs??ue,origin:v.origin??"inspector"}),x=(B)=>B.samples?.nodeIds.length===0?[{code:"empty-profile",message:"In-process capture produced zero samples."}]:[];if(v.origin==="jsc"){let{result:B,cpu:S,jit:A,diagnosticWallNs:O}=await he(o,v),W=h({workload:c,phase:"cpu",configFingerprint:T,diagnosticWallNs:O,cpu:S,jit:A,warnings:x(S),artifacts:[]});return{result:B,run:W}}let{result:C,cpu:N,diagnosticWallNs:R}=await fe(o,v),U=h({workload:c,phase:"cpu",configFingerprint:T,diagnosticWallNs:R,cpu:N,warnings:x(N),artifacts:[]});return{result:C,run:U}}
|
|
19
|
-
export{m,a,d,b,f,n,w,I};
|