ReviewOS

zig-utils/zig-js

Runtime: make named property deletion proportional

#522
Closed chris opened this 8 days ago ·
8 days ago

Parent: #493 Related: #480, #472, #521, #459

Defect

Object.deleteNamedDataOwnInternal reconstructs the complete shape, slot array, attributes, and surviving key list for every configurable named-property deletion. Deleting n distinct properties from one wide object repeatedly enumerates and rebuilds the shrinking object, producing superlinear CPU and extreme transient/arena memory growth.

A ReleaseFast 4,096-property setup witness was sampled after more than 110 seconds with a 3.3 GB physical footprint. Every sampled main-thread stack was still inside deleteNamedDataOwnInternal, dominated by repeated string equality, setOwnUnlocked, and full-object rebuild work. This was discovered while isolating #521; own-key enumeration is tracked there, while deletion/rebuild needs its own representation-level fix.

Required change

  • Add deterministic 1K/2K/4K delete and delete/re-add growth witnesses with exact survivor/order/descriptor checksums.
  • Design owned tombstones, slot indirection, or another proportional deletion representation that preserves hidden-class transition correctness, insertion order, accessor/data conversion, attributes, dense/sparse indices, and moving-GC relocation.
  • Bound compaction/rebuild work and historical capacity; allocation failure must leave the object exact and usable.
  • Preserve inline-cache invalidation and no-GIL property-lock synchronization.
  • Add ordinary/accessor/indexed, OOM, moving-GC, no-GIL, and TSan coverage.
  • Record exact-parent CPU, allocation, and memory evidence with no ordinary get/set regression.

No-workaround rules

No property-count limit, deletion refusal, dictionary-mode semantics shortcut, stale slot exposure, skipped descriptor/order behavior, global cache, or benchmark-specific path.

8 days ago

Cross-link from completed #521: the own-key enumeration path is now linear and scratch-bounded without touching deletion reconstruction. Exact ordered growth fell to 0.194/0.404/0.670 ms at 1024/2048/4096 keys. The setup-time deletion stacks and multi-gigabyte footprint that motivated this issue therefore remain an independent defect, not an enumeration workaround. #521 closure evidence: /zig-utils/zig-js/issues/521#issuecomment-5245487200.

8 days ago

Implemented and verified on main.

Commits:

Exact-parent ReleaseFast results (7 order-balanced pairs):

rowmedian wall timemedian instructionspeak RSS
delete 1K2.270 → 0.221 ms39,158,968 → 3,023,68923,511,040 → 18,956,288 B
delete 2K11.876 → 0.399 ms118,695,636 → 5,873,96630,801,920 → 21,856,256 B
delete 4K88.625 → 0.799 ms528,963,775 → 11,692,36045,170,688 → 27,279,360 B
re-add 4K88.746 → 0.846 ms532,216,137 → 11,897,14245,301,760 → 27,361,280 B

The 4K live retained-RSS rows fell from 45,137,920 to 27,197,440 bytes for delete and 45,154,304 to 27,262,976 bytes for delete/re-add. Attribution shows similar backing allocation-call counts, so no call-count claim is made; backing bytes fell 52.4–52.7%, growth calls fell 52.2–82.0%, and retained backing growth fell 55.1–55.5%. The 25,000-iteration ordinary get/set control remained flat at 0.383 → 0.376 ms and 4,060,947 → 4,065,870 instructions.

Verification:

  • full unit suite: 1,558 passed, 1 skipped, 0 failed, 0 leaked across 10 shards
  • focused deletion TSan cluster: 14/14 passed, zero failures/leaks
  • affected JIT invalidation test passed
  • six relevant test262 subtrees: 1,593 → 1,593, zero gains and zero regressions

All required semantics and failure/synchronization cases are covered, and the evidence is checked in under docs/.data/exact-parent-named-delete-*. Closing as complete.

Sign in to comment on this issue.