Skip to content

perf: property access is 13x-112x node warm — two static reads cost 162 instructions against node's 12, and a const-key read costs 7.6x the same read spelled statically #10761

Description

@proggeramlug

Summary

Property access — the most executed operation in JavaScript — costs perry 13× to 112× what node costs, measured with both JITs warm.

Fitted at two widely separated ranges so warmup contamination is visible. perry's figures are identical at both ranges (it is AOT and has no warmup); node's and bun's fall sharply, which is why a small-range fit understates this badly.

operation fit range perry node bun
O.a + O.b — two static reads 20k→200k 162 53 21
500k→5M 162 12 25
O[K] + O[J] — two const-key reads 20k→200k 1236 41 84
500k→5M 1236 11 15
O.v = k; h += O.v — write then read 20k→200k 182 74 71
500k→5M 183 12 13
String(k % 1000) — number to string 20k→200k 433 75 78
500k→5M 435 114 32

The gaps, warm

operation perry vs best of node/bun
two static property reads 13.5× (162 vs 12)
two const-key property reads 112× (1,236 vs 11)
property write + read 15× (183 vs 12)
String(n) 13.6× (435 vs 32)

A single static property read costs perry ~81 instructions. node does it in ~6.

Why the const-key row is the place to start

O[K] with a hoisted const K = "a" costs 1,236 for two reads — 112× node — while the identical reads spelled O.a cost 162. That is a 7.6× penalty for the spelling alone, on a pure hit path with no missing keys, no prototype walk, and no invalidation obligations whatsoever.

It is the same class of defect as #10743 (a[i] += 1 costing 11.5× a[i] = a[i] + 1): a fast path exists and a common spelling cannot reach it. #10743 turned out to be tractable — the fix was in the matcher, not the lowering — and it took that row from 277 to 25.5.

Relationship to existing issues

Measurement notes for anyone reproducing

Fit per-op costs at two widely separated ranges and check the value is stable. This campaign published three wrong tables before adopting that: one comparing totals (crediting perry with node's ~95M startup), one fitting deltas inside node's warmup, and one using a benchmark shape real code does not use. perry's flatness across ranges is the detector — if perry's number does not move and the other runtime's moves 4×, the other runtime is not yet at steady state.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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