Skip to content

perf: re-profile of construction on v0.5.1325 — typed_shape_layout_entry ~25% is the new top lever, js_array_length ~10-15% is unexplained #7578

Description

@proggeramlug

Fresh leaf profile of push_cls on v0.5.1325 — the construction picture has changed

The #7469 decomposition in docs/engine-plan.md is from the v0.5.1299 era and
its top rows are now closed. Re-profiled on current main (v0.5.1325,
f3d290874, own --release build, PERRY_DEBUG_SYMBOLS=1), two independent
sample runs:

item run 1 (766 leaf) run 2 (379 leaf)
gc::layout::typed_shape_layout_entry 26.4% 24.8%
write barriersjs_write_barrier_slot + write_barrier_decoded_parent + barrier_child_prologue + incremental_mark_barrier_value 25.0% 26.9%
user code — chunk + Node_constructor 23.9% 20.8%
js_array_length 10.2% 15.0%
js_ctor_return_override + constructor_return_overrides_this 4.0% ~3%
is_registered_set + is_registered_map 3.1%
gc::layout_tables::layout_forget_object 1.7% 2.9%
_tlv_get_addr 1.0%

Confirms the last campaign worked. _tlv_get_addr is 1.0%, from 27.0%
(#7565). layout_forget_object is 1.7–2.9%, from 14.5% (#7525/#7532).
Neither is a lever any more.

Method, stated plainly. Dev Mac under load ~8, not the pinned quiet mini, so
these are leaf-share figures and not publishable wall-clock. A share
decomposition is far more robust to external load than a timing is — the process
executes the same instructions either way — and the two runs agree within a few
points on every row. Re-measure on perry-macos.local before quoting absolute
times.

Two levers, and one surprise

1. typed_shape_layout_entry ~25% — the top single function

push_cls's Node takes the js_gc_declare_typed_shape_layout path
(verified in the emitted IR: 1 call to declare, 0 to init), so this is not
the ValidateSlots loop — #7515/#7532 are working as intended. It is the install
itself, which layout.rs:1022 documents as reducing, on a shape_install::hit,
to "the two header bit-writes shape_install_shared would have performed".

If that is what it costs on the hit path, why is it an FFI call at all? The
emitted call is

call void @js_gc_declare_typed_shape_layout(i64 %r55, i32 2,
    ptr @perry_typed_shape_raw_f64_mask_push_cls_ts__Node, i32 1, ptr null, i32 0)

— every argument but the object pointer is a compile-time constant for the
class. This is the exact shape #7566 just won 1.81× on: an FFI call whose real
cost is the call plus the thread-local resolutions it performs, where the work
itself is a couple of stores. The same remedy may apply (emit the hit path
inline, keep the call as the slow path), with the same size discipline —
#7566's loop-membership gate is the precedent for not paying bytes at every
site.

2. Write barriers ~25% — now tied for the top spot

Owned by #7511, and correctness-first: a missed barrier is a use-after-free,
not a slowdown. Noting only that it is no longer 16.1% — as a share it has grown
because everything around it shrank. PERRY_WRITE_BARRIERS=0 cannot be used to
bound it
(it also switches the collector out of evacuating mode and makes the
bench slower).

3. js_array_length at 10–15% — the surprise

push_cls's inner loop is keep.push(new Node(v, w)). That an array-length read
is 10–15% of the profile is hard to justify: the length is a field of the
ArrayHeader the push already has in hand, and js_array_length is in the
checker's NONCOLLECTING set precisely because it is a plain accessor
(array/indexing.rs:537). This looks like one FFI call per push for a load that
should fold, and it is the cheapest of the three to investigate.

Not levers, but odd

is_registered_set + is_registered_map at ~3% in a benchmark containing
no Set and no Map. That is the shape #7510 already hit once — one immortal
side-table entry nullifying an is_empty() fast path — and worth a look while
someone is in this code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions