Context
honest_bench JSON pipeline (100 records, 21 KB input). Post-#47/#48 median: Perry 222 ms vs Zig 36 ms (6.2×). Profile of a 2000-iteration loop variant shows 96% of samples sit on the return address of a bl target that reads offset 0x20 of a heap header and does a thread-local lookup — i.e. a generic property lookup firing once per r.id/r.name/r.email/etc. access inside the hot transform loop.
The pattern
const records = JSON.parse(text) as any[]; // homogeneous-shape records
for (let i = 0; i < records.length; i++) {
const r = records[i];
if (r.active !== true) continue; // ← hot
out.push({
id: r.id, name: r.name, email: r.email, // ← each .foo = lookup
age: r.age, country: r.country, tags: r.tags,
score: r.score, active: r.active, addr: r.addr,
display_name: r.name.toUpperCase(),
age_group: age < 30 ? 'young' : age < 50 ? 'mid' : 'senior',
});
}
Every .field on r goes through the generic lookup. At 100 records × 11 fields × 2000 outer iters = 2.2M lookups, the 30-instruction helper path dominates.
Proposal — polymorphic inline cache (PIC) on shape-stable receivers
When a hot PropertyGet(receiver, name) is statically typed (r: any[]'s element shape stable across records) or observed monomorphic, emit:
; at each hot access site:
ldr w_shape, [x_obj, #shape_offset] ; 1 insn: shape id
cmp w_shape, #CACHED_SHAPE
b.ne .slow_path
ldr x_val, [x_obj, #CACHED_FIELD_OFFSET] ; 1 insn: direct load
— replacing the current 30+-instruction helper call.
Scope ladder
- Monomorphic hot site (what this issue mostly covers): one shape seen, one fast path + one slow fallback. This is the 80% win.
- Bimorphic / trimorphic: inline up to 3 shape checks before falling back. Standard V8/JSC pattern.
- Object-literal shape inference:
{id: r.id, name: r.name, ...} constructed from stable-shape r produces a statically-predictable output shape; allocate once with shape pre-cached.
Impact projection
JSON pipeline at 100 records: 222 ms → ~70–100 ms band (2-3× remaining gap vs Zig's 36 ms is then the fixed cost of parse + stringify, not the transform loop).
Secondary interactions
- Unblocks
JSON.stringify of homogeneous-shape arrays — a shape-aware stringifier can iterate the known offset table once instead of hashing each key per record.
- Unblocks the same-shape output object allocation in the
.push({…}) — if the shape is known at compile time, the arena allocator can bump-allocate N slots and fill them directly from the source record's offsets.
Repro
benchmarks/honest_bench/workloads/1_json_pipeline/perry/json_pipeline.ts on assets/input_small.json.
Context
honest_benchJSON pipeline (100 records, 21 KB input). Post-#47/#48 median: Perry 222 ms vs Zig 36 ms (6.2×). Profile of a 2000-iteration loop variant shows 96% of samples sit on the return address of abltarget that reads offset0x20of a heap header and does a thread-local lookup — i.e. a generic property lookup firing once perr.id/r.name/r.email/etc. access inside the hot transform loop.The pattern
Every
.fieldonrgoes through the generic lookup. At 100 records × 11 fields × 2000 outer iters = 2.2M lookups, the 30-instruction helper path dominates.Proposal — polymorphic inline cache (PIC) on shape-stable receivers
When a hot
PropertyGet(receiver, name)is statically typed (r: any[]'s element shape stable across records) or observed monomorphic, emit:— replacing the current 30+-instruction helper call.
Scope ladder
{id: r.id, name: r.name, ...}constructed from stable-shaperproduces a statically-predictable output shape; allocate once with shape pre-cached.Impact projection
JSON pipeline at 100 records: 222 ms → ~70–100 ms band (2-3× remaining gap vs Zig's 36 ms is then the fixed cost of parse + stringify, not the transform loop).
Secondary interactions
JSON.stringifyof homogeneous-shape arrays — a shape-aware stringifier can iterate the known offset table once instead of hashing each key per record..push({…})— if the shape is known at compile time, the arena allocator can bump-allocate N slots and fill them directly from the source record's offsets.Repro
benchmarks/honest_bench/workloads/1_json_pipeline/perry/json_pipeline.tsonassets/input_small.json.