Skip to content

perf(runtime): preserve shared shape index across delete - #9067

Merged
proggeramlug merged 1 commit into
PerryTS:mainfrom
proggeramlug:fix/9006-shared-shape-index
Aug 29, 2026
Merged

perf(runtime): preserve shared shape index across delete#9067
proggeramlug merged 1 commit into
PerryTS:mainfrom
proggeramlug:fix/9006-shared-shape-index

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • clone the validated slot index when delete forks a shared keys array, while retaining the cheaper move for owned sources
  • add an end-to-end regression proving both the untouched sibling and deleting object's shifted clone keep valid accelerators
  • add a dedicated 500-key shared-sibling benchmark and changelog fragment; no version metadata is changed

Performance

On bench_shared_shape_delete.ts, 15 interleaved A/B pairs pinned to one CPU:

  • median: 171 ms -> 70 ms (-59.1%)
  • minimum: 111 ms -> 43 ms (-61.3%)
  • checksum: identical in every arm

Both executables were built from commit-matched compiler/runtime pairs on perrymaster.skelpo.net.

Verification

  • cargo test -p perry-runtime shared_delete_preserves_the_sibling_shape_index -- --nocapture
  • cargo test --quiet -p perry-runtime -- --test-threads=1 (2787 passed, 4 ignored)
  • RUSTFLAGS="-D warnings" cargo check -p perry-runtime --all-targets
  • scripts/run_lint_gates.sh with compile tier skipped (all 58 runnable lint gates passed; compile covered by the targeted warning-denied check)
  • cargo clippy -p perry-runtime --lib

Closes #9006.

Summary by CodeRabbit

  • Bug Fixes

    • Improved performance when deleting properties from objects that share internal structures.
    • Preserved fast property lookups on related objects after a deletion.
    • Added regression coverage to ensure sibling objects remain unaffected.
  • Benchmarks

    • Added benchmarking for shared-object property deletion and subsequent lookups.
    • Demonstrated a significant reduction in processing time for this scenario.

Clone the validated slot index when delete forks a shared keys array, leaving sibling objects accelerated while retaining the move path for owned sources. Add an end-to-end sibling regression and a dedicated A/B benchmark for PerryTS#9006.
@proggeramlug
proggeramlug force-pushed the fix/9006-shared-shape-index branch from 3fd3871 to 0689349 Compare August 29, 2026 16:13
@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 53004497-f270-4943-a9ca-4c30a21d5e94

📥 Commits

Reviewing files that changed from the base of the PR and between 5792671 and 0689349.

📒 Files selected for processing (5)
  • benchmarks/bench_shared_shape_delete.ts
  • changelog.d/9067-preserve-shared-delete-index.md
  • crates/perry-runtime/src/object/delete_rest.rs
  • crates/perry-runtime/src/object/shapes.rs
  • crates/perry-runtime/src/object/shapes_slot_list.rs

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The runtime preserves shape-index accelerators for sibling objects when a delete compacts a shared keys array. Owned indexes retain move behavior. A regression test and a benchmark validate sibling index preservation and repeated dynamic lookups.

Changes

Shared delete index preservation

Layer / File(s) Summary
Runtime index migration
crates/perry-runtime/src/object/shapes.rs, crates/perry-runtime/src/object/shapes_slot_list.rs, crates/perry-runtime/src/object/delete_rest.rs
ShapeIndex is cloneable. shape_index_migrate_after_delete clones indexes for shared keys arrays and moves indexes for owned arrays. The delete path passes the shared-state flag.
Regression validation and benchmark
crates/perry-runtime/src/object/shapes_slot_list.rs, benchmarks/bench_shared_shape_delete.ts, changelog.d/9067-preserve-shared-delete-index.md
The regression test checks both sibling and deleting-object indexes. The benchmark measures repeated deletes and dynamic sibling lookups. The changelog records the implementation and benchmark results.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 06893

This change preserves shared shape indexes during property deletion while keeping owned-array deletions efficient, with regression coverage and passing validation checks. No actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant js_object_delete_field
  participant shape_index_migrate_after_delete
  participant sibling_Object_hasOwn
  js_object_delete_field->>shape_index_migrate_after_delete: pass shared keys-array state
  shape_index_migrate_after_delete->>shape_index_migrate_after_delete: clone shared index or move owned index
  sibling_Object_hasOwn->>shape_index_migrate_after_delete: perform indexed sibling lookup
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main runtime performance change: preserving a shared shape index across deletion.
Description check ✅ Passed The description provides a clear summary, concrete changes, linked issue, performance results, and verification commands. It does not use every template heading or complete the checklist, but it conta…
Linked Issues check ✅ Passed The changes satisfy issue [#9006] by cloning the slot index for shared keys arrays, retaining the move path for owned arrays, adding regression coverage, and providing the requested benchmark.
Out of Scope Changes check ✅ Passed The implementation, regression test, benchmark, and changelog fragment directly support the linked issue and stated objectives. No unrelated code changes are evident.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 4 files. (1 skipped: 1 u…
Full details: Description check

Explanation

The description provides a clear summary, concrete changes, linked issue, performance results, and verification commands. It does not use every template heading or complete the checklist, but it contains the required substantive information.

Full details: Docstring Coverage

Explanation

Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 4 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Merged — this closes #9006, which I filed against #9002, and the fix is exactly the right shape: clone for a shared source, move for an owned one.

The asymmetry is the whole point and the comment states it well — forking a shared array is a genuine shape transition (siblings keep naming the old address), while replacing an owned array transfers its identity. get().cloned() vs remove() on that one predicate is a small change for a −59% result.

Verified end-to-end rather than only at the unit level: 25 same-shape siblings of 60 keys with a delete on one — every untouched sibling keeps all 60 keys and correct values, the deleter has 59 with the right hole; ten siblings each deleting a different key; an owned (non-shared) receiver; and delete-then-re-add on one sibling leaving the other untouched. Byte-identical to node, zero sibling corruption.

Worth being precise about what changed, since #9006 was explicitly a throughput issue: correctness was never at risk either way — a stranded sibling rebuilt its index and got the right answer. What this fixes is that it no longer has to.

One process note. Your verification ran run_lint_gates.sh with the compile tier skipped, substituting a targeted cargo check -p perry-runtime --all-targets. I ran the full tier and it's green here, so no harm — but that substitution is exactly what hid the blocker on #9066: perry-runtime compiles standalone with default features and fails under -p perry's feature unification, which a single-crate --all-targets check cannot see. Worth running the compile tier unskipped on anything touching a crate that others depend on.

Validation: the new shared_delete_preserves_the_sibling_shape_index test passes, perry-runtime --lib 2808/0, perry --bins 1066/0, fmt --check, run_lint_gates.sh all 60 gates passed; 2 CI-only skipped (compile tier included).

@proggeramlug
proggeramlug merged commit 18aa443 into PerryTS:main Aug 29, 2026
18 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

shape_index_migrate_after_delete steals a shared keys array's index, stranding sibling objects

1 participant