Archive — history, not state. Kept for its reasoning and its evidence; its plan is closed.
Profiling axes: separating what carries to native from what doesn’t
Performance here has three distinct dimensions, and improvements must be attributed to the right one — VM-only wins must never be mistaken for wins that carry to AOT native code:
- Mcode in the hot path (carries fully to native): exact dispatched
instruction counts per bench, now with per-opcode histograms
(
$runtime.op_counts(), gated by$runtime.count_instructions(true)— a 256-entry counter array bumped in the same untaken branch as the instruction counter; zero cost when off). - VM dispatch efficiency (interpreter-only): ns per dispatched instruction, derived per bench (wall / instructions). A change that moves wall time but not instructions and not allocation is VM-only.
- Runtime services (carries fully to native — same allocator, GC,
record hash, text machinery under AOT): allocation bytes/objects, GC
cycles, and GC time (
gc_ms, exposed through$runtime.vm_stats()), plus four service-targeted benches whose wall time is dominated by one service: record_lookup (hash hit/miss), record_grow (insert+rehash), text_search, gc_churn (collector throughput with a surviving live set).
All benches are plain pit programs, so the identical suite runs under a future AOT backend for direct comparability.
Baseline snapshot (M-series dev machine, gate = make budget):
| bench | ms | instr | ns/i | alloc | GCs |
|---|---|---|---|---|---|
| numeric_loop | 7.3 | 11.0M | 0.66 | 1.0KB | 0 |
| string_build | 0.3 | 220k | 1.30 | 330KB | 2 |
| array_sweep | 2.5 | 2.60M | 0.97 | 2.1MB | 3 |
| record_churn | 11.1 | 5.25M | 2.11 | 2.7KB | 0 |
| closure_calls | 7.6 | 2.85M | 2.66 | 10.8MB/150k | 135 |
| record_lookup | 7.1 | 5.25M | 1.34 | 47KB | 0 |
| record_grow | 5.9 | 2.07M | 2.83 | 8.3MB/14k | 41 |
| text_search | 1.2 | 481k | 2.47 | 969KB/20k | 5 |
| gc_churn | 6.2 | 1.70M | 3.65 | 8.8MB/100k | 57 |
The ns/instr spread (0.66 pure arithmetic -> 3.65 GC-bound) is the axis separation working: services, not dispatch, dominate the heavy benches.
First finding surfaced by the new columns: text_search performs ~1 allocation per search() call (20,024 for 20k searches) — the search intrinsic allocates on hit, a runtime-services target that would carry to native.
Gating: instructions ±2%, alloc bytes ±5%, wall ±35%; gc count/time and ns/instr reported, not gated (heap-state / derived).
Source: plans/archive/perf-2026-07/2026-07-08-profiling-axes.md