Follow-up to #10478 / #10479, fixed in PR #10592, which resolves an instanceof receiver per value kind
instead of assuming a heap pointer. That fixed real segfaults — instanceof crashed on 1-, 3- and 5-byte
inline strings (taking down ajv, and with it every fastify schema route) and Object.create(proto).constructor
returned a bogus value (crashing lodash's isEqual). The cost lands on constructor-shaped object allocation.
Measurement (from PR #10592, re-measured on a fresh host, not carried over from the pre-loss run)
| fixture |
instructions |
bench_ctor_o.ts |
+32.6% |
bench_prim.ts |
−5.7% |
The +32.6% closely matches the +32.5% the original (lost) run recorded, so it reproduces across two
independent measurements on different hosts.
Attribution — it is NOT the EventEmitter subclass work in the same PR
bench_ctor_o never calls instanceof and never touches EventEmitter. The subclass-delegation change
shipped in the same PR was measured separately: +1.8% on a direct-EventEmitter-instance check, and a
63.9% reduction on mixed direct+subclass traffic (a subclass instance no longer burns through every
fallback probe on a guaranteed miss). The +32.6% belongs to the per-value-kind receiver resolution.
Why there is no honest "before" to compare against
The pre-fix binary segfaults on the inputs this path now handles correctly. There is no same-work
baseline; the old speed came from a fast path that was wrong. Only fix-vs-Node is meaningful.
Where to look
Follow-up to #10478 / #10479, fixed in PR #10592, which resolves an
instanceofreceiver per value kindinstead of assuming a heap pointer. That fixed real segfaults —
instanceofcrashed on 1-, 3- and 5-byteinline strings (taking down ajv, and with it every fastify schema route) and
Object.create(proto).constructorreturned a bogus value (crashing lodash's
isEqual). The cost lands on constructor-shaped object allocation.Measurement (from PR #10592, re-measured on a fresh host, not carried over from the pre-loss run)
bench_ctor_o.tsbench_prim.tsThe +32.6% closely matches the +32.5% the original (lost) run recorded, so it reproduces across two
independent measurements on different hosts.
Attribution — it is NOT the EventEmitter subclass work in the same PR
bench_ctor_onever callsinstanceofand never touchesEventEmitter. The subclass-delegation changeshipped in the same PR was measured separately: +1.8% on a direct-
EventEmitter-instance check, and a63.9% reduction on mixed direct+subclass traffic (a subclass instance no longer burns through every
fallback probe on a guaranteed miss). The +32.6% belongs to the per-value-kind receiver resolution.
Why there is no honest "before" to compare against
The pre-fix binary segfaults on the inputs this path now handles correctly. There is no same-work
baseline; the old speed came from a fast path that was wrong. Only fix-vs-Node is meaningful.
Where to look
crates/perry-runtime/src/object/instanceof.rsand the prototype/class-registry paths PR fix(runtime): resolve instanceof and Object.create constructor per receiver value kind #10592 touched(
prototype_objects.rs,class_object_props.rs,object_ops/prototype.rs,util_types.rs).could be resolved once. Check whether the value-kind discrimination can be hoisted or cached per call site
rather than repeated per constructor invocation.
Guarded builtin-named method dispatch is 3-4x Node on dayjs/decimal-shaped receivers (#10476 follow-up) #10594. They may share a root cause in how often a receiver is re-classified.