Skip to content

test(perf-proofs): prove null-prototype entities are slower, not faster - #14

Merged
jakeklassen merged 2 commits into
mainfrom
perf/null-proto-entities-proof
Oct 14, 2026
Merged

jakeklassen merged 2 commits into
mainfrom
perf/null-proto-entities-proof

Conversation

@jakeklassen

Copy link
Copy Markdown
Owner

What

Investigates the null-prototype optimization from this nodeland.dev article against the objecs core lib, and lands the finding as a perf-proofs benchmark + a CLAUDE.md convention.

Finding: null-prototype entities are slower, not faster

The article's counterintuitive claim holds and applies directly to objecs's hot path. { __proto__: null } and Object.create(null) start in V8 dictionary mode (hash-table props, no hidden class / inline caches), and reads never restore fast mode. Measured on Node 26.11.1 (Ryzen 9 7950X3D), plain literal = baseline:

entity construction create iterate (1000) presence (1000)
{ __proto__: null } 11× slower 6.6× slower 15× slower
Object.create(null) 19× slower 6.2× slower 15× slower
setPrototypeOf(obj, null) 74× slower 4.0× slower 9.6× slower
  • Dictionary mode, not megamorphism, is the dominant cost — isolating each kind in its own monomorphic access site barely narrowed the gap (iteration 8.5× → 6.6×), so mixing shapes adds only ~30% on top.
  • setPrototypeOf(obj, null) keeps fast properties (reads fastest of the three null-proto forms) but the setPrototypeOf call makes it the slowest to create — only worth it for long-lived, read-many objects, never per-frame churn.

Core lib impact: none to fix, but it validates ADR 0001

The core lib creates zero null-prototype objects (it uses Map/Set for lookups, plain arrays for storage), so there is no speedup to extract — the article instead independently confirms the dictionary-mode reasoning behind ADR 0001 (= undefined not delete). This is user-facing guidance: a user reaching for null-proto entities would quietly take a 6–15× hit.

Changes

  • test(perf-proofs): null-proto-vs-plain.ts — creation / iteration / presence across all four construction styles, with monomorphic per-kind access sites to isolate dictionary mode from megamorphism. Plus CLAUDE.md convention + perf-proofs README entry.
  • docs(perf-proofs): fix a stale comment in delete-vs-set-undefined.ts that still claimed objecs uses delete (ADR 0001 reversed that).

Verification

pnpm lint ✅ · perf-proofs typecheck ✅ · both object benchmarks run clean. No core objecs API or behavior change.

🤖 Generated with Claude Code

jakeklassen and others added 2 commits October 13, 2026 21:14
Adds null-proto-vs-plain benchmark testing the claim from
https://adventures.nodeland.dev/archive/optimizing-objects-with-null-prototypes/.
Null-prototype objects start in V8 dictionary mode, so `{ __proto__: null }`
and `Object.create(null)` entities are 6-15x slower to iterate and
archetype-match than plain literals — the same dictionary-mode penalty as
`delete`. `setPrototypeOf(obj, null)` keeps fast properties (reads fastest of
the three) but is 74x slower to create. Measured on Node 26.11.1.

Isolating each kind in its own monomorphic access site barely moved the gap
(iteration 8.5x -> 6.6x), confirming dictionary mode, not megamorphism, is the
dominant cost. Documents the finding as a CLAUDE.md convention alongside the
existing delete-vs-set-undefined / ADR 0001 guidance. No core lib change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The comment claimed objecs uses `delete` in removeEntityComponents; ADR 0001
reversed that to `= undefined`. Fixes the comment to match current behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@netlify

netlify Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for objecs ready!

Name Link
🔨 Latest commit 3fcd7d0
🔍 Latest deploy log https://app.netlify.com/projects/objecs/deploys/6aced790c30f13f42a1b82ab
😎 Deploy Preview https://deploy-preview-14--objecs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@jakeklassen
jakeklassen merged commit c1c1787 into main Oct 14, 2026
7 checks passed
@jakeklassen
jakeklassen deleted the perf/null-proto-entities-proof branch October 14, 2026 11:33
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.

1 participant