Skip to content

perf(engine): back strong Map/Set with a SameValueZero-keyed ordered hash#890

Merged
frostney merged 5 commits into
mainfrom
claude/affectionate-mahavira-eb0c9a
Jun 28, 2026
Merged

perf(engine): back strong Map/Set with a SameValueZero-keyed ordered hash#890
frostney merged 5 commits into
mainfrom
claude/affectionate-mahavira-eb0c9a

Conversation

@frostney

@frostney frostney commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • What. Back strong Map and Set with a single new TGocciaOrderedValueMap — a SameValueZero-keyed, insertion-ordered hash store and the first production consumer of the generic insertion-ordered TOrderedMap (ADR 0019). It overrides HashKey/KeysEqual with a hash exactly consistent with IsSameValueZero. get/set/has/delete go from O(n) (a linear IsSameValueZero scan) to O(1) amortized; build/probe loops from O(n²) to O(n); set-algebra (union/intersection/difference/symmetricDifference/isSubsetOf/isSupersetOf/isDisjointFrom) from O(n·m) to O(n+m). Both Map and Set use the one store (Set stores key = value) — no per-collection subclass and no parallel structure.
  • Conformance fixes (discovered during implementation; not covered by the prior suite). Adopting the spec [[MapData]] model fixed real bugs, each with new regression tests that fail on the old flat-list code:
    • -0 keys are now canonicalized to +0 (ES2026 §24.5.1 CanonicalizeKeyedCollectionKey).
    • forEach/iterators now visit entries appended during iteration (ES2026 §24.1.3.5 re-reads the length each step).
    • deleting during forEach no longer throws RangeError: Argument out of range.
    • deleting the current key during for...of no longer skips the next entry or leaves size wrong (§24.1.5.1).
    • an omitted Map/Set argument is now treated as the undefined key/value (m.set("k"), m.get(), s.add()) rather than a no-op.
  • Design / constraints. Deletes tombstone (no shift); iterators hold a physical cursor over the live entry array. The inherited tombstone-reclaiming compaction renumbers entries, so a new CanCompact virtual on TOrderedMap (default True, behavior unchanged for existing users) gates it on a per-store live-iterator counter: while any iteration is in flight the store only Grows (which preserves indices) and never compacts, then reclaims tombstones once idle. The SameValueZero hash is the load-bearing invariant (an inconsistent hash silently loses entries) and is guarded by a Pascal invariant test.
  • Re-entrancy hardening (review round). Internal cursor walks that recurse into user code retain the store so a mutating getter/callback cannot renumber entries mid-walk: structuredClone, deep-equal (toEqual), and the REPL formatter. The Set operations are bounded to the members present when the operation began (ES2026 §24.2.4): intersection/isSubsetOf/isDisjointFrom retain the store and iterate the original physical slots (EntrySlotCount + NextEntryBounded), so a has callback that appends — or deletes-then-re-adds — a member cannot make it be revisited; difference uses a value snapshot because the spec builds its result from a copy of O.[[SetData]]. Set canonicalization moved into the store (AddSetMember) so callers cannot leak -0 into the value slot.
  • Non-goals. Identity-keyed WeakMap/WeakSet keep THashMap; the content-hash vs identity-hash distinction is recorded in the ADR. No wider collections rewrite.
  • Known limitation / follow-up. An iterator that is partially consumed and then abandoned (never exhausted or closed) keeps the compaction gate held until the collection is cleared — a safe degradation (no wrong results; the common cases — full for...of, spread, forEach, exhausted or return()-closed iterators — always release the gate), matching how engines pin a collection while an iterator references it. A destructor-based release is unsafe here (the GC sweep frees a Map before its iterators via RecycleFree, so the destructor would touch freed memory); the correct fix is GC-driven release during the mark phase, tracked as a follow-up and documented in ADR 0077.
  • ADR: docs/adr/0077-samevaluezero-ordered-collections.md.
  • Closes perf: back strong Map/Set with a SameValueZero-keyed insertion-ordered hash #807

Testing

  • Verified no regressions and confirmed the new feature or bugfix in end-to-end JavaScript/TypeScript tests — full suite 10,983/10,983 passing in both interpreter and bytecode modes, including new Map/Set regression tests for every fix above (incl. mutating-has set operations: appended, deleted, and delete-then-readd); Map/Set re-validated under ./build.pas --prod (the -O4/FASTMATH-sensitive path).
  • Updated documentation — ADR 0077 added and indexed.
  • Optional: Verified no regressions and confirmed the bugfix in native Pascal tests (value types changed) — Goccia.Values.OrderedValueMap.Test.pas (17 cases: hash/equality consistency over a value matrix incl. distinct-instance collapse, -0 canonicalization, AddSetMember, tombstone cursor, bounded iteration, iterator-gate counter); all 39 Pascal unit-test binaries pass.
  • Optional: Verified no benchmark regressions — the gain is asymptotic (O(n²)→O(1) per op; O(n·m)→O(n+m) set-algebra); benchmarks/collections.js uses small N (≤50) where O(n²)≈O(n), so no benchmark number is headlined (consistent with the same-runner-benchmark caveat in ADR 0076).

🤖 Generated with Claude Code

…hash

Strong Map and Set stored entries in a flat list and located keys by a
linear SameValueZero scan, making get/set/has/delete O(n), build/probe
loops O(n^2), and set-algebra O(n*m). Back both with a single new
TGocciaOrderedValueMap — the first production consumer of the generic
insertion-ordered TOrderedMap (ADR 0019) — whose HashKey/KeysEqual are
overridden with a hash exactly consistent with IsSameValueZero. Ops are
now O(1) amortized and set-algebra O(n+m); insertion order is preserved.

Deletes tombstone instead of shifting and iterators hold a physical
cursor, matching the spec [[MapData]] model. Compaction (which renumbers
entries) is gated by a per-store live-iterator counter via a new
CanCompact virtual on TOrderedMap, so live cursors never observe a
renumbering. Insertion canonicalizes -0 to +0 (ES2026 §24.5.1).

Adopting the spec model fixed four pre-existing conformance bugs the
flat-list code carried, none covered by the prior suite: -0 keys stored
un-normalized; forEach/iterators not seeing entries appended during
iteration; deleting during forEach throwing "Argument out of range"; and
deleting the current key during for...of skipping the next entry. JS
regression tests for all four ship here, plus a Pascal invariant test
for the hash/equality consistency.

Closes #807

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

vercel Bot commented Jun 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
gocciascript-homepage Ignored Ignored Preview Jun 28, 2026 12:07am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 25ab9dea-9548-4879-9acb-c654e94f92da

📥 Commits

Reviewing files that changed from the base of the PR and between e738827 and 7b65c46.

📒 Files selected for processing (1)
  • source/units/Goccia.Values.SetValue.pas
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/units/Goccia.Values.SetValue.pas

📝 Walkthrough

Walkthrough

Replaces flat-list storage for strong Map and Set with a SameValueZero-keyed ordered store, switches iterators and consumers to cursor-based traversal, and adds conformance tests plus an ADR documenting the design.

Changes

SameValueZero-keyed ordered Map/Set store

Layer / File(s) Summary
CanCompact hook in TOrderedMap
source/shared/OrderedMap.pas
Adds CanCompact and gates Compact in Add with it.
TGocciaOrderedValueMap core
source/units/Goccia.Values.OrderedValueMap.pas
Adds SameValueZero hashing/equality, -0 canonicalization, set-member insertion, cursor traversal, and iterator-retention tracking.
Map storage, iterators, and consumers
source/units/Goccia.Values.MapValue.pas, source/units/Goccia.Values.Iterator.Concrete.pas, source/units/Goccia.Builtins.GlobalMap.pas
Moves Map onto the ordered store, rewrites map iterators to use live cursors, and updates MapGroupBy to read grouped values with TryGetValue.
Set storage, iterators, and consumers
source/units/Goccia.Values.SetValue.pas, source/units/Goccia.Values.Iterator.Concrete.pas, source/units/Goccia.Builtins.Globals.pas, source/units/Goccia.Evaluator.Comparison.pas, source/units/Goccia.REPL.Formatter.pas
Moves Set onto the ordered store, rewrites set operations to cursor/snapshot iteration, and updates structured clone, deep equality, and REPL formatting to use the new APIs.
Ordered store and conformance tests
source/units/Goccia.Values.OrderedValueMap.Test.pas, tests/built-ins/Map/prototype/*, tests/built-ins/Set/prototype/*
Adds tests for SameValueZero key handling, canonicalization, ordered iteration, live mutation during iteration, and set-algebra mutation cases.
ADR 0077
docs/adr/0077-samevaluezero-ordered-collections.md, docs/adr/README.md
Adds the design record and updates the ADR index.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

  • #807 — Directly matches the SameValueZero-keyed ordered store change for strong Map/Set, including hashing, ordered iteration, and iterator semantics.
  • #841 — Overlaps with the added Map/Set conformance coverage for live iteration and key-equality behavior.

Possibly related PRs

  • frostney/GocciaScript#24 — Updates structuredClone internals to use cursor-based Map/Set iteration, which overlaps with this PR’s consumer rewiring.
  • frostney/GocciaScript#429 — Touches the same Set algebra implementation area that this PR rewrites for cursor/snapshot iteration.
  • frostney/GocciaScript#421 — Modifies the same Map/Set prototype implementation area around forEach and callback handling.

Suggested labels

performance, new feature, spec compliance, internal

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: a SameValueZero-keyed ordered hash backing strong Map/Set.
Description check ✅ Passed The description matches the template with a Summary section and Testing checklist, and it includes implementation constraints and validation notes.
Linked Issues check ✅ Passed The PR implements the requested SameValueZero ordered store, updates internal consumers, preserves iterator semantics, and adds the ADR and regression tests.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes are present; the added docs, tests, and internal consumer updates all support the stated Map/Set storage work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Suite Timing

Test Runner (interpreted: 10,987 passed; bytecode: 10,987 passed)
Metric Interpreted Bytecode
Total 10987 10987
Passed 10987 ✅ 10987 ✅
Workers 4 4
Test Duration 18.53s 19.73s
Lex (cumulative) 523.6ms 514.3ms
Parse (cumulative) 369.3ms 360.3ms
Compile (cumulative) 748.3ms
Execute (cumulative) 82.2ms 48.12s
Engine Total (cumulative) 975.1ms 49.74s
Lex (avg/worker) 130.9ms 128.6ms
Parse (avg/worker) 92.3ms 90.1ms
Compile (avg/worker) 187.1ms
Execute (avg/worker) 20.5ms 12.03s
Engine Total (avg/worker) 243.8ms 12.43s

Memory

GC rows aggregate the main thread plus all worker thread-local GCs. Test runner worker shutdown frees thread-local heaps in bulk; that shutdown reclamation is not counted as GC collections or collected objects.

Metric Interpreted Bytecode
GC Live 282.89 MiB 280.31 MiB
GC Peak Live 336.05 MiB 330.51 MiB
GC Allocated During Run 529.38 MiB 473.54 MiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 195 195
GC Collected Objects 2,600,438 2,170,625
Heap Start Allocated 181.0 KiB 181.0 KiB
Heap End Allocated 3.65 MiB 3.65 MiB
Heap Delta Allocated 3.48 MiB 3.48 MiB
Heap Delta Free 1.68 MiB 1.68 MiB
Benchmarks (interpreted: 436; bytecode: 436)
Metric Interpreted Bytecode
Total 436 436
Workers 4 4
Duration 2.94min 2.69min

Memory

GC rows aggregate the main thread plus all worker thread-local GCs. Benchmark runner performs explicit between-file collections, so collection and collected-object counts can be much higher than the test runner.

Metric Interpreted Bytecode
GC Live 6.28 MiB 6.28 MiB
GC Peak Live 99.36 MiB 71.83 MiB
GC Allocated During Run 15.88 GiB 9.74 GiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 3,614 3,518
GC Collected Objects 247,202,760 214,869,840
Heap Start Allocated 3.36 MiB 3.36 MiB
Heap End Allocated 3.36 MiB 3.36 MiB
Heap Delta Allocated 128 B 128 B

Measured on ubuntu-latest x64.

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results

436 benchmarks · PR vs same-runner main build

Interpreted: 🟢 55 improved · 🔴 16 regressed · 365 unchanged · avg +2.3%
Bytecode: 🟢 55 improved · 🔴 29 regressed · 352 unchanged · avg +2.9%

Typical per-run noise (median variance): interpreted ±3.6%, bytecode ±2.6%. Deltas within noise overlap and read as unchanged.

arraybuffer.js — Interp: 14 unch. · avg +1.6% · Bytecode: 🟢 1, 13 unch. · avg +0.1%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
create ArrayBuffer(0) 118,731 ops/sec [101,357..182,373] → 131,785 ops/sec [118,145..137,617] ~ overlap (+11.0%) 161,749 ops/sec [130,849..176,909] → 152,084 ops/sec [148,217..156,311] ~ overlap (-6.0%)
create ArrayBuffer(64) 115,232 ops/sec [114,427..171,056] → 116,130 ops/sec [115,110..119,842] ~ overlap (+0.8%) 154,131 ops/sec [146,023..159,634] → 148,280 ops/sec [145,623..164,945] ~ overlap (-3.8%)
create ArrayBuffer(1024) 105,366 ops/sec [104,588..122,652] → 106,479 ops/sec [102,895..126,647] ~ overlap (+1.1%) 133,157 ops/sec [130,723..137,090] → 145,273 ops/sec [127,518..196,829] ~ overlap (+9.1%)
create ArrayBuffer(8192) 67,509 ops/sec [64,396..71,529] → 70,085 ops/sec [65,796..73,911] ~ overlap (+3.8%) 78,114 ops/sec [74,127..95,760] → 75,256 ops/sec [74,847..75,731] ~ overlap (-3.7%)
slice full buffer (64 bytes) 99,574 ops/sec [98,302..114,416] → 100,985 ops/sec [99,158..114,221] ~ overlap (+1.4%) 118,471 ops/sec [117,539..118,882] → 116,188 ops/sec [115,285..117,875] ~ overlap (-1.9%)
slice half buffer (512 of 1024 bytes) 88,161 ops/sec [50,794..111,113] → 95,094 ops/sec [92,563..107,998] ~ overlap (+7.9%) 111,165 ops/sec [110,811..111,538] → 110,067 ops/sec [109,397..111,076] ~ overlap (-1.0%)
slice with negative indices 84,048 ops/sec [81,399..84,647] → 85,033 ops/sec [84,207..102,493] ~ overlap (+1.2%) 113,407 ops/sec [112,916..114,611] → 114,065 ops/sec [113,722..114,951] ~ overlap (+0.6%)
slice empty range 94,336 ops/sec [90,499..104,390] → 95,365 ops/sec [86,016..100,491] ~ overlap (+1.1%) 116,217 ops/sec [115,673..116,829] → 117,040 ops/sec [116,729..117,502] ~ overlap (+0.7%)
byteLength access 283,564 ops/sec [270,507..336,122] → 293,168 ops/sec [273,808..361,480] ~ overlap (+3.4%) 351,813 ops/sec [337,093..372,131] → 342,455 ops/sec [312,551..360,029] ~ overlap (-2.7%)
Symbol.toStringTag access 222,746 ops/sec [214,483..243,098] → 217,785 ops/sec [214,470..305,254] ~ overlap (-2.2%) 229,827 ops/sec [189,872..239,447] → 233,535 ops/sec [230,299..256,504] ~ overlap (+1.6%)
ArrayBuffer.isView 169,698 ops/sec [166,890..181,664] → 170,301 ops/sec [169,137..170,896] ~ overlap (+0.4%) 222,391 ops/sec [200,681..223,423] → 227,556 ops/sec [218,978..261,389] ~ overlap (+2.3%)
clone ArrayBuffer(64) 130,411 ops/sec [126,107..142,508] → 125,458 ops/sec [124,080..135,663] ~ overlap (-3.8%) 160,195 ops/sec [147,901..165,017] → 169,781 ops/sec [168,421..171,589] 🟢 +6.0%
clone ArrayBuffer(1024) 115,075 ops/sec [113,933..120,722] → 112,491 ops/sec [98,226..122,011] ~ overlap (-2.2%) 138,928 ops/sec [137,922..141,558] → 143,143 ops/sec [139,797..145,692] ~ overlap (+3.0%)
clone ArrayBuffer inside object 84,986 ops/sec [81,545..97,111] → 84,156 ops/sec [82,046..85,198] ~ overlap (-1.0%) 103,099 ops/sec [93,024..103,534] → 99,351 ops/sec [98,468..101,957] ~ overlap (-3.6%)
arrays.js — Interp: 🔴 2, 17 unch. · avg -1.1% · Bytecode: 🟢 1, 🔴 3, 15 unch. · avg -0.8%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
Array.from length 100 3,458 ops/sec [3,275..3,535] → 3,245 ops/sec [3,133..4,500] ~ overlap (-6.1%) 2,832 ops/sec [2,412..3,688] → 2,984 ops/sec [2,950..3,065] ~ overlap (+5.4%)
Array.from 10 elements 68,234 ops/sec [64,582..83,777] → 66,016 ops/sec [65,089..72,918] ~ overlap (-3.3%) 72,913 ops/sec [72,313..105,274] → 73,323 ops/sec [72,840..73,835] ~ overlap (+0.6%)
Array.of 10 elements 85,447 ops/sec [82,082..99,542] → 83,041 ops/sec [82,645..83,496] ~ overlap (-2.8%) 105,390 ops/sec [104,807..108,170] → 106,805 ops/sec [106,109..107,159] ~ overlap (+1.3%)
spread into new array 96,968 ops/sec [92,477..138,865] → 98,696 ops/sec [96,255..100,169] ~ overlap (+1.8%) 56,486 ops/sec [53,485..58,874] → 54,806 ops/sec [53,742..70,077] ~ overlap (-3.0%)
map over 50 elements 5,252 ops/sec [5,017..5,641] → 5,184 ops/sec [5,044..5,262] ~ overlap (-1.3%) 5,687 ops/sec [5,364..6,216] → 5,921 ops/sec [5,769..6,495] ~ overlap (+4.1%)
filter over 50 elements 4,792 ops/sec [2,596..4,913] → 4,819 ops/sec [4,668..4,877] ~ overlap (+0.6%) 5,748 ops/sec [5,449..6,847] → 5,405 ops/sec [5,255..5,847] ~ overlap (-6.0%)
reduce sum 50 elements 5,304 ops/sec [5,242..6,772] → 5,733 ops/sec [5,257..5,981] ~ overlap (+8.1%) 5,668 ops/sec [5,457..6,329] → 5,516 ops/sec [5,497..5,576] ~ overlap (-2.7%)
forEach over 50 elements 3,652 ops/sec [3,616..3,674] → 3,650 ops/sec [3,564..3,763] ~ overlap (-0.1%) 6,068 ops/sec [6,044..6,082] → 6,016 ops/sec [5,762..6,039] 🔴 -0.8%
find in 50 elements 6,995 ops/sec [6,810..7,382] → 7,255 ops/sec [7,027..8,004] ~ overlap (+3.7%) 7,470 ops/sec [7,309..7,808] → 7,524 ops/sec [7,480..7,536] ~ overlap (+0.7%)
sort 20 elements 4,424 ops/sec [4,308..5,095] → 4,470 ops/sec [4,357..4,614] ~ overlap (+1.0%) 4,895 ops/sec [4,847..4,913] → 4,863 ops/sec [4,801..4,899] ~ overlap (-0.7%)
flat nested array 41,290 ops/sec [39,257..46,389] → 41,034 ops/sec [40,949..41,171] ~ overlap (-0.6%) 48,253 ops/sec [43,000..48,703] → 45,318 ops/sec [43,638..52,654] ~ overlap (-6.1%)
flatMap 21,688 ops/sec [21,627..21,948] → 21,628 ops/sec [21,327..21,845] ~ overlap (-0.3%) 22,961 ops/sec [22,327..27,744] → 22,844 ops/sec [22,527..23,435] ~ overlap (-0.5%)
map inside map (5x5) 5,641 ops/sec [5,584..5,968] → 5,697 ops/sec [5,628..6,674] ~ overlap (+1.0%) 6,184 ops/sec [5,818..6,807] → 6,242 ops/sec [6,089..7,724] ~ overlap (+0.9%)
filter inside map (5x10) 3,975 ops/sec [3,738..4,365] → 3,859 ops/sec [3,766..3,911] ~ overlap (-2.9%) 4,297 ops/sec [4,021..4,594] → 4,160 ops/sec [3,970..4,539] ~ overlap (-3.2%)
reduce inside map (5x10) 4,814 ops/sec [4,635..4,819] → 4,376 ops/sec [4,306..4,529] 🔴 -9.1% 4,441 ops/sec [4,399..4,457] → 4,571 ops/sec [4,459..6,121] 🟢 +2.9%
forEach inside forEach (5x10) 3,179 ops/sec [3,107..3,428] → 3,069 ops/sec [3,048..3,129] ~ overlap (-3.5%) 4,711 ops/sec [4,700..4,893] → 4,889 ops/sec [4,839..5,341] ~ overlap (+3.8%)
find inside some (10x10) 3,219 ops/sec [3,184..3,298] → 3,162 ops/sec [3,131..3,174] 🔴 -1.7% 3,381 ops/sec [3,324..3,548] → 3,354 ops/sec [3,261..3,410] ~ overlap (-0.8%)
map+filter chain nested (5x20) 1,184 ops/sec [1,158..1,194] → 1,176 ops/sec [1,077..1,231] ~ overlap (-0.7%) 1,369 ops/sec [1,368..1,372] → 1,252 ops/sec [1,242..1,305] 🔴 -8.6%
reduce flatten (10x5) 10,548 ops/sec [10,005..10,640] → 9,964 ops/sec [9,742..10,151] ~ overlap (-5.5%) 4,180 ops/sec [4,175..4,197] → 4,057 ops/sec [4,025..4,110] 🔴 -2.9%
async-await.js — Interp: 🟢 1, 5 unch. · avg -1.9% · Bytecode: 🔴 1, 5 unch. · avg +0.1%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
single await 26,702 ops/sec [22,860..27,380] → 22,929 ops/sec [22,605..23,142] ~ overlap (-14.1%) 23,499 ops/sec [18,828..33,003] → 23,252 ops/sec [21,681..25,461] ~ overlap (-1.1%)
multiple awaits 9,639 ops/sec [9,201..10,359] → 9,330 ops/sec [9,229..10,805] ~ overlap (-3.2%) 8,396 ops/sec [8,327..8,439] → 8,308 ops/sec [8,225..9,372] ~ overlap (-1.0%)
await non-Promise value 30,777 ops/sec [30,234..31,493] → 31,691 ops/sec [29,988..47,138] ~ overlap (+3.0%) 31,764 ops/sec [31,619..32,510] → 30,198 ops/sec [30,133..30,224] 🔴 -4.9%
await with try/catch 20,360 ops/sec [17,768..20,602] → 19,569 ops/sec [19,191..19,982] ~ overlap (-3.9%) 22,711 ops/sec [21,624..27,162] → 22,542 ops/sec [21,933..23,620] ~ overlap (-0.7%)
await Promise.all 5,154 ops/sec [5,050..5,291] → 5,453 ops/sec [5,315..5,580] 🟢 +5.8% 5,483 ops/sec [5,313..5,652] → 5,842 ops/sec [5,507..5,867] ~ overlap (+6.6%)
nested async function call 13,237 ops/sec [12,729..13,613] → 13,384 ops/sec [12,166..14,433] ~ overlap (+1.1%) 13,301 ops/sec [12,286..13,326] → 13,508 ops/sec [12,275..14,889] ~ overlap (+1.6%)
async-generators.js — Interp: 2 unch. · avg -0.7% · Bytecode: 2 unch. · avg -2.1%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
for-await-of over async generator 1,559 ops/sec [1,548..1,603] → 1,528 ops/sec [1,495..1,552] ~ overlap (-2.0%) 622 ops/sec [542..695] → 589 ops/sec [571..746] ~ overlap (-5.4%)
async generator with await in body 10,295 ops/sec [10,068..15,546] → 10,355 ops/sec [10,121..13,323] ~ overlap (+0.6%) 4,319 ops/sec [4,256..4,858] → 4,371 ops/sec [4,356..4,568] ~ overlap (+1.2%)
atomics.js — Interp: 6 unch. · avg -0.2% · Bytecode: 🟢 3, 3 unch. · avg +1.3%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
load and store Int32Array 81,841 ops/sec [78,280..82,421] → 79,517 ops/sec [78,907..83,444] ~ overlap (-2.8%) 120,331 ops/sec [116,939..121,191] → 122,530 ops/sec [122,241..122,969] 🟢 +1.8%
read-modify-write Int32Array 55,483 ops/sec [53,587..63,674] → 57,122 ops/sec [53,423..63,571] ~ overlap (+3.0%) 82,726 ops/sec [77,314..90,712] → 82,393 ops/sec [81,102..90,363] ~ overlap (-0.4%)
compareExchange hit and miss 50,961 ops/sec [49,325..51,338] → 49,990 ops/sec [48,999..53,318] ~ overlap (-1.9%) 72,480 ops/sec [70,058..72,832] → 74,154 ops/sec [72,934..82,939] 🟢 +2.3%
wait with zero timeout 132,175 ops/sec [130,558..133,345] → 132,279 ops/sec [131,317..133,665] ~ overlap (+0.1%) 181,507 ops/sec [174,687..185,344] → 188,960 ops/sec [187,485..189,412] 🟢 +4.1%
waitAsync synchronous not-equal 97,866 ops/sec [93,535..109,478] → 97,049 ops/sec [94,486..98,362] ~ overlap (-0.8%) 119,861 ops/sec [119,089..120,340] → 119,565 ops/sec [118,809..120,280] ~ overlap (-0.2%)
notify with no waiters 136,331 ops/sec [134,517..140,147] → 138,080 ops/sec [136,784..139,921] ~ overlap (+1.3%) 187,333 ops/sec [186,058..189,423] → 188,177 ops/sec [185,826..192,800] ~ overlap (+0.5%)
base64.js — Interp: 🟢 1, 9 unch. · avg +1.0% · Bytecode: 🟢 1, 9 unch. · avg +1.4%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
short ASCII (13 chars) 2,371 ops/sec [2,300..2,910] → 2,517 ops/sec [2,383..2,526] ~ overlap (+6.1%) 2,379 ops/sec [2,375..2,383] → 2,380 ops/sec [2,351..2,386] ~ overlap (+0.0%)
medium ASCII (450 chars) 87 ops/sec [84..89] → 87 ops/sec [84..88] ~ overlap (+0.5%) 86 ops/sec [86..86] → 86 ops/sec [86..86] ~ overlap (-0.1%)
Latin-1 characters 3,581 ops/sec [3,422..3,826] → 3,466 ops/sec [3,417..3,506] ~ overlap (-3.2%) 3,426 ops/sec [3,414..3,431] → 3,420 ops/sec [3,335..3,487] ~ overlap (-0.2%)
short base64 (20 chars) 1,731 ops/sec [1,716..2,080] → 1,762 ops/sec [1,723..2,062] ~ overlap (+1.8%) 1,684 ops/sec [1,641..2,065] → 1,714 ops/sec [1,654..1,748] ~ overlap (+1.8%)
medium base64 (600 chars) 69 ops/sec [67..71] → 69 ops/sec [68..76] ~ overlap (+0.1%) 66 ops/sec [62..68] → 67 ops/sec [67..67] ~ overlap (+1.0%)
Latin-1 output 2,484 ops/sec [2,405..2,531] → 2,469 ops/sec [2,366..2,541] ~ overlap (-0.6%) 2,404 ops/sec [2,316..2,428] → 2,449 ops/sec [2,390..2,454] ~ overlap (+1.8%)
forgiving (no padding) 3,651 ops/sec [3,639..3,665] → 3,698 ops/sec [3,639..4,649] ~ overlap (+1.3%) 3,502 ops/sec [3,422..3,581] → 3,623 ops/sec [3,521..3,980] ~ overlap (+3.5%)
with whitespace 1,587 ops/sec [1,573..1,593] → 1,611 ops/sec [1,590..1,829] ~ overlap (+1.5%) 1,571 ops/sec [1,546..1,577] → 1,609 ops/sec [1,585..1,801] 🟢 +2.4%
atob(btoa(short)) 995 ops/sec [986..999] → 1,014 ops/sec [1,006..1,020] 🟢 +1.9% 989 ops/sec [985..1,069] → 1,000 ops/sec [951..1,019] ~ overlap (+1.1%)
atob(btoa(medium)) 39 ops/sec [36..41] → 39 ops/sec [38..41] ~ overlap (+0.7%) 39 ops/sec [35..41] → 40 ops/sec [38..43] ~ overlap (+2.4%)
classes.js — Interp: 🟢 4, 🔴 2, 25 unch. · avg +0.9% · Bytecode: 🟢 2, 🔴 1, 28 unch. · avg +1.4%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
simple class new 40,684 ops/sec [39,735..49,078] → 42,924 ops/sec [40,318..48,797] ~ overlap (+5.5%) 51,768 ops/sec [51,465..52,812] → 51,875 ops/sec [50,288..53,215] ~ overlap (+0.2%)
class with defaults 31,509 ops/sec [31,221..31,805] → 32,064 ops/sec [31,742..37,002] ~ overlap (+1.8%) 35,969 ops/sec [35,203..36,240] → 36,359 ops/sec [36,201..36,464] ~ overlap (+1.1%)
50 instances via Array.from 1,445 ops/sec [1,417..1,459] → 1,465 ops/sec [1,432..1,470] ~ overlap (+1.4%) 1,693 ops/sec [1,686..1,695] → 1,685 ops/sec [1,675..1,689] ~ overlap (-0.5%)
instance method call 18,377 ops/sec [18,209..22,428] → 19,030 ops/sec [18,292..20,464] ~ overlap (+3.6%) 28,118 ops/sec [27,235..31,530] → 28,409 ops/sec [28,089..28,470] ~ overlap (+1.0%)
static method call 31,036 ops/sec [30,692..45,166] → 31,691 ops/sec [29,270..36,311] ~ overlap (+2.1%) 56,244 ops/sec [54,584..69,867] → 57,803 ops/sec [56,403..67,797] ~ overlap (+2.8%)
single-level inheritance 16,412 ops/sec [16,164..20,782] → 16,645 ops/sec [16,352..17,223] ~ overlap (+1.4%) 18,404 ops/sec [17,962..21,387] → 19,440 ops/sec [19,280..21,138] ~ overlap (+5.6%)
two-level inheritance 14,208 ops/sec [14,118..15,572] → 14,602 ops/sec [14,571..14,664] ~ overlap (+2.8%) 15,179 ops/sec [14,797..15,522] → 14,933 ops/sec [14,658..14,999] ~ overlap (-1.6%)
private field access 19,747 ops/sec [19,724..19,776] → 20,294 ops/sec [20,130..20,499] 🟢 +2.8% 14,690 ops/sec [14,029..15,507] → 14,573 ops/sec [14,477..18,697] ~ overlap (-0.8%)
private methods 22,220 ops/sec [22,056..27,079] → 23,043 ops/sec [22,635..24,508] ~ overlap (+3.7%) 17,940 ops/sec [17,227..19,147] → 16,430 ops/sec [16,266..16,531] 🔴 -8.4%
getter/setter access 22,046 ops/sec [16,717..26,751] → 22,014 ops/sec [21,838..26,620] ~ overlap (-0.1%) 29,706 ops/sec [29,332..33,842] → 30,003 ops/sec [29,606..31,403] ~ overlap (+1.0%)
class decorator (identity) 36,662 ops/sec [33,648..38,909] → 34,039 ops/sec [33,368..34,535] ~ overlap (-7.2%) 32,767 ops/sec [31,263..34,707] → 33,680 ops/sec [33,161..50,008] ~ overlap (+2.8%)
class decorator (wrapping) 17,772 ops/sec [17,588..19,211] → 19,635 ops/sec [17,949..21,919] ~ overlap (+10.5%) 17,500 ops/sec [11,858..26,453] → 16,992 ops/sec [16,851..17,055] ~ overlap (-2.9%)
identity method decorator 23,174 ops/sec [22,629..23,278] → 23,241 ops/sec [22,990..23,421] ~ overlap (+0.3%) 27,429 ops/sec [27,249..34,138] → 27,734 ops/sec [27,115..32,571] ~ overlap (+1.1%)
wrapping method decorator 18,284 ops/sec [18,187..18,351] → 18,634 ops/sec [18,444..19,209] 🟢 +1.9% 20,364 ops/sec [19,923..20,995] → 20,935 ops/sec [20,741..21,438] ~ overlap (+2.8%)
stacked method decorators (x3) 13,237 ops/sec [13,227..13,333] → 13,642 ops/sec [13,128..14,620] ~ overlap (+3.1%) 15,299 ops/sec [15,068..15,979] → 15,617 ops/sec [14,829..17,770] ~ overlap (+2.1%)
identity field decorator 27,458 ops/sec [26,174..28,171] → 26,770 ops/sec [26,085..27,361] ~ overlap (-2.5%) 28,339 ops/sec [26,850..35,004] → 29,449 ops/sec [26,574..31,216] ~ overlap (+3.9%)
field initializer decorator 21,661 ops/sec [21,489..24,048] → 23,457 ops/sec [21,630..25,036] ~ overlap (+8.3%) 23,740 ops/sec [23,191..27,583] → 27,582 ops/sec [24,080..28,384] ~ overlap (+16.2%)
getter decorator (identity) 20,899 ops/sec [20,317..23,187] → 21,028 ops/sec [20,963..21,052] ~ overlap (+0.6%) 18,378 ops/sec [18,243..19,094] → 18,667 ops/sec [18,612..20,005] ~ overlap (+1.6%)
setter decorator (identity) 17,548 ops/sec [17,237..18,129] → 16,829 ops/sec [16,426..17,010] 🔴 -4.1% 14,764 ops/sec [14,682..14,785] → 15,063 ops/sec [14,962..15,104] 🟢 +2.0%
static method decorator 28,275 ops/sec [24,448..30,966] → 27,112 ops/sec [27,039..27,404] ~ overlap (-4.1%) 31,145 ops/sec [30,943..31,171] → 31,491 ops/sec [31,289..31,724] 🟢 +1.1%
static field decorator 28,898 ops/sec [28,595..32,989] → 32,218 ops/sec [29,919..33,636] ~ overlap (+11.5%) 34,070 ops/sec [33,796..34,210] → 33,459 ops/sec [33,207..41,660] ~ overlap (-1.8%)
private method decorator 19,794 ops/sec [17,629..22,172] → 17,506 ops/sec [17,155..17,616] 🔴 -11.6% 20,232 ops/sec [20,024..20,428] → 20,373 ops/sec [19,898..24,655] ~ overlap (+0.7%)
private field decorator 20,594 ops/sec [20,046..22,545] → 20,030 ops/sec [19,186..20,637] ~ overlap (-2.7%) 17,970 ops/sec [17,773..19,242] → 19,016 ops/sec [18,261..25,972] ~ overlap (+5.8%)
plain auto-accessor (no decorator) 35,105 ops/sec [34,836..35,209] → 36,100 ops/sec [35,130..36,203] ~ overlap (+2.8%) 33,319 ops/sec [32,768..46,378] → 34,262 ops/sec [33,886..37,800] ~ overlap (+2.8%)
auto-accessor with decorator 20,626 ops/sec [19,660..23,157] → 21,449 ops/sec [19,842..23,324] ~ overlap (+4.0%) 21,542 ops/sec [20,008..25,146] → 20,160 ops/sec [19,460..20,465] ~ overlap (-6.4%)
decorator writing metadata 16,053 ops/sec [15,832..16,142] → 16,887 ops/sec [16,334..23,639] 🟢 +5.2% 20,749 ops/sec [18,857..25,184] → 19,138 ops/sec [18,932..19,172] ~ overlap (-7.8%)
static getter read 38,255 ops/sec [37,884..39,129] → 39,255 ops/sec [39,175..39,284] 🟢 +2.6% 53,845 ops/sec [53,396..57,884] → 53,645 ops/sec [53,605..53,677] ~ overlap (-0.4%)
static getter/setter pair 29,770 ops/sec [27,125..32,197] → 27,692 ops/sec [27,182..31,466] ~ overlap (-7.0%) 39,926 ops/sec [38,599..46,223] → 39,984 ops/sec [37,934..40,222] ~ overlap (+0.1%)
inherited static getter 27,721 ops/sec [27,048..28,388] → 26,353 ops/sec [24,906..33,182] ~ overlap (-4.9%) 35,291 ops/sec [34,392..37,446] → 37,076 ops/sec [33,765..39,721] ~ overlap (+5.1%)
inherited static setter 27,652 ops/sec [25,487..33,179] → 28,608 ops/sec [27,246..29,881] ~ overlap (+3.5%) 33,615 ops/sec [33,481..33,686] → 34,844 ops/sec [33,604..49,558] ~ overlap (+3.7%)
inherited static getter with this binding 21,531 ops/sec [19,826..23,417] → 19,952 ops/sec [19,835..19,995] ~ overlap (-7.3%) 26,587 ops/sec [26,348..27,535] → 29,188 ops/sec [26,530..31,916] ~ overlap (+9.8%)
closures.js — Interp: 🟢 1, 10 unch. · avg +4.5% · Bytecode: 🟢 1, 10 unch. · avg +1.0%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
closure over single variable 27,981 ops/sec [27,732..28,234] → 27,950 ops/sec [27,460..30,583] ~ overlap (-0.1%) 91,167 ops/sec [89,644..92,600] → 87,937 ops/sec [86,673..93,345] ~ overlap (-3.5%)
closure over multiple variables 35,259 ops/sec [34,840..36,888] → 35,149 ops/sec [30,770..37,965] ~ overlap (-0.3%) 85,625 ops/sec [81,727..90,101] → 84,237 ops/sec [83,788..87,041] ~ overlap (-1.6%)
nested closures 37,934 ops/sec [37,407..38,250] → 38,180 ops/sec [37,218..41,134] ~ overlap (+0.6%) 81,284 ops/sec [80,153..84,261] → 87,366 ops/sec [87,025..88,836] 🟢 +7.5%
function as argument 27,702 ops/sec [27,339..28,295] → 28,318 ops/sec [28,061..32,312] ~ overlap (+2.2%) 82,008 ops/sec [78,011..82,722] → 83,112 ops/sec [81,856..85,081] ~ overlap (+1.3%)
function returning function 35,595 ops/sec [34,978..35,833] → 40,879 ops/sec [40,588..41,307] 🟢 +14.8% 94,396 ops/sec [94,067..94,785] → 96,909 ops/sec [90,675..107,317] ~ overlap (+2.7%)
compose two functions 22,856 ops/sec [22,043..23,512] → 24,681 ops/sec [22,442..30,613] ~ overlap (+8.0%) 57,208 ops/sec [53,227..60,633] → 61,166 ops/sec [56,679..65,104] ~ overlap (+6.9%)
fn.call 51,000 ops/sec [48,302..55,746] → 50,286 ops/sec [50,219..50,778] ~ overlap (-1.4%) 71,722 ops/sec [70,121..75,170] → 72,314 ops/sec [71,297..85,971] ~ overlap (+0.8%)
fn.apply 38,748 ops/sec [38,405..38,933] → 39,471 ops/sec [38,370..41,442] ~ overlap (+1.9%) 69,393 ops/sec [67,968..76,508] → 69,660 ops/sec [68,332..81,147] ~ overlap (+0.4%)
fn.bind 41,080 ops/sec [40,569..49,860] → 46,158 ops/sec [39,979..53,724] ~ overlap (+12.4%) 92,669 ops/sec [85,876..105,785] → 97,544 ops/sec [91,597..108,190] ~ overlap (+5.3%)
recursive sum to 50 3,255 ops/sec [3,197..3,325] → 3,615 ops/sec [3,124..3,648] ~ overlap (+11.1%) 13,528 ops/sec [12,662..14,601] → 12,076 ops/sec [11,630..14,058] ~ overlap (-10.7%)
recursive tree traversal 4,920 ops/sec [4,872..4,989] → 4,926 ops/sec [4,834..5,194] ~ overlap (+0.1%) 11,227 ops/sec [10,883..11,455] → 11,480 ops/sec [11,067..11,620] ~ overlap (+2.2%)
collections.js — Interp: 🟢 8, 4 unch. · avg +36.9% · Bytecode: 🟢 8, 4 unch. · avg +50.8%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
add 50 elements 2,232 ops/sec [2,209..2,448] → 2,951 ops/sec [2,884..2,957] 🟢 +32.2% 2,506 ops/sec [2,502..2,524] → 3,676 ops/sec [3,523..3,974] 🟢 +46.7%
has lookup (50 elements) 35,949 ops/sec [35,698..36,727] → 62,730 ops/sec [61,290..63,073] 🟢 +74.5% 46,655 ops/sec [42,374..56,247] → 105,546 ops/sec [93,875..132,563] 🟢 +126.2%
delete elements 20,284 ops/sec [19,978..23,831] → 34,259 ops/sec [33,721..34,395] 🟢 +68.9% 24,030 ops/sec [22,853..25,894] → 46,403 ops/sec [45,963..46,673] 🟢 +93.1%
forEach iteration 3,672 ops/sec [3,661..3,689] → 3,814 ops/sec [3,704..4,004] 🟢 +3.9% 6,373 ops/sec [5,098..7,178] → 6,228 ops/sec [6,117..6,303] ~ overlap (-2.3%)
spread to array 12,257 ops/sec [11,880..17,037] → 13,413 ops/sec [13,295..13,807] ~ overlap (+9.4%) 71,226 ops/sec [69,482..84,953] → 71,148 ops/sec [70,275..79,977] ~ overlap (-0.1%)
deduplicate array 17,982 ops/sec [16,994..21,746] → 23,006 ops/sec [19,543..26,296] ~ overlap (+27.9%) 31,441 ops/sec [30,770..34,469] → 40,465 ops/sec [40,186..51,296] 🟢 +28.7%
set 50 entries 1,693 ops/sec [1,683..1,694] → 2,166 ops/sec [2,146..2,308] 🟢 +28.0% 2,215 ops/sec [2,034..2,389] → 2,824 ops/sec [2,803..2,866] 🟢 +27.5%
get lookup (50 entries) 35,903 ops/sec [35,304..36,707] → 63,843 ops/sec [63,609..64,066] 🟢 +77.8% 43,534 ops/sec [41,436..45,328] → 84,780 ops/sec [82,793..86,013] 🟢 +94.7%
has check 51,736 ops/sec [51,196..51,829] → 81,592 ops/sec [78,568..94,538] 🟢 +57.7% 61,397 ops/sec [58,854..65,989] → 112,998 ops/sec [109,148..119,608] 🟢 +84.0%
delete entries 20,284 ops/sec [20,022..20,407] → 34,267 ops/sec [29,956..43,135] 🟢 +68.9% 23,052 ops/sec [22,543..24,435] → 43,920 ops/sec [42,310..53,200] 🟢 +90.5%
forEach iteration 4,056 ops/sec [3,619..4,452] → 3,810 ops/sec [3,682..4,129] ~ overlap (-6.1%) 6,241 ops/sec [6,045..6,466] → 6,176 ops/sec [6,079..6,444] ~ overlap (-1.0%)
keys/values/entries 3,386 ops/sec [3,367..4,590] → 3,369 ops/sec [3,103..3,701] ~ overlap (-0.5%) 11,521 ops/sec [11,417..14,662] → 13,986 ops/sec [11,601..17,969] ~ overlap (+21.4%)
csv.js — Interp: 🟢 3, 🔴 1, 9 unch. · avg -0.5% · Bytecode: 🔴 1, 12 unch. · avg -3.0%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
parse simple 3-column CSV 44,255 ops/sec [39,962..46,952] → 39,913 ops/sec [38,500..47,099] ~ overlap (-9.8%) 43,714 ops/sec [42,789..49,892] → 42,738 ops/sec [42,588..42,810] ~ overlap (-2.2%)
parse 10-row CSV 11,380 ops/sec [11,319..11,623] → 11,695 ops/sec [11,395..11,716] ~ overlap (+2.8%) 12,698 ops/sec [11,754..14,069] → 11,924 ops/sec [11,834..12,366] ~ overlap (-6.1%)
parse 100-row CSV 1,791 ops/sec [1,781..1,798] → 1,876 ops/sec [1,860..1,892] 🟢 +4.8% 2,019 ops/sec [1,940..2,071] → 1,795 ops/sec [1,787..2,081] ~ overlap (-11.1%)
parse CSV with quoted fields 56,756 ops/sec [56,533..57,674] → 58,353 ops/sec [56,380..58,633] ~ overlap (+2.8%) 70,387 ops/sec [63,560..75,980] → 64,888 ops/sec [63,310..92,218] ~ overlap (-7.8%)
parse without headers (array of arrays) 6,566 ops/sec [6,411..6,919] → 5,607 ops/sec [5,548..5,642] 🔴 -14.6% 5,547 ops/sec [5,526..5,955] → 5,628 ops/sec [5,487..7,179] ~ overlap (+1.5%)
parse with semicolon delimiter 8,362 ops/sec [8,313..10,465] → 8,389 ops/sec [8,378..8,427] ~ overlap (+0.3%) 10,886 ops/sec [8,377..14,213] → 8,629 ops/sec [8,446..11,783] ~ overlap (-20.7%)
stringify array of objects 54,184 ops/sec [53,207..55,273] → 55,265 ops/sec [54,381..74,867] ~ overlap (+2.0%) 65,147 ops/sec [63,878..68,734] → 63,650 ops/sec [63,027..63,868] 🔴 -2.3%
stringify array of arrays 22,025 ops/sec [21,665..25,030] → 21,717 ops/sec [20,540..22,933] ~ overlap (-1.4%) 23,054 ops/sec [22,880..24,899] → 23,815 ops/sec [23,529..24,352] ~ overlap (+3.3%)
stringify with values needing escaping 50,390 ops/sec [43,011..56,899] → 42,871 ops/sec [41,378..47,791] ~ overlap (-14.9%) 49,128 ops/sec [49,053..51,129] → 49,445 ops/sec [48,082..49,781] ~ overlap (+0.6%)
reviver converts numbers 878 ops/sec [853..923] → 928 ops/sec [865..944] ~ overlap (+5.7%) 1,095 ops/sec [1,087..1,098] → 1,100 ops/sec [1,040..1,243] ~ overlap (+0.4%)
reviver filters empty to null 7,416 ops/sec [6,939..8,301] → 8,790 ops/sec [8,415..9,394] 🟢 +18.5% 10,253 ops/sec [10,223..10,271] → 10,394 ops/sec [9,783..11,261] ~ overlap (+1.4%)
parse then stringify 8,198 ops/sec [7,409..8,854] → 7,589 ops/sec [7,548..7,625] ~ overlap (-7.4%) 7,702 ops/sec [7,656..7,771] → 7,909 ops/sec [7,407..9,005] ~ overlap (+2.7%)
stringify then parse 7,112 ops/sec [7,089..7,450] → 7,474 ops/sec [7,450..7,502] 🟢 +5.1% 7,444 ops/sec [7,387..12,276] → 7,594 ops/sec [7,489..7,636] ~ overlap (+2.0%)
destructuring.js — Interp: 🟢 2, 🔴 1, 19 unch. · avg -0.4% · Bytecode: 🟢 3, 🔴 2, 17 unch. · avg +1.8%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
simple array destructuring 65,722 ops/sec [65,165..66,863] → 63,642 ops/sec [61,555..64,957] 🔴 -3.2% 83,068 ops/sec [81,625..106,089] → 82,100 ops/sec [77,879..82,337] ~ overlap (-1.2%)
with rest element 58,554 ops/sec [53,769..70,017] → 52,894 ops/sec [50,724..53,867] ~ overlap (-9.7%) 65,266 ops/sec [58,566..77,248] → 65,054 ops/sec [64,587..77,878] ~ overlap (-0.3%)
with defaults 67,954 ops/sec [66,044..74,294] → 65,026 ops/sec [63,213..66,908] ~ overlap (-4.3%) 87,062 ops/sec [86,259..88,103] → 88,547 ops/sec [88,489..88,742] 🟢 +1.7%
skip elements 73,520 ops/sec [72,688..75,333] → 74,485 ops/sec [73,294..75,652] ~ overlap (+1.3%) 89,666 ops/sec [87,932..90,158] → 90,886 ops/sec [89,128..105,471] ~ overlap (+1.4%)
nested array destructuring 31,412 ops/sec [31,009..32,311] → 29,630 ops/sec [27,995..33,222] ~ overlap (-5.7%) 31,253 ops/sec [31,106..31,453] → 32,265 ops/sec [31,715..39,854] 🟢 +3.2%
swap variables 91,118 ops/sec [79,623..101,585] → 90,657 ops/sec [84,756..95,214] ~ overlap (-0.5%) 118,121 ops/sec [113,836..118,577] → 129,668 ops/sec [117,910..187,755] ~ overlap (+9.8%)
simple object destructuring 79,871 ops/sec [78,170..84,967] → 79,489 ops/sec [77,447..82,595] ~ overlap (-0.5%) 93,585 ops/sec [91,819..95,206] → 93,323 ops/sec [90,261..98,814] ~ overlap (-0.3%)
with defaults 86,701 ops/sec [86,033..89,635] → 87,079 ops/sec [84,286..88,221] ~ overlap (+0.4%) 134,064 ops/sec [130,832..135,761] → 143,875 ops/sec [136,680..150,304] 🟢 +7.3%
with renaming 86,718 ops/sec [85,207..89,896] → 87,418 ops/sec [85,308..89,788] ~ overlap (+0.8%) 97,568 ops/sec [97,270..118,175] → 98,874 ops/sec [96,069..99,206] ~ overlap (+1.3%)
nested object destructuring 47,266 ops/sec [46,193..50,136] → 48,158 ops/sec [47,790..49,602] ~ overlap (+1.9%) 49,664 ops/sec [49,158..53,676] → 50,608 ops/sec [50,307..56,781] ~ overlap (+1.9%)
rest properties 37,684 ops/sec [36,372..42,350] → 37,063 ops/sec [36,813..37,145] ~ overlap (-1.6%) 44,947 ops/sec [42,549..46,669] → 47,437 ops/sec [43,942..50,079] ~ overlap (+5.5%)
object parameter 26,058 ops/sec [25,465..27,544] → 26,241 ops/sec [25,935..26,634] ~ overlap (+0.7%) 42,164 ops/sec [41,653..45,945] → 43,141 ops/sec [43,036..43,451] ~ overlap (+2.3%)
array parameter 30,780 ops/sec [30,750..30,872] → 31,502 ops/sec [30,400..33,385] ~ overlap (+2.3%) 43,008 ops/sec [41,896..48,957] → 45,981 ops/sec [41,938..52,287] ~ overlap (+6.9%)
mixed destructuring in map 8,322 ops/sec [8,313..8,380] → 8,425 ops/sec [8,128..8,456] ~ overlap (+1.2%) 11,358 ops/sec [10,915..12,131] → 11,812 ops/sec [11,050..12,848] ~ overlap (+4.0%)
forEach with array destructuring 13,263 ops/sec [13,136..13,430] → 13,752 ops/sec [13,547..13,795] 🟢 +3.7% 16,006 ops/sec [15,811..16,051] → 18,237 ops/sec [15,814..22,703] ~ overlap (+13.9%)
map with array destructuring 15,522 ops/sec [15,188..15,633] → 15,753 ops/sec [15,633..17,201] 🟢 +1.5% 15,016 ops/sec [14,902..20,560] → 15,487 ops/sec [15,038..16,185] ~ overlap (+3.1%)
filter with array destructuring 16,225 ops/sec [15,979..16,886] → 16,308 ops/sec [16,102..16,368] ~ overlap (+0.5%) 17,850 ops/sec [15,894..24,269] → 16,357 ops/sec [16,178..16,634] ~ overlap (-8.4%)
reduce with array destructuring 16,654 ops/sec [16,378..19,083] → 16,940 ops/sec [16,789..17,098] ~ overlap (+1.7%) 16,449 ops/sec [15,976..17,074] → 16,657 ops/sec [16,188..18,103] ~ overlap (+1.3%)
map with object destructuring 20,248 ops/sec [18,470..23,940] → 18,730 ops/sec [18,659..19,395] ~ overlap (-7.5%) 21,872 ops/sec [21,694..22,102] → 22,412 ops/sec [22,044..23,329] ~ overlap (+2.5%)
map with nested destructuring 15,097 ops/sec [14,885..15,317] → 15,533 ops/sec [15,301..17,174] ~ overlap (+2.9%) 22,964 ops/sec [22,688..23,443] → 22,281 ops/sec [22,140..22,390] 🔴 -3.0%
map with rest in destructuring 8,454 ops/sec [6,815..10,352] → 8,826 ops/sec [8,532..10,566] ~ overlap (+4.4%) 8,139 ops/sec [7,948..8,580] → 7,303 ops/sec [7,091..8,618] ~ overlap (-10.3%)
map with defaults in destructuring 10,395 ops/sec [10,197..11,551] → 10,456 ops/sec [10,279..11,083] ~ overlap (+0.6%) 17,306 ops/sec [16,996..18,025] → 16,662 ops/sec [16,178..16,953] 🔴 -3.7%
fibonacci.js — Interp: 8 unch. · avg +0.4% · Bytecode: 🟢 1, 7 unch. · avg +1.1%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
recursive fib(15) 91 ops/sec [89..92] → 89 ops/sec [88..89] ~ overlap (-2.6%) 315 ops/sec [313..315] → 328 ops/sec [318..350] 🟢 +4.1%
recursive fib(20) 9 ops/sec [8..9] → 9 ops/sec [8..10] ~ overlap (+4.1%) 33 ops/sec [32..34] → 32 ops/sec [30..33] ~ overlap (-3.3%)
recursive fib(15) typed 93 ops/sec [92..98] → 91 ops/sec [88..114] ~ overlap (-1.6%) 333 ops/sec [323..586] → 334 ops/sec [328..355] ~ overlap (+0.4%)
recursive fib(20) typed 9 ops/sec [8..9] → 9 ops/sec [9..9] ~ overlap (+3.2%) 31 ops/sec [30..35] → 30 ops/sec [30..30] ~ overlap (-2.8%)
iterative fib(20) via reduce 4,060 ops/sec [3,725..5,188] → 4,094 ops/sec [4,014..4,632] ~ overlap (+0.8%) 6,176 ops/sec [5,910..6,840] → 6,314 ops/sec [6,082..7,871] ~ overlap (+2.2%)
iterator fib(20) 2,253 ops/sec [2,170..2,271] → 2,299 ops/sec [2,204..2,403] ~ overlap (+2.0%) 4,821 ops/sec [4,657..4,885] → 5,303 ops/sec [4,764..5,736] ~ overlap (+10.0%)
iterator fib(20) via Iterator.from + take 2,162 ops/sec [2,151..2,173] → 2,134 ops/sec [2,118..2,198] ~ overlap (-1.3%) 2,726 ops/sec [2,660..2,838] → 2,682 ops/sec [2,600..2,992] ~ overlap (-1.6%)
iterator fib(20) last value via reduce 1,891 ops/sec [1,872..1,894] → 1,866 ops/sec [1,828..1,906] ~ overlap (-1.3%) 2,264 ops/sec [2,234..2,293] → 2,254 ops/sec [2,240..2,266] ~ overlap (-0.5%)
float16array.js — Interp: 🟢 5, 27 unch. · avg +1.2% · Bytecode: 🟢 5, 27 unch. · avg +2.0%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
new Float16Array(0) 94,109 ops/sec [91,319..126,546] → 94,067 ops/sec [92,608..94,751] ~ overlap (-0.0%) 115,099 ops/sec [111,008..125,421] → 112,655 ops/sec [109,935..113,893] ~ overlap (-2.1%)
new Float16Array(100) 90,837 ops/sec [90,398..94,436] → 89,774 ops/sec [84,535..90,549] ~ overlap (-1.2%) 118,309 ops/sec [104,273..128,439] → 108,752 ops/sec [108,337..108,791] ~ overlap (-8.1%)
new Float16Array(1000) 79,848 ops/sec [79,502..79,943] → 81,486 ops/sec [78,106..88,310] ~ overlap (+2.1%) 94,433 ops/sec [92,065..97,439] → 112,251 ops/sec [92,548..116,643] ~ overlap (+18.9%)
Float16Array.from([...100]) 1,568 ops/sec [1,495..1,571] → 1,535 ops/sec [1,468..1,833] ~ overlap (-2.1%) 1,688 ops/sec [1,590..1,827] → 1,627 ops/sec [1,621..1,638] ~ overlap (-3.6%)
Float16Array.of(1.5, 2.5, 3.5, 4.5, 5.5) 78,042 ops/sec [77,299..78,322] → 78,332 ops/sec [75,100..79,610] ~ overlap (+0.4%) 73,276 ops/sec [70,539..77,699] → 73,738 ops/sec [72,772..73,988] ~ overlap (+0.6%)
new Float16Array(float64Array) 26,742 ops/sec [25,572..27,573] → 26,586 ops/sec [25,738..26,606] ~ overlap (-0.6%) 27,526 ops/sec [27,440..27,637] → 27,769 ops/sec [27,173..30,686] ~ overlap (+0.9%)
sequential write 100 elements 982 ops/sec [951..1,069] → 997 ops/sec [991..999] ~ overlap (+1.5%) 3,172 ops/sec [3,135..3,370] → 3,325 ops/sec [3,100..3,582] ~ overlap (+4.8%)
sequential read 100 elements 1,105 ops/sec [1,088..1,110] → 1,131 ops/sec [1,123..1,166] 🟢 +2.4% 3,660 ops/sec [3,625..3,704] → 3,901 ops/sec [3,798..4,262] 🟢 +6.6%
write special values (NaN, Inf, -0) 42,465 ops/sec [41,827..42,837] → 42,640 ops/sec [42,163..42,921] ~ overlap (+0.4%) 81,732 ops/sec [78,827..85,453] → 80,445 ops/sec [77,805..85,197] ~ overlap (-1.6%)
Float16Array write 987 ops/sec [983..989] → 999 ops/sec [992..1,003] 🟢 +1.3% 3,265 ops/sec [3,170..3,375] → 3,504 ops/sec [3,468..3,538] 🟢 +7.3%
Float32Array write 991 ops/sec [970..997] → 999 ops/sec [997..1,003] ~ overlap (+0.8%) 3,271 ops/sec [3,194..3,347] → 3,462 ops/sec [3,426..3,468] 🟢 +5.8%
Float64Array write 980 ops/sec [975..1,024] → 1,007 ops/sec [993..1,009] ~ overlap (+2.7%) 3,320 ops/sec [3,195..3,839] → 3,308 ops/sec [3,292..4,209] ~ overlap (-0.4%)
Float16Array read 1,100 ops/sec [1,090..1,112] → 1,085 ops/sec [1,084..1,091] ~ overlap (-1.4%) 3,875 ops/sec [3,317..4,480] → 3,585 ops/sec [3,573..3,604] ~ overlap (-7.5%)
Float32Array read 1,115 ops/sec [1,107..1,119] → 1,293 ops/sec [1,108..1,374] ~ overlap (+16.0%) 3,687 ops/sec [3,433..3,941] → 3,772 ops/sec [3,385..4,390] ~ overlap (+2.3%)
Float64Array read 1,129 ops/sec [1,111..1,153] → 1,141 ops/sec [1,135..1,264] ~ overlap (+1.0%) 3,462 ops/sec [3,426..3,839] → 3,755 ops/sec [3,389..4,095] ~ overlap (+8.5%)
fill(1.5) 7,054 ops/sec [6,549..9,077] → 6,940 ops/sec [6,720..7,470] ~ overlap (-1.6%) 6,948 ops/sec [6,896..6,963] → 6,985 ops/sec [6,744..7,080] ~ overlap (+0.5%)
slice() 12,291 ops/sec [11,742..14,631] → 12,379 ops/sec [12,206..12,740] ~ overlap (+0.7%) 12,704 ops/sec [12,589..13,233] → 12,700 ops/sec [12,531..12,914] ~ overlap (-0.0%)
map(x => x * 2) 2,070 ops/sec [2,056..2,152] → 2,084 ops/sec [2,051..2,116] ~ overlap (+0.7%) 2,126 ops/sec [2,105..2,208] → 2,212 ops/sec [2,191..2,238] ~ overlap (+4.1%)
filter(x => x > 25) 2,053 ops/sec [1,981..2,088] → 2,056 ops/sec [2,035..2,108] ~ overlap (+0.2%) 2,357 ops/sec [2,265..2,494] → 2,330 ops/sec [2,318..2,343] ~ overlap (-1.2%)
reduce (sum) 2,021 ops/sec [1,987..2,036] → 2,078 ops/sec [2,037..2,100] 🟢 +2.9% 1,979 ops/sec [1,968..2,033] → 2,067 ops/sec [2,031..2,075] ~ overlap (+4.5%)
sort() 18,300 ops/sec [18,230..18,339] → 19,006 ops/sec [18,358..19,320] 🟢 +3.9% 20,119 ops/sec [19,874..20,150] → 20,108 ops/sec [19,919..20,310] ~ overlap (-0.1%)
indexOf() 24,755 ops/sec [24,149..24,879] → 25,731 ops/sec [25,478..25,901] 🟢 +3.9% 27,355 ops/sec [26,443..28,253] → 27,229 ops/sec [26,709..32,056] ~ overlap (-0.5%)
reverse() 27,832 ops/sec [26,968..28,277] → 28,136 ops/sec [27,960..33,702] ~ overlap (+1.1%) 30,511 ops/sec [29,577..30,572] → 31,342 ops/sec [29,783..34,830] ~ overlap (+2.7%)
toReversed() 21,993 ops/sec [21,553..22,486] → 22,472 ops/sec [21,643..25,215] ~ overlap (+2.2%) 23,296 ops/sec [22,994..23,633] → 23,506 ops/sec [23,467..28,959] ~ overlap (+0.9%)
toSorted() 11,114 ops/sec [10,964..11,317] → 11,733 ops/sec [10,795..12,414] ~ overlap (+5.6%) 11,407 ops/sec [11,296..11,637] → 11,403 ops/sec [11,323..11,531] ~ overlap (-0.0%)
create view over existing buffer 104,833 ops/sec [100,477..114,402] → 106,084 ops/sec [104,769..111,232] ~ overlap (+1.2%) 125,274 ops/sec [120,899..126,247] → 128,374 ops/sec [123,579..142,191] ~ overlap (+2.5%)
subarray() 68,386 ops/sec [68,029..68,871] → 69,030 ops/sec [67,538..70,154] ~ overlap (+0.9%) 80,467 ops/sec [74,625..88,531] → 76,604 ops/sec [76,169..77,176] ~ overlap (-4.8%)
set() from array 98,638 ops/sec [93,266..106,979] → 101,522 ops/sec [97,537..102,993] ~ overlap (+2.9%) 128,466 ops/sec [124,014..132,820] → 131,027 ops/sec [126,198..160,123] ~ overlap (+2.0%)
for-of loop 1,505 ops/sec [1,487..1,906] → 1,641 ops/sec [1,502..1,762] ~ overlap (+9.0%) 5,756 ops/sec [5,653..5,886] → 6,630 ops/sec [6,382..6,749] 🟢 +15.2%
spread into array 7,541 ops/sec [6,364..8,540] → 6,434 ops/sec [6,103..7,193] ~ overlap (-14.7%) 23,204 ops/sec [22,493..24,323] → 23,698 ops/sec [23,564..23,960] ~ overlap (+2.1%)
f16round(1.337) 172,583 ops/sec [167,739..175,793] → 170,141 ops/sec [169,083..175,383] ~ overlap (-1.4%) 221,884 ops/sec [220,467..222,700] → 230,583 ops/sec [226,715..242,547] 🟢 +3.9%
f16round over 100 values 1,102 ops/sec [1,075..1,226] → 1,095 ops/sec [1,076..1,107] ~ overlap (-0.7%) 3,060 ops/sec [3,040..3,080] → 3,090 ops/sec [3,049..3,187] ~ overlap (+1.0%)
for-in/for-in.js — Interp: 🟢 2, 1 unch. · avg +4.7% · Bytecode: 3 unch. · avg -0.3%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
for...in over 50 own keys 2,851 ops/sec [2,809..2,869] → 2,877 ops/sec [2,861..2,927] ~ overlap (+0.9%) 3,823 ops/sec [3,785..3,870] → 3,776 ops/sec [3,675..3,845] ~ overlap (-1.2%)
for...in over an 8-level chain of 50 shared keys 2,083 ops/sec [2,039..2,154] → 2,187 ops/sec [2,156..2,352] 🟢 +5.0% 2,848 ops/sec [2,830..2,892] → 2,848 ops/sec [2,780..2,891] ~ overlap (-0.0%)
for...in over a 16-level chain of 100 shared keys 848 ops/sec [835..876] → 917 ops/sec [915..921] 🟢 +8.1% 1,084 ops/sec [1,072..1,092] → 1,086 ops/sec [1,070..1,100] ~ overlap (+0.2%)
for-of.js — Interp: 🟢 2, 5 unch. · avg +2.7% · Bytecode: 7 unch. · avg -0.3%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
for...of with 10-element array 12,780 ops/sec [12,684..14,782] → 13,868 ops/sec [13,486..13,903] ~ overlap (+8.5%) 77,673 ops/sec [73,456..81,581] → 78,943 ops/sec [71,609..80,529] ~ overlap (+1.6%)
for...of with 100-element array 1,513 ops/sec [1,506..1,516] → 1,602 ops/sec [1,578..1,948] 🟢 +5.9% 12,251 ops/sec [10,015..14,683] → 10,930 ops/sec [10,120..12,870] ~ overlap (-10.8%)
for...of with string (10 chars) 10,314 ops/sec [10,077..10,374] → 11,744 ops/sec [10,666..12,784] 🟢 +13.9% 30,760 ops/sec [27,408..35,649] → 27,479 ops/sec [27,187..32,123] ~ overlap (-10.7%)
for...of with Set (10 elements) 13,336 ops/sec [13,155..15,896] → 13,830 ops/sec [12,119..15,382] ~ overlap (+3.7%) 84,566 ops/sec [84,332..84,707] → 82,573 ops/sec [81,377..84,803] ~ overlap (-2.4%)
for...of with Map entries (10 entries) 6,997 ops/sec [6,928..8,275] → 6,771 ops/sec [6,481..7,807] ~ overlap (-3.2%) 12,766 ops/sec [12,097..16,477] → 13,611 ops/sec [12,102..14,906] ~ overlap (+6.6%)
for...of with destructuring 8,362 ops/sec [7,830..9,210] → 7,597 ops/sec [7,495..8,419] ~ overlap (-9.2%) 14,679 ops/sec [14,463..14,729] → 15,124 ops/sec [14,434..15,819] ~ overlap (+3.0%)
for-await-of with sync array 8,721 ops/sec [7,861..10,350] → 8,670 ops/sec [8,647..10,183] ~ overlap (-0.6%) 1,672 ops/sec [1,513..1,835] → 1,845 ops/sec [1,759..1,935] ~ overlap (+10.4%)
generators.js — Interp: 🟢 1, 3 unch. · avg +2.3% · Bytecode: 🟢 1, 3 unch. · avg -0.2%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
manual next over object generator 597 ops/sec [590..602] → 690 ops/sec [674..695] 🟢 +15.6% 1,018 ops/sec [1,007..1,140] → 1,020 ops/sec [993..1,120] ~ overlap (+0.1%)
for...of over object generator 970 ops/sec [859..1,054] → 982 ops/sec [954..991] ~ overlap (+1.2%) 1,464 ops/sec [1,456..1,471] → 1,481 ops/sec [1,474..1,482] 🟢 +1.2%
yield delegation 972 ops/sec [970..1,046] → 982 ops/sec [966..1,094] ~ overlap (+1.0%) 1,498 ops/sec [1,224..1,703] → 1,475 ops/sec [1,466..1,488] ~ overlap (-1.5%)
class generator method 967 ops/sec [964..970] → 885 ops/sec [872..967] ~ overlap (-8.5%) 1,501 ops/sec [1,403..1,574] → 1,494 ops/sec [1,474..1,687] ~ overlap (-0.5%)
intl.js — Interp: 🔴 1, 5 unch. · avg -1.6% · Bytecode: 6 unch. · avg +1.7%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
format decimal 22,927 ops/sec [22,901..23,918] → 23,163 ops/sec [22,937..27,012] ~ overlap (+1.0%) 25,834 ops/sec [21,737..27,883] → 27,838 ops/sec [24,658..27,943] ~ overlap (+7.8%)
format currency 18,742 ops/sec [18,480..19,038] → 20,057 ops/sec [18,087..23,172] ~ overlap (+7.0%) 19,494 ops/sec [19,259..19,875] → 19,742 ops/sec [19,615..20,314] ~ overlap (+1.3%)
format UTC date 2,452 ops/sec [2,430..2,512] → 2,335 ops/sec [2,331..2,388] 🔴 -4.8% 2,315 ops/sec [2,272..2,394] → 2,302 ops/sec [2,291..2,349] ~ overlap (-0.5%)
formatRange UTC dates 3,135 ops/sec [2,948..3,213] → 2,971 ops/sec [2,949..2,992] ~ overlap (-5.2%) 2,740 ops/sec [2,691..2,945] → 2,760 ops/sec [2,712..2,765] ~ overlap (+0.7%)
compare numeric strings 98,463 ops/sec [93,560..103,625] → 96,086 ops/sec [95,622..99,845] ~ overlap (-2.4%) 106,306 ops/sec [102,361..116,869] → 105,656 ops/sec [100,362..115,659] ~ overlap (-0.6%)
sort short string list 19,784 ops/sec [18,470..24,471] → 18,737 ops/sec [18,210..18,776] ~ overlap (-5.3%) 20,056 ops/sec [19,852..20,142] → 20,324 ops/sec [20,107..21,083] ~ overlap (+1.3%)
iterators.js — Interp: 🟢 2, 40 unch. · avg +3.1% · Bytecode: 🟢 2, 🔴 6, 34 unch. · avg -2.0%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
Iterator.from({next}).toArray() — 20 elements 2,860 ops/sec [2,759..3,210] → 2,908 ops/sec [2,864..2,918] ~ overlap (+1.7%) 3,552 ops/sec [3,523..3,798] → 3,913 ops/sec [3,898..3,994] 🟢 +10.2%
Iterator.from({next}).toArray() — 50 elements 1,197 ops/sec [1,178..1,202] → 1,223 ops/sec [1,211..1,225] 🟢 +2.2% 1,537 ops/sec [1,526..1,567] → 1,677 ops/sec [1,669..1,681] 🟢 +9.1%
spread pre-wrapped iterator — 20 elements 2,996 ops/sec [2,987..3,207] → 3,087 ops/sec [3,066..3,091] ~ overlap (+3.1%) 4,987 ops/sec [4,905..7,633] → 5,570 ops/sec [5,248..7,461] ~ overlap (+11.7%)
Iterator.from({next}).forEach — 50 elements 863 ops/sec [854..908] → 873 ops/sec [825..913] ~ overlap (+1.1%) 1,203 ops/sec [1,192..1,384] → 1,237 ops/sec [1,193..1,296] ~ overlap (+2.8%)
Iterator.from({next}).reduce — 50 elements 966 ops/sec [894..1,072] → 947 ops/sec [933..950] ~ overlap (-1.9%) 1,268 ops/sec [1,221..1,268] → 1,260 ops/sec [1,254..1,267] ~ overlap (-0.6%)
wrap array iterator 17,396 ops/sec [16,805..17,816] → 19,068 ops/sec [17,092..21,433] ~ overlap (+9.6%) 18,307 ops/sec [17,618..18,461] → 17,374 ops/sec [17,271..17,844] ~ overlap (-5.1%)
wrap plain {next()} object 1,924 ops/sec [1,889..1,929] → 1,910 ops/sec [1,846..2,013] ~ overlap (-0.7%) 2,744 ops/sec [2,671..3,259] → 2,634 ops/sec [2,594..2,821] ~ overlap (-4.0%)
map + toArray (50 elements) 721 ops/sec [705..732] → 741 ops/sec [729..759] ~ overlap (+2.8%) 933 ops/sec [897..1,044] → 885 ops/sec [871..961] ~ overlap (-5.2%)
filter + toArray (50 elements) 778 ops/sec [769..783] → 784 ops/sec [766..948] ~ overlap (+0.8%) 978 ops/sec [961..1,295] → 952 ops/sec [911..1,105] ~ overlap (-2.7%)
take(10) + toArray (50 element source) 3,919 ops/sec [3,905..4,127] → 4,616 ops/sec [3,946..5,424] ~ overlap (+17.8%) 5,061 ops/sec [4,974..5,135] → 4,837 ops/sec [4,774..7,159] ~ overlap (-4.4%)
drop(40) + toArray (50 element source) 984 ops/sec [967..1,110] → 1,015 ops/sec [977..1,045] ~ overlap (+3.1%) 1,352 ops/sec [1,325..1,402] → 1,338 ops/sec [1,304..1,506] ~ overlap (-1.0%)
chained map + filter + take (100 element source) 1,193 ops/sec [1,174..1,210] → 1,279 ops/sec [1,203..1,394] ~ overlap (+7.2%) 1,553 ops/sec [1,447..1,747] → 1,379 ops/sec [1,371..1,555] ~ overlap (-11.2%)
some + every (50 elements) 522 ops/sec [517..532] → 529 ops/sec [525..545] ~ overlap (+1.3%) 737 ops/sec [734..739] → 706 ops/sec [696..1,057] ~ overlap (-4.2%)
find (50 elements) 1,191 ops/sec [1,140..1,263] → 1,179 ops/sec [1,140..1,229] ~ overlap (-1.0%) 1,602 ops/sec [1,598..1,610] → 1,590 ops/sec [1,547..1,608] ~ overlap (-0.7%)
concat 2 arrays (10 + 10 elements) 6,955 ops/sec [6,539..8,602] → 6,787 ops/sec [6,424..7,429] ~ overlap (-2.4%) 6,793 ops/sec [6,697..6,934] → 6,822 ops/sec [6,471..6,998] ~ overlap (+0.4%)
concat 5 arrays (10 elements each) 2,818 ops/sec [2,751..2,898] → 2,925 ops/sec [2,903..3,773] 🟢 +3.8% 2,931 ops/sec [2,863..3,042] → 2,710 ops/sec [2,608..2,906] ~ overlap (-7.5%)
concat 2 arrays (20 + 20 elements) 4,078 ops/sec [3,557..5,016] → 3,689 ops/sec [3,676..3,708] ~ overlap (-9.5%) 3,673 ops/sec [3,641..4,074] → 3,389 ops/sec [3,326..3,552] 🔴 -7.7%
concat + filter + toArray (20 + 20 elements) 1,531 ops/sec [1,522..1,818] → 1,574 ops/sec [1,522..1,836] ~ overlap (+2.8%) 1,666 ops/sec [1,611..1,719] → 1,525 ops/sec [1,517..1,550] 🔴 -8.5%
concat + map + take (20 + 20 elements, take 10) 3,804 ops/sec [3,773..3,814] → 3,881 ops/sec [3,609..3,994] ~ overlap (+2.0%) 3,932 ops/sec [3,877..5,236] → 3,755 ops/sec [3,711..3,792] 🔴 -4.5%
concat Sets (15 + 15 elements) 4,902 ops/sec [4,813..5,784] → 4,970 ops/sec [4,777..5,822] ~ overlap (+1.4%) 4,891 ops/sec [4,836..5,044] → 4,636 ops/sec [4,609..4,672] 🔴 -5.2%
concat strings (13 + 13 characters) 5,318 ops/sec [5,149..5,489] → 5,370 ops/sec [5,274..8,648] ~ overlap (+1.0%) 5,575 ops/sec [4,992..6,964] → 5,173 ops/sec [4,955..6,275] ~ overlap (-7.2%)
zip 2 arrays (10 + 10 elements) 12,236 ops/sec [12,173..15,337] → 12,260 ops/sec [12,093..16,993] ~ overlap (+0.2%) 12,259 ops/sec [12,182..12,359] → 11,681 ops/sec [11,396..12,091] 🔴 -4.7%
zip 3 arrays (10 elements each) 12,749 ops/sec [11,795..12,935] → 12,117 ops/sec [11,245..13,827] ~ overlap (-5.0%) 11,499 ops/sec [11,450..11,586] → 10,784 ops/sec [10,632..12,692] ~ overlap (-6.2%)
zip 2 arrays (20 + 20 elements) 8,165 ops/sec [7,100..8,930] → 7,397 ops/sec [7,294..8,042] ~ overlap (-9.4%) 7,472 ops/sec [7,377..7,854] → 7,111 ops/sec [7,031..8,557] ~ overlap (-4.8%)
zip 2 arrays (50 + 50 elements) 3,353 ops/sec [3,208..4,247] → 3,681 ops/sec [3,318..3,825] ~ overlap (+9.8%) 3,359 ops/sec [3,269..3,378] → 3,576 ops/sec [3,207..3,629] ~ overlap (+6.5%)
zip shortest mode (20 + 10 elements) 12,181 ops/sec [11,904..16,714] → 13,743 ops/sec [12,015..18,983] ~ overlap (+12.8%) 12,545 ops/sec [12,415..13,518] → 14,055 ops/sec [11,988..14,919] ~ overlap (+12.0%)
zip longest mode (10 + 20 elements) 6,900 ops/sec [6,795..7,137] → 7,681 ops/sec [6,834..7,923] ~ overlap (+11.3%) 7,291 ops/sec [6,707..8,608] → 6,809 ops/sec [6,637..9,507] ~ overlap (-6.6%)
zip strict mode (20 + 20 elements) 7,118 ops/sec [7,008..8,914] → 7,677 ops/sec [7,197..7,974] ~ overlap (+7.9%) 7,256 ops/sec [7,178..7,290] → 6,967 ops/sec [6,726..7,728] ~ overlap (-4.0%)
zip + map + toArray (20 + 20 elements) 2,336 ops/sec [2,328..2,349] → 2,357 ops/sec [2,318..2,635] ~ overlap (+0.9%) 2,361 ops/sec [2,273..2,719] → 2,253 ops/sec [2,105..2,642] ~ overlap (-4.6%)
zip + filter + toArray (20 + 20 elements) 2,559 ops/sec [2,419..2,924] → 2,541 ops/sec [2,422..2,957] ~ overlap (-0.7%) 2,371 ops/sec [2,332..2,803] → 2,306 ops/sec [2,275..2,334] ~ overlap (-2.7%)
zip Sets (15 + 15 elements) 9,549 ops/sec [9,400..10,683] → 9,766 ops/sec [9,440..14,609] ~ overlap (+2.3%) 9,774 ops/sec [9,020..14,200] → 9,505 ops/sec [9,257..10,453] ~ overlap (-2.8%)
zipKeyed 2 keys (10 elements each) 11,773 ops/sec [11,432..13,248] → 12,555 ops/sec [11,928..13,489] ~ overlap (+6.6%) 11,752 ops/sec [11,626..11,904] → 11,830 ops/sec [11,758..11,859] ~ overlap (+0.7%)
zipKeyed 3 keys (20 elements each) 6,346 ops/sec [6,283..6,386] → 6,324 ops/sec [6,046..6,452] ~ overlap (-0.3%) 5,955 ops/sec [5,943..5,979] → 6,025 ops/sec [5,878..6,080] ~ overlap (+1.2%)
zipKeyed longest mode (10 + 20 elements) 7,022 ops/sec [6,356..8,066] → 7,325 ops/sec [6,583..9,205] ~ overlap (+4.3%) 6,464 ops/sec [6,319..7,580] → 6,288 ops/sec [6,256..6,592] ~ overlap (-2.7%)
zipKeyed strict mode (20 + 20 elements) 6,599 ops/sec [6,456..8,206] → 8,016 ops/sec [6,705..8,819] ~ overlap (+21.5%) 6,767 ops/sec [6,511..8,600] → 7,060 ops/sec [6,813..7,218] ~ overlap (+4.3%)
zipKeyed + filter + map (20 elements) 2,168 ops/sec [2,139..2,731] → 2,157 ops/sec [2,086..2,579] ~ overlap (-0.5%) 2,341 ops/sec [2,324..2,352] → 2,182 ops/sec [2,181..2,188] 🔴 -6.8%
array.values().map().filter().toArray() 857 ops/sec [829..973] → 874 ops/sec [868..1,059] ~ overlap (+1.9%) 913 ops/sec [912..915] → 908 ops/sec [876..1,017] ~ overlap (-0.6%)
array.values().take(5).toArray() 14,964 ops/sec [14,406..17,193] → 15,451 ops/sec [14,425..16,033] ~ overlap (+3.3%) 15,136 ops/sec [15,028..15,237] → 14,646 ops/sec [14,352..15,683] ~ overlap (-3.2%)
array.values().drop(45).toArray() 3,210 ops/sec [3,074..3,228] → 3,726 ops/sec [3,106..3,859] ~ overlap (+16.1%) 3,199 ops/sec [3,149..3,234] → 3,000 ops/sec [2,895..3,280] ~ overlap (-6.2%)
map.entries() chained helpers 1,189 ops/sec [1,148..1,243] → 1,200 ops/sec [1,162..1,435] ~ overlap (+0.9%) 1,152 ops/sec [1,113..1,492] → 1,165 ops/sec [1,098..1,227] ~ overlap (+1.1%)
set.values() chained helpers 2,171 ops/sec [2,036..2,586] → 2,104 ops/sec [2,036..2,407] ~ overlap (-3.1%) 2,199 ops/sec [2,156..2,261] → 2,078 ops/sec [2,012..2,475] ~ overlap (-5.5%)
string iterator map + toArray 2,172 ops/sec [2,154..2,183] → 2,232 ops/sec [2,113..2,348] ~ overlap (+2.8%) 2,156 ops/sec [2,098..2,318] → 2,117 ops/sec [1,970..2,178] ~ overlap (-1.8%)
json.js — Interp: 🟢 4, 19 unch. · avg +3.1% · Bytecode: 🟢 3, 🔴 2, 18 unch. · avg +0.6%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
parse simple object 57,302 ops/sec [56,763..58,336] → 58,815 ops/sec [58,113..65,374] ~ overlap (+2.6%) 67,298 ops/sec [67,014..69,320] → 62,859 ops/sec [61,433..67,843] ~ overlap (-6.6%)
parse nested object 38,392 ops/sec [37,552..47,153] → 41,777 ops/sec [40,114..49,983] ~ overlap (+8.8%) 42,762 ops/sec [42,624..43,098] → 42,674 ops/sec [41,667..43,717] ~ overlap (-0.2%)
parse array of objects 24,935 ops/sec [23,356..29,179] → 24,733 ops/sec [23,960..30,785] ~ overlap (-0.8%) 26,769 ops/sec [25,695..30,153] → 28,089 ops/sec [25,494..29,775] ~ overlap (+4.9%)
parse large flat object 25,975 ops/sec [25,883..26,175] → 25,749 ops/sec [25,582..42,532] ~ overlap (-0.9%) 27,533 ops/sec [27,387..27,753] → 28,154 ops/sec [27,981..28,234] 🟢 +2.3%
parse mixed types 29,771 ops/sec [29,222..30,101] → 32,616 ops/sec [30,410..34,595] 🟢 +9.6% 32,560 ops/sec [32,300..33,438] → 33,097 ops/sec [32,870..33,544] ~ overlap (+1.7%)
stringify simple object 56,636 ops/sec [55,461..78,875] → 60,482 ops/sec [56,959..74,717] ~ overlap (+6.8%) 54,316 ops/sec [53,837..57,167] → 54,898 ops/sec [53,375..58,114] ~ overlap (+1.1%)
stringify nested object 34,954 ops/sec [33,399..35,322] → 34,902 ops/sec [34,130..37,484] ~ overlap (-0.1%) 30,431 ops/sec [30,010..33,741] → 30,919 ops/sec [29,317..31,511] ~ overlap (+1.6%)
stringify array of objects 15,661 ops/sec [15,260..19,186] → 15,860 ops/sec [15,367..16,325] ~ overlap (+1.3%) 16,829 ops/sec [16,018..16,883] → 16,486 ops/sec [16,121..18,040] ~ overlap (-2.0%)
stringify mixed types 22,527 ops/sec [22,054..25,254] → 24,955 ops/sec [23,192..26,854] ~ overlap (+10.8%) 21,571 ops/sec [21,278..24,966] → 21,600 ops/sec [21,379..23,305] ~ overlap (+0.1%)
reviver doubles numbers 9,529 ops/sec [9,455..12,460] → 9,924 ops/sec [9,651..9,937] ~ overlap (+4.1%) 13,776 ops/sec [13,696..13,795] → 13,880 ops/sec [13,695..13,910] ~ overlap (+0.8%)
reviver filters properties 9,629 ops/sec [9,515..9,681] → 9,915 ops/sec [9,822..10,037] 🟢 +3.0% 12,069 ops/sec [11,919..13,181] → 12,414 ops/sec [12,341..12,467] ~ overlap (+2.9%)
reviver on nested object 11,435 ops/sec [11,409..12,170] → 12,120 ops/sec [11,610..14,721] ~ overlap (+6.0%) 14,831 ops/sec [14,565..16,128] → 15,109 ops/sec [14,967..15,728] ~ overlap (+1.9%)
reviver on array 5,971 ops/sec [5,809..6,692] → 6,451 ops/sec [5,989..6,905] ~ overlap (+8.0%) 8,662 ops/sec [8,529..8,925] → 8,776 ops/sec [8,568..8,906] ~ overlap (+1.3%)
replacer function doubles numbers 10,687 ops/sec [10,183..10,949] → 10,666 ops/sec [10,432..10,872] ~ overlap (-0.2%) 15,139 ops/sec [14,499..19,010] → 14,611 ops/sec [14,280..14,716] ~ overlap (-3.5%)
replacer function excludes properties 13,989 ops/sec [13,612..14,254] → 14,770 ops/sec [13,951..16,185] ~ overlap (+5.6%) 19,230 ops/sec [17,816..23,809] → 20,187 ops/sec [18,190..21,704] ~ overlap (+5.0%)
array replacer (allowlist) 36,577 ops/sec [36,383..37,880] → 41,247 ops/sec [38,771..46,835] 🟢 +12.8% 34,611 ops/sec [33,781..44,570] → 34,325 ops/sec [34,004..41,119] ~ overlap (-0.8%)
stringify with 2-space indent 32,098 ops/sec [30,962..37,688] → 31,331 ops/sec [30,855..31,473] ~ overlap (-2.4%) 31,020 ops/sec [30,469..31,056] → 30,024 ops/sec [29,884..30,111] 🔴 -3.2%
stringify with tab indent 30,677 ops/sec [30,518..30,739] → 32,076 ops/sec [31,547..38,278] 🟢 +4.6% 30,855 ops/sec [30,775..30,937] → 30,081 ops/sec [29,553..31,203] ~ overlap (-2.5%)
stringify deeply nested object with 2-space indent 6,648 ops/sec [6,405..7,221] → 6,563 ops/sec [6,372..7,484] ~ overlap (-1.3%) 6,621 ops/sec [6,447..6,670] → 6,555 ops/sec [6,477..6,654] ~ overlap (-1.0%)
stringify deeply nested array with 2-space indent 9,610 ops/sec [9,200..11,243] → 9,518 ops/sec [9,163..10,920] ~ overlap (-1.0%) 9,738 ops/sec [9,676..11,107] → 9,433 ops/sec [9,203..9,556] 🔴 -3.1%
stringify very deeply nested object with 2-space indent 1,345 ops/sec [1,203..1,489] → 1,193 ops/sec [1,133..1,343] ~ overlap (-11.3%) 1,121 ops/sec [1,107..1,137] → 1,152 ops/sec [1,145..1,155] 🟢 +2.7%
parse then stringify 19,464 ops/sec [19,389..30,223] → 19,502 ops/sec [17,840..20,060] ~ overlap (+0.2%) 20,911 ops/sec [20,798..21,008] → 21,371 ops/sec [21,012..23,545] 🟢 +2.2%
stringify then parse 11,738 ops/sec [11,620..11,932] → 12,381 ops/sec [11,615..13,204] ~ overlap (+5.5%) 11,809 ops/sec [11,669..12,632] → 12,701 ops/sec [12,311..13,643] ~ overlap (+7.6%)
jsx.jsx — Interp: 🟢 1, 20 unch. · avg -0.6% · Bytecode: 🟢 3, 🔴 3, 15 unch. · avg -0.1%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
simple element 83,110 ops/sec [81,108..83,935] → 75,731 ops/sec [74,575..92,799] ~ overlap (-8.9%) 87,182 ops/sec [86,195..89,354] → 88,988 ops/sec [88,456..97,199] ~ overlap (+2.1%)
self-closing element 85,992 ops/sec [77,188..94,375] → 78,400 ops/sec [76,021..84,770] ~ overlap (-8.8%) 93,250 ops/sec [92,451..96,836] → 92,300 ops/sec [87,988..106,270] ~ overlap (-1.0%)
element with string attribute 65,745 ops/sec [65,065..74,866] → 71,199 ops/sec [68,786..78,190] ~ overlap (+8.3%) 72,433 ops/sec [71,917..81,865] → 70,575 ops/sec [67,181..75,400] ~ overlap (-2.6%)
element with multiple attributes 61,682 ops/sec [55,178..68,641] → 58,501 ops/sec [56,955..66,101] ~ overlap (-5.2%) 52,552 ops/sec [49,034..66,417] → 55,473 ops/sec [51,453..61,697] ~ overlap (+5.6%)
element with expression attribute 62,377 ops/sec [58,174..67,412] → 62,097 ops/sec [58,321..65,706] ~ overlap (-0.4%) 70,725 ops/sec [68,744..95,117] → 77,962 ops/sec [70,235..79,959] ~ overlap (+10.2%)
text child 76,197 ops/sec [74,312..93,432] → 80,111 ops/sec [74,889..91,551] ~ overlap (+5.1%) 87,096 ops/sec [86,762..87,194] → 86,759 ops/sec [85,686..92,385] ~ overlap (-0.4%)
expression child 78,566 ops/sec [70,843..87,343] → 71,208 ops/sec [70,673..81,811] ~ overlap (-9.4%) 82,733 ops/sec [82,095..82,976] → 81,301 ops/sec [76,500..96,192] ~ overlap (-1.7%)
mixed text and expression 75,536 ops/sec [67,178..75,987] → 68,304 ops/sec [64,218..69,629] ~ overlap (-9.6%) 74,129 ops/sec [73,275..76,385] → 75,459 ops/sec [71,362..76,567] ~ overlap (+1.8%)
nested elements (3 levels) 29,799 ops/sec [29,638..32,629] → 31,922 ops/sec [30,977..38,195] ~ overlap (+7.1%) 36,526 ops/sec [33,530..41,274] → 31,702 ops/sec [31,509..32,005] 🔴 -13.2%
sibling children 22,837 ops/sec [21,902..35,835] → 23,120 ops/sec [23,116..23,154] ~ overlap (+1.2%) 26,734 ops/sec [24,816..29,959] → 23,484 ops/sec [23,348..23,775] 🔴 -12.2%
component element 55,769 ops/sec [53,521..88,005] → 60,411 ops/sec [55,125..73,461] ~ overlap (+8.3%) 73,840 ops/sec [62,775..78,945] → 63,042 ops/sec [62,282..65,104] ~ overlap (-14.6%)
component with children 41,130 ops/sec [34,882..47,005] → 35,723 ops/sec [35,108..46,460] ~ overlap (-13.1%) 47,257 ops/sec [42,854..49,004] → 37,602 ops/sec [37,482..37,804] 🔴 -20.4%
dotted component 48,210 ops/sec [47,808..58,761] → 51,294 ops/sec [47,309..65,566] ~ overlap (+6.4%) 51,114 ops/sec [50,728..72,843] → 48,737 ops/sec [48,549..51,632] ~ overlap (-4.6%)
empty fragment 79,988 ops/sec [78,757..82,073] → 81,274 ops/sec [80,019..99,533] ~ overlap (+1.6%) 91,237 ops/sec [90,802..92,097] → 92,505 ops/sec [90,400..95,863] ~ overlap (+1.4%)
fragment with children 23,243 ops/sec [22,417..24,876] → 23,659 ops/sec [23,276..37,842] ~ overlap (+1.8%) 23,672 ops/sec [23,284..23,824] → 24,930 ops/sec [24,737..25,211] 🟢 +5.3%
spread attributes 38,624 ops/sec [38,318..39,281] → 40,138 ops/sec [36,729..48,455] ~ overlap (+3.9%) 38,335 ops/sec [35,373..40,284] → 38,166 ops/sec [36,923..39,444] ~ overlap (-0.4%)
spread with overrides 34,551 ops/sec [33,358..47,362] → 34,586 ops/sec [34,318..42,450] ~ overlap (+0.1%) 31,502 ops/sec [31,226..33,355] → 34,380 ops/sec [33,633..39,056] 🟢 +9.1%
shorthand props 63,328 ops/sec [56,631..81,608] → 57,902 ops/sec [55,889..59,646] ~ overlap (-8.6%) 55,789 ops/sec [55,538..55,929] → 60,943 ops/sec [58,205..62,586] 🟢 +9.2%
nav bar structure 10,995 ops/sec [10,866..11,167] → 11,792 ops/sec [11,238..14,163] 🟢 +7.3% 11,280 ops/sec [10,576..12,785] → 11,853 ops/sec [11,259..13,807] ~ overlap (+5.1%)
card component tree 13,709 ops/sec [12,769..14,110] → 13,359 ops/sec [12,927..14,497] ~ overlap (-2.6%) 12,730 ops/sec [12,059..12,893] → 15,098 ops/sec [12,614..17,874] ~ overlap (+18.6%)
10 list items via Array.from 5,832 ops/sec [5,680..5,973] → 5,930 ops/sec [5,556..6,628] ~ overlap (+1.7%) 5,306 ops/sec [5,238..5,516] → 5,394 ops/sec [5,232..6,635] ~ overlap (+1.7%)
modules.js — Interp: 9 unch. · avg +1.0% · Bytecode: 9 unch. · avg +0.5%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
call imported function 124,693 ops/sec [121,512..135,365] → 126,403 ops/sec [125,209..130,005] ~ overlap (+1.4%) 44,730 ops/sec [44,450..46,606] → 48,437 ops/sec [44,333..52,733] ~ overlap (+8.3%)
call two imported functions 78,521 ops/sec [71,244..90,567] → 73,146 ops/sec [71,956..80,801] ~ overlap (-6.8%) 23,193 ops/sec [23,013..23,432] → 22,897 ops/sec [22,639..26,721] ~ overlap (-1.3%)
read imported constant 334,838 ops/sec [330,966..363,933] → 340,260 ops/sec [325,848..372,115] ~ overlap (+1.6%) 47,371 ops/sec [46,538..52,453] → 48,167 ops/sec [47,028..57,675] ~ overlap (+1.7%)
read imported string 348,427 ops/sec [331,656..409,606] → 335,357 ops/sec [333,229..423,411] ~ overlap (-3.8%) 48,142 ops/sec [46,226..53,840] → 48,606 ops/sec [47,257..48,894] ~ overlap (+1.0%)
read JSON string property 337,735 ops/sec [332,257..363,637] → 340,265 ops/sec [335,848..440,484] ~ overlap (+0.7%) 48,741 ops/sec [48,066..52,868] → 48,032 ops/sec [47,840..48,120] ~ overlap (-1.5%)
read JSON number property 332,572 ops/sec [325,030..340,861] → 378,039 ops/sec [333,663..480,029] ~ overlap (+13.7%) 47,533 ops/sec [45,577..53,232] → 47,728 ops/sec [46,991..47,936] ~ overlap (+0.4%)
read JSON boolean property 343,600 ops/sec [338,559..344,899] → 359,392 ops/sec [336,905..397,251] ~ overlap (+4.6%) 50,824 ops/sec [46,869..55,740] → 52,460 ops/sec [48,550..53,064] ~ overlap (+3.2%)
read JSON array property 346,721 ops/sec [335,636..349,957] → 357,033 ops/sec [328,625..463,144] ~ overlap (+3.0%) 49,855 ops/sec [47,268..55,560] → 48,716 ops/sec [47,044..54,134] ~ overlap (-2.3%)
read multiple JSON properties 209,130 ops/sec [194,294..209,414] → 198,344 ops/sec [197,434..234,548] ~ overlap (-5.2%) 17,577 ops/sec [16,419..18,475] → 16,700 ops/sec [16,368..16,842] ~ overlap (-5.0%)
numbers.js — Interp: 11 unch. · avg -2.0% · Bytecode: 🟢 4, 7 unch. · avg +6.0%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
integer arithmetic 97,336 ops/sec [94,484..110,980] → 99,193 ops/sec [94,628..138,100] ~ overlap (+1.9%) 364,391 ops/sec [364,224..364,668] → 378,177 ops/sec [366,285..456,338] 🟢 +3.8%
floating point arithmetic 111,982 ops/sec [106,688..114,591] → 113,705 ops/sec [109,536..143,540] ~ overlap (+1.5%) 205,373 ops/sec [203,029..211,970] → 237,465 ops/sec [210,613..255,250] ~ overlap (+15.6%)
number coercion 43,329 ops/sec [40,294..45,655] → 41,867 ops/sec [40,711..42,711] ~ overlap (-3.4%) 57,090 ops/sec [56,766..57,307] → 60,605 ops/sec [60,103..64,168] 🟢 +6.2%
toFixed 34,408 ops/sec [33,345..38,411] → 34,928 ops/sec [34,618..37,646] ~ overlap (+1.5%) 34,792 ops/sec [34,354..38,251] → 37,835 ops/sec [37,351..48,307] ~ overlap (+8.7%)
toString 52,901 ops/sec [50,292..54,581] → 51,326 ops/sec [48,537..52,733] ~ overlap (-3.0%) 61,631 ops/sec [57,670..62,816] → 64,122 ops/sec [61,691..78,904] ~ overlap (+4.0%)
valueOf 74,473 ops/sec [70,828..97,168] → 72,786 ops/sec [71,088..74,348] ~ overlap (-2.3%) 89,558 ops/sec [85,927..90,606] → 92,197 ops/sec [82,339..93,397] ~ overlap (+2.9%)
toPrecision 24,678 ops/sec [23,210..25,588] → 24,275 ops/sec [23,089..34,258] ~ overlap (-1.6%) 25,030 ops/sec [24,155..25,955] → 29,245 ops/sec [26,460..31,180] 🟢 +16.8%
Number.isNaN 78,163 ops/sec [75,565..79,728] → 78,982 ops/sec [76,256..80,351] ~ overlap (+1.0%) 96,134 ops/sec [95,823..96,561] → 106,322 ops/sec [98,651..126,899] 🟢 +10.6%
Number.isFinite 75,076 ops/sec [73,705..75,549] → 76,957 ops/sec [74,086..84,852] ~ overlap (+2.5%) 89,472 ops/sec [86,802..91,930] → 91,947 ops/sec [89,550..95,880] ~ overlap (+2.8%)
Number.isInteger 90,935 ops/sec [76,740..104,807] → 79,291 ops/sec [77,411..81,631] ~ overlap (-12.8%) 102,325 ops/sec [94,312..106,338] → 96,251 ops/sec [95,005..98,031] ~ overlap (-5.9%)
Number.parseInt and parseFloat 72,218 ops/sec [66,786..77,622] → 67,079 ops/sec [65,595..71,024] ~ overlap (-7.1%) 73,359 ops/sec [69,605..77,904] → 73,911 ops/sec [69,898..74,812] ~ overlap (+0.8%)
objects.js — Interp: 🟢 1, 6 unch. · avg +1.1% · Bytecode: 7 unch. · avg -2.2%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
create simple object 143,436 ops/sec [139,287..146,770] → 156,223 ops/sec [150,359..213,004] 🟢 +8.9% 129,126 ops/sec [127,907..145,590] → 127,641 ops/sec [122,953..137,829] ~ overlap (-1.2%)
create nested object 94,437 ops/sec [79,529..109,706] → 88,798 ops/sec [81,392..89,474] ~ overlap (-6.0%) 63,499 ops/sec [60,032..68,734] → 61,629 ops/sec [56,828..85,223] ~ overlap (-2.9%)
create 50 objects via Array.from 3,315 ops/sec [3,296..3,697] → 3,293 ops/sec [3,181..3,853] ~ overlap (-0.7%) 2,451 ops/sec [2,428..2,759] → 2,488 ops/sec [2,387..2,827] ~ overlap (+1.5%)
property read 132,401 ops/sec [126,630..164,527] → 131,618 ops/sec [124,678..132,507] ~ overlap (-0.6%) 281,996 ops/sec [262,148..290,175] → 270,578 ops/sec [259,939..299,596] ~ overlap (-4.0%)
Object.keys 83,438 ops/sec [82,517..83,881] → 84,097 ops/sec [80,493..110,749] ~ overlap (+0.8%) 109,094 ops/sec [97,505..123,387] → 101,095 ops/sec [93,147..107,285] ~ overlap (-7.3%)
Object.entries 47,398 ops/sec [40,059..51,104] → 40,939 ops/sec [39,414..61,996] ~ overlap (-13.6%) 46,306 ops/sec [43,034..60,139] → 44,217 ops/sec [42,670..55,706] ~ overlap (-4.5%)
spread operator 50,742 ops/sec [50,129..52,760] → 60,331 ops/sec [51,842..83,234] ~ overlap (+18.9%) 53,259 ops/sec [53,146..53,267] → 55,027 ops/sec [51,828..62,541] ~ overlap (+3.3%)
promises.js — Interp: 12 unch. · avg -0.1% · Bytecode: 12 unch. · avg +0.5%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
Promise.resolve(value) 68,295 ops/sec [67,963..68,810] → 69,379 ops/sec [66,900..81,757] ~ overlap (+1.6%) 75,510 ops/sec [72,971..80,057] → 74,830 ops/sec [74,183..76,139] ~ overlap (-0.9%)
new Promise(resolve => resolve(value)) 57,498 ops/sec [49,063..64,055] → 51,935 ops/sec [49,844..61,836] ~ overlap (-9.7%) 58,437 ops/sec [57,236..58,786] → 56,548 ops/sec [56,248..76,846] ~ overlap (-3.2%)
Promise.reject(reason) 69,037 ops/sec [68,474..93,339] → 74,704 ops/sec [67,498..80,627] ~ overlap (+8.2%) 79,489 ops/sec [74,890..90,488] → 88,177 ops/sec [74,621..100,643] ~ overlap (+10.9%)
resolve + then (1 handler) 23,277 ops/sec [22,676..23,505] → 24,215 ops/sec [23,221..28,941] ~ overlap (+4.0%) 25,134 ops/sec [24,268..27,405] → 24,872 ops/sec [24,754..31,776] ~ overlap (-1.0%)
resolve + then chain (3 deep) 10,437 ops/sec [9,569..12,210] → 10,171 ops/sec [9,930..10,367] ~ overlap (-2.5%) 10,503 ops/sec [10,474..10,566] → 10,666 ops/sec [10,513..12,907] ~ overlap (+1.5%)
resolve + then chain (10 deep) 3,259 ops/sec [3,221..3,476] → 3,284 ops/sec [3,265..3,319] ~ overlap (+0.8%) 3,511 ops/sec [3,454..3,650] → 3,435 ops/sec [3,414..3,552] ~ overlap (-2.2%)
reject + catch + then 16,002 ops/sec [13,538..17,061] → 14,178 ops/sec [13,655..15,108] ~ overlap (-11.4%) 14,398 ops/sec [13,879..14,572] → 14,120 ops/sec [14,038..15,400] ~ overlap (-1.9%)
resolve + finally + then 6,430 ops/sec [6,298..6,705] → 6,665 ops/sec [6,615..7,245] ~ overlap (+3.7%) 6,698 ops/sec [6,568..6,716] → 6,779 ops/sec [6,599..7,100] ~ overlap (+1.2%)
Promise.all (5 resolved) 4,625 ops/sec [4,407..5,163] → 4,561 ops/sec [4,503..4,592] ~ overlap (-1.4%) 4,659 ops/sec [4,613..4,874] → 4,779 ops/sec [4,758..4,795] ~ overlap (+2.6%)
Promise.race (5 resolved) 5,412 ops/sec [5,106..6,834] → 5,276 ops/sec [5,067..6,111] ~ overlap (-2.5%) 5,308 ops/sec [5,295..5,316] → 5,475 ops/sec [5,134..5,862] ~ overlap (+3.2%)
Promise.allSettled (5 mixed) 3,936 ops/sec [3,769..4,808] → 3,979 ops/sec [3,815..5,140] ~ overlap (+1.1%) 4,078 ops/sec [4,058..4,111] → 3,921 ops/sec [3,646..4,932] ~ overlap (-3.9%)
Promise.any (5 mixed) 4,494 ops/sec [4,416..4,985] → 4,816 ops/sec [4,564..6,132] ~ overlap (+7.2%) 4,818 ops/sec [4,680..4,860] → 4,823 ops/sec [4,544..5,265] ~ overlap (+0.1%)
property-access.js — Interp: 🟢 1, 4 unch. · avg +7.0% · Bytecode: 5 unch. · avg -0.5%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
class instance fields across 1000 instances 133 ops/sec [131..192] → 144 ops/sec [139..170] ~ overlap (+8.3%) 660 ops/sec [637..1,000] → 649 ops/sec [646..656] ~ overlap (-1.7%)
object literal fields across 1000 literals 142 ops/sec [140..143] → 145 ops/sec [140..148] ~ overlap (+2.1%) 680 ops/sec [645..740] → 701 ops/sec [649..731] ~ overlap (+3.0%)
mixed-shape literals across 1000 literals 135 ops/sec [132..137] → 146 ops/sec [140..149] 🟢 +8.0% 534 ops/sec [515..572] → 517 ops/sec [504..545] ~ overlap (-3.1%)
own-class method across 1000 instances 84 ops/sec [81..95] → 94 ops/sec [90..97] ~ overlap (+13.0%) 324 ops/sec [316..334] → 327 ops/sec [315..333] ~ overlap (+1.0%)
inherited method across 1000 instances 93 ops/sec [88..103] → 96 ops/sec [92..108] ~ overlap (+3.6%) 379 ops/sec [356..383] → 373 ops/sec [371..379] ~ overlap (-1.6%)
regexp.js — Interp: 11 unch. · avg -4.0% · Bytecode: 🔴 1, 10 unch. · avg -1.1%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
regex literal creation 8,649 ops/sec [7,520..10,169] → 7,241 ops/sec [6,852..8,493] ~ overlap (-16.3%) 181,271 ops/sec [180,949..181,712] → 177,797 ops/sec [176,424..196,843] ~ overlap (-1.9%)
new RegExp(pattern, flags) 7,225 ops/sec [6,959..7,311] → 6,759 ops/sec [6,610..7,580] ~ overlap (-6.4%) 7,365 ops/sec [7,214..7,504] → 6,800 ops/sec [6,608..6,948] 🔴 -7.7%
RegExp(existingRegex) returns the same regex 173,550 ops/sec [169,809..178,303] → 173,853 ops/sec [165,178..175,567] ~ overlap (+0.2%) 281,468 ops/sec [265,974..291,745] → 273,154 ops/sec [262,553..288,207] ~ overlap (-3.0%)
test() on a global regex 53,901 ops/sec [53,610..62,315] → 55,077 ops/sec [51,719..60,613] ~ overlap (+2.2%) 76,640 ops/sec [73,773..85,602] → 72,625 ops/sec [72,084..73,946] ~ overlap (-5.2%)
exec() with capture groups 15,478 ops/sec [15,180..16,332] → 15,518 ops/sec [14,808..15,702] ~ overlap (+0.3%) 16,723 ops/sec [16,473..17,272] → 16,638 ops/sec [16,179..17,181] ~ overlap (-0.5%)
toString() 151,450 ops/sec [148,618..163,444] → 149,800 ops/sec [145,727..184,525] ~ overlap (-1.1%) 227,469 ops/sec [218,263..256,643] → 222,239 ops/sec [219,759..228,700] ~ overlap (-2.3%)
match() with global regex 21,389 ops/sec [20,305..21,865] → 20,683 ops/sec [20,591..21,646] ~ overlap (-3.3%) 23,050 ops/sec [22,758..24,233] → 22,828 ops/sec [22,739..22,928] ~ overlap (-1.0%)
matchAll() with capture groups 9,776 ops/sec [9,625..9,982] → 9,452 ops/sec [9,236..12,369] ~ overlap (-3.3%) 13,976 ops/sec [13,878..14,074] → 14,116 ops/sec [13,772..14,509] ~ overlap (+1.0%)
replace() with global regex 19,679 ops/sec [18,357..20,335] → 18,688 ops/sec [17,757..18,801] ~ overlap (-5.0%) 19,752 ops/sec [19,281..21,957] → 20,279 ops/sec [19,506..20,883] ~ overlap (+2.7%)
search() with regex 42,491 ops/sec [38,138..49,507] → 38,961 ops/sec [38,754..40,306] ~ overlap (-8.3%) 46,495 ops/sec [44,797..53,142] → 49,904 ops/sec [47,374..53,839] ~ overlap (+7.3%)
split() with regex separator 7,585 ops/sec [7,533..8,377] → 7,356 ops/sec [7,141..9,072] ~ overlap (-3.0%) 7,890 ops/sec [7,829..7,913] → 7,774 ops/sec [7,753..7,915] ~ overlap (-1.5%)
strings.js — Interp: 🟢 3, 16 unch. · avg +5.2% · Bytecode: 🔴 2, 17 unch. · avg -1.7%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
string concatenation 118,790 ops/sec [108,580..166,373] → 131,652 ops/sec [119,408..149,887] ~ overlap (+10.8%) 487,502 ops/sec [471,872..526,318] → 492,803 ops/sec [484,553..598,896] ~ overlap (+1.1%)
template literal 189,435 ops/sec [185,500..201,376] → 253,354 ops/sec [218,681..257,893] 🟢 +33.7% 389,496 ops/sec [370,862..434,564] → 384,692 ops/sec [360,941..439,724] ~ overlap (-1.2%)
string repeat 155,086 ops/sec [132,794..181,112] → 138,331 ops/sec [130,605..165,833] ~ overlap (-10.8%) 156,277 ops/sec [153,819..187,839] → 154,475 ops/sec [151,961..181,594] ~ overlap (-1.2%)
split and join 47,145 ops/sec [46,317..60,008] → 53,432 ops/sec [49,722..61,964] ~ overlap (+13.3%) 62,026 ops/sec [54,742..65,803] → 54,636 ops/sec [52,588..59,631] ~ overlap (-11.9%)
indexOf and includes 51,041 ops/sec [50,415..56,693] → 55,927 ops/sec [51,076..75,880] ~ overlap (+9.6%) 57,322 ops/sec [57,165..59,163] → 60,605 ops/sec [57,085..63,255] ~ overlap (+5.7%)
toUpperCase and toLowerCase 67,854 ops/sec [64,038..97,636] → 67,577 ops/sec [65,938..74,140] ~ overlap (-0.4%) 78,018 ops/sec [77,201..124,939] → 77,367 ops/sec [74,222..85,226] ~ overlap (-0.8%)
slice and substring 48,677 ops/sec [47,221..57,458] → 51,295 ops/sec [50,387..60,180] ~ overlap (+5.4%) 64,940 ops/sec [60,420..67,684] → 61,417 ops/sec [59,664..71,008] ~ overlap (-5.4%)
trim operations 58,353 ops/sec [56,367..67,407] → 60,328 ops/sec [59,318..64,819] ~ overlap (+3.4%) 69,706 ops/sec [65,316..73,465] → 68,280 ops/sec [66,924..70,091] ~ overlap (-2.0%)
replace and replaceAll 56,987 ops/sec [55,594..78,023] → 57,884 ops/sec [56,534..61,555] ~ overlap (+1.6%) 62,903 ops/sec [58,994..67,670] → 61,465 ops/sec [59,698..62,003] ~ overlap (-2.3%)
startsWith and endsWith 43,386 ops/sec [40,508..46,698] → 42,275 ops/sec [41,230..48,052] ~ overlap (-2.6%) 44,623 ops/sec [44,429..45,029] → 44,811 ops/sec [43,707..54,262] ~ overlap (+0.4%)
padStart and padEnd 62,862 ops/sec [62,306..63,211] → 65,784 ops/sec [65,088..65,813] 🟢 +4.6% 73,025 ops/sec [72,689..73,844] → 74,199 ops/sec [71,726..75,983] ~ overlap (+1.6%)
identity tag, no substitutions 102,201 ops/sec [93,669..154,998] → 107,320 ops/sec [105,657..156,862] ~ overlap (+5.0%) 157,201 ops/sec [152,558..158,554] → 157,763 ops/sec [154,111..159,656] ~ overlap (+0.4%)
tag with 1 substitution 24,286 ops/sec [23,317..28,785] → 25,811 ops/sec [25,733..25,895] ~ overlap (+6.3%) 31,053 ops/sec [30,468..33,991] → 31,141 ops/sec [29,930..36,275] ~ overlap (+0.3%)
tag with 3 substitutions 13,669 ops/sec [13,326..15,297] → 14,119 ops/sec [13,922..14,210] ~ overlap (+3.3%) 19,137 ops/sec [18,947..19,685] → 18,974 ops/sec [17,869..27,005] ~ overlap (-0.8%)
tag with 6 substitutions 8,216 ops/sec [6,937..8,303] → 8,308 ops/sec [8,160..8,414] ~ overlap (+1.1%) 11,691 ops/sec [11,655..11,728] → 11,099 ops/sec [10,865..11,654] 🔴 -5.1%
String.raw, no substitutions 83,836 ops/sec [82,103..85,649] → 84,107 ops/sec [80,082..84,636] ~ overlap (+0.3%) 87,323 ops/sec [86,855..87,526] → 87,092 ops/sec [83,247..92,282] ~ overlap (-0.3%)
String.raw, 2 substitutions 66,674 ops/sec [65,236..67,615] → 68,016 ops/sec [67,220..72,969] ~ overlap (+2.0%) 72,430 ops/sec [68,763..78,890] → 67,751 ops/sec [67,241..68,323] 🔴 -6.5%
tag accessing .raw array 39,084 ops/sec [38,171..45,759] → 41,803 ops/sec [41,469..41,981] ~ overlap (+7.0%) 45,161 ops/sec [44,491..45,686] → 44,261 ops/sec [43,305..45,263] ~ overlap (-2.0%)
method as tag (this binding) 16,353 ops/sec [16,251..16,420] → 17,196 ops/sec [17,054..17,335] 🟢 +5.2% 22,649 ops/sec [21,840..23,528] → 22,035 ops/sec [21,875..22,096] ~ overlap (-2.7%)
temporal.js — Interp: 🟢 1, 5 unch. · avg +1.5% · Bytecode: 6 unch. · avg -1.9%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
PlainDate.add({ months: 1 }) 41,657 ops/sec [40,362..42,717] → 43,478 ops/sec [42,735..49,293] 🟢 +4.4% 44,810 ops/sec [43,522..48,674] → 43,895 ops/sec [42,378..50,973] ~ overlap (-2.0%)
PlainDate.until(..., { largestUnit: 'months' }) 49,923 ops/sec [47,876..51,253] → 53,436 ops/sec [50,577..56,684] ~ overlap (+7.0%) 57,536 ops/sec [53,610..59,525] → 53,781 ops/sec [53,516..56,731] ~ overlap (-6.5%)
Duration.total days relative to PlainDate 46,270 ops/sec [44,343..46,523] → 45,900 ops/sec [45,409..73,853] ~ overlap (-0.8%) 45,235 ops/sec [44,684..51,745] → 45,563 ops/sec [43,089..46,592] ~ overlap (+0.7%)
Duration.round to hours 33,501 ops/sec [32,570..33,613] → 33,875 ops/sec [33,453..34,029] ~ overlap (+1.1%) 33,078 ops/sec [32,421..33,370] → 32,654 ops/sec [32,124..33,390] ~ overlap (-1.3%)
ZonedDateTime.from named time zone 9,464 ops/sec [9,316..10,891] → 9,282 ops/sec [9,125..11,924] ~ overlap (-1.9%) 9,504 ops/sec [9,225..9,536] → 9,815 ops/sec [9,356..13,035] ~ overlap (+3.3%)
ZonedDateTime.since across DST 9,770 ops/sec [9,753..11,365] → 9,710 ops/sec [9,672..9,848] ~ overlap (-0.6%) 10,412 ops/sec [9,783..11,520] → 9,823 ops/sec [9,506..11,024] ~ overlap (-5.7%)
tsv.js — Interp: 9 unch. · avg -3.1% · Bytecode: 🟢 1, 🔴 2, 6 unch. · avg +0.0%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
parse simple 3-column TSV 42,840 ops/sec [40,971..50,955] → 40,521 ops/sec [38,937..44,937] ~ overlap (-5.4%) 45,095 ops/sec [42,344..48,857] → 42,118 ops/sec [40,564..49,451] ~ overlap (-6.6%)
parse 10-row TSV 13,339 ops/sec [12,406..14,166] → 11,339 ops/sec [11,258..16,159] ~ overlap (-15.0%) 11,736 ops/sec [11,583..12,082] → 11,277 ops/sec [11,183..12,398] ~ overlap (-3.9%)
parse 100-row TSV 1,864 ops/sec [1,849..1,883] → 2,134 ops/sec [1,816..2,348] ~ overlap (+14.5%) 1,869 ops/sec [1,711..2,684] → 1,940 ops/sec [1,767..2,156] ~ overlap (+3.8%)
parse TSV with backslash-escaped fields 8,670 ops/sec [8,582..10,126] → 8,536 ops/sec [8,384..9,965] ~ overlap (-1.5%) 9,435 ops/sec [8,743..10,720] → 9,599 ops/sec [8,806..10,793] ~ overlap (+1.7%)
parse without headers (array of arrays) 5,472 ops/sec [5,426..6,391] → 5,560 ops/sec [5,474..7,500] ~ overlap (+1.6%) 5,607 ops/sec [5,544..5,647] → 6,035 ops/sec [5,559..7,786] ~ overlap (+7.6%)
stringify array of objects 35,187 ops/sec [33,942..36,962] → 35,305 ops/sec [34,994..44,108] ~ overlap (+0.3%) 37,331 ops/sec [36,160..43,134] → 39,767 ops/sec [39,637..44,659] ~ overlap (+6.5%)
stringify array of arrays 11,267 ops/sec [10,739..11,336] → 10,817 ops/sec [9,953..12,540] ~ overlap (-4.0%) 10,989 ops/sec [10,942..11,114] → 11,284 ops/sec [11,147..11,384] 🟢 +2.7%
stringify with values needing escaping 29,184 ops/sec [28,684..34,699] → 28,696 ops/sec [28,175..30,909] ~ overlap (-1.7%) 33,698 ops/sec [31,017..37,292] → 30,519 ops/sec [30,130..30,783] 🔴 -9.4%
parse then stringify 7,864 ops/sec [6,617..7,920] → 6,564 ops/sec [6,417..6,644] ~ overlap (-16.5%) 6,605 ops/sec [6,563..6,674] → 6,445 ops/sec [6,326..6,520] 🔴 -2.4%
typed-arrays.js — Interp: 🟢 8, 🔴 1, 13 unch. · avg +23.1% · Bytecode: 🟢 4, 🔴 3, 15 unch. · avg -2.8%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
new Int32Array(0) 96,261 ops/sec [95,829..96,526] → 94,630 ops/sec [93,101..95,819] 🔴 -1.7% 125,187 ops/sec [111,084..168,142] → 124,864 ops/sec [114,237..131,399] ~ overlap (-0.3%)
new Int32Array(100) 90,375 ops/sec [89,320..91,745] → 91,830 ops/sec [91,132..109,580] ~ overlap (+1.6%) 108,462 ops/sec [103,034..119,224] → 108,321 ops/sec [107,326..109,252] ~ overlap (-0.1%)
new Int32Array(1000) 71,138 ops/sec [66,876..72,523] → 80,081 ops/sec [74,468..95,491] 🟢 +12.6% 82,358 ops/sec [80,569..102,331] → 82,295 ops/sec [79,606..85,176] ~ overlap (-0.1%)
new Float64Array(100) 88,696 ops/sec [84,610..102,141] → 93,189 ops/sec [91,362..96,055] ~ overlap (+5.1%) 104,688 ops/sec [103,330..105,887] → 110,611 ops/sec [100,652..133,351] ~ overlap (+5.7%)
Int32Array.from([...]) 1,592 ops/sec [1,489..1,829] → 1,533 ops/sec [1,525..1,582] ~ overlap (-3.7%) 1,470 ops/sec [1,458..1,608] → 1,775 ops/sec [1,511..2,011] ~ overlap (+20.8%)
Int32Array.of(1, 2, 3, 4, 5) 67,709 ops/sec [67,270..68,032] → 68,231 ops/sec [66,868..68,476] ~ overlap (+0.8%) 78,013 ops/sec [77,633..79,181] → 78,298 ops/sec [77,889..79,020] ~ overlap (+0.4%)
sequential write 100 elements 1,043 ops/sec [1,002..1,117] → 1,055 ops/sec [1,047..1,139] ~ overlap (+1.1%) 3,736 ops/sec [3,717..3,745] → 3,739 ops/sec [3,657..3,877] ~ overlap (+0.1%)
sequential read 100 elements 1,114 ops/sec [1,103..1,133] → 1,141 ops/sec [1,123..1,347] ~ overlap (+2.4%) 4,252 ops/sec [4,201..4,340] → 4,411 ops/sec [3,966..6,983] ~ overlap (+3.7%)
Float64Array write 100 elements 1,010 ops/sec [1,000..1,012] → 1,012 ops/sec [994..1,350] ~ overlap (+0.2%) 3,269 ops/sec [3,180..3,382] → 3,240 ops/sec [3,201..3,254] ~ overlap (-0.9%)
fill(42) 2,136 ops/sec [2,047..2,158] → 2,034 ops/sec [2,020..2,254] ~ overlap (-4.8%) 2,064 ops/sec [2,039..2,067] → 2,023 ops/sec [2,013..2,054] ~ overlap (-2.0%)
slice() 8,737 ops/sec [8,640..9,236] → 14,530 ops/sec [8,464..14,881] ~ overlap (+66.3%) 8,780 ops/sec [8,696..9,351] → 8,598 ops/sec [8,480..8,701] ~ overlap (-2.1%)
map(x => x * 2) 2,000 ops/sec [1,945..2,024] → 3,314 ops/sec [3,308..3,319] 🟢 +65.7% 2,067 ops/sec [2,049..2,103] → 2,110 ops/sec [2,104..2,124] 🟢 +2.1%
filter(x => x > 50) 2,193 ops/sec [2,043..2,373] → 3,545 ops/sec [3,507..3,550] 🟢 +61.7% 2,286 ops/sec [2,254..2,299] → 2,336 ops/sec [2,329..2,341] 🟢 +2.2%
reduce (sum) 2,154 ops/sec [2,101..3,576] → 3,604 ops/sec [3,586..3,622] 🟢 +67.3% 2,177 ops/sec [2,137..2,182] → 2,221 ops/sec [2,214..2,225] 🟢 +2.0%
sort() 13,383 ops/sec [13,140..13,517] → 22,801 ops/sec [22,659..22,993] 🟢 +70.4% 13,702 ops/sec [13,583..15,095] → 13,482 ops/sec [13,443..13,513] 🔴 -1.6%
indexOf() 29,314 ops/sec [28,914..33,336] → 49,410 ops/sec [49,250..49,862] 🟢 +68.6% 35,423 ops/sec [30,250..39,121] → 30,393 ops/sec [29,846..30,653] ~ overlap (-14.2%)
reverse() 15,622 ops/sec [15,474..15,688] → 25,749 ops/sec [14,597..25,970] ~ overlap (+64.8%) 15,511 ops/sec [15,158..16,515] → 16,133 ops/sec [15,165..17,257] ~ overlap (+4.0%)
create view over existing buffer 108,000 ops/sec [107,014..108,740] → 166,534 ops/sec [105,684..167,928] ~ overlap (+54.2%) 127,643 ops/sec [126,734..128,076] → 125,996 ops/sec [123,739..130,596] ~ overlap (-1.3%)
subarray() 68,653 ops/sec [67,787..70,668] → 69,304 ops/sec [68,437..74,884] ~ overlap (+0.9%) 76,412 ops/sec [75,650..89,890] → 76,247 ops/sec [75,562..76,973] ~ overlap (-0.2%)
set() from array 124,571 ops/sec [124,003..126,291] → 86,925 ops/sec [76,742..124,779] ~ overlap (-30.2%) 86,202 ops/sec [85,575..86,868] → 87,915 ops/sec [87,586..88,231] 🟢 +2.0%
for-of loop 2,491 ops/sec [2,488..2,507] → 2,552 ops/sec [2,544..2,566] 🟢 +2.4% 12,898 ops/sec [7,617..12,943] → 7,526 ops/sec [7,406..7,575] 🔴 -41.7%
spread into array 10,385 ops/sec [10,132..10,466] → 10,665 ops/sec [10,612..10,684] 🟢 +2.7% 38,565 ops/sec [38,444..38,764] → 23,418 ops/sec [22,957..25,114] 🔴 -39.3%
uint8array-encoding.js — Interp: 🟢 4, 14 unch. · avg +3.9% · Bytecode: 🟢 6, 🔴 1, 11 unch. · avg +19.6%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
short (5 bytes) 172,571 ops/sec [140,436..213,275] → 143,899 ops/sec [133,077..154,697] ~ overlap (-16.6%) 182,292 ops/sec [181,074..183,948] → 182,796 ops/sec [178,887..206,482] ~ overlap (+0.3%)
medium (450 bytes) 100,891 ops/sec [98,746..101,334] → 103,134 ops/sec [99,887..119,991] ~ overlap (+2.2%) 117,369 ops/sec [117,152..117,586] → 121,048 ops/sec [115,325..124,558] ~ overlap (+3.1%)
large (4096 bytes) 29,624 ops/sec [29,395..30,066] → 29,794 ops/sec [28,968..33,809] ~ overlap (+0.6%) 30,635 ops/sec [30,524..30,885] → 30,727 ops/sec [30,650..33,077] ~ overlap (+0.3%)
base64url alphabet 77,371 ops/sec [76,732..98,526] → 79,799 ops/sec [75,607..91,711] ~ overlap (+3.1%) 79,208 ops/sec [78,780..80,286] → 78,098 ops/sec [77,256..78,694] 🔴 -1.4%
omitPadding 98,731 ops/sec [95,340..106,226] → 101,131 ops/sec [95,651..101,539] ~ overlap (+2.4%) 108,642 ops/sec [107,391..115,350] → 116,644 ops/sec [105,144..173,993] ~ overlap (+7.4%)
short (8 chars) 175,905 ops/sec [112,417..177,945] → 115,026 ops/sec [114,380..115,717] ~ overlap (-34.6%) 130,729 ops/sec [130,475..133,511] → 215,886 ops/sec [215,011..216,202] 🟢 +65.1%
medium (600 chars) 61,006 ops/sec [60,641..62,897] → 62,337 ops/sec [61,595..65,285] ~ overlap (+2.2%) 66,309 ops/sec [65,334..66,395] → 114,728 ops/sec [114,157..115,244] 🟢 +73.0%
large (5464 chars) 13,136 ops/sec [13,091..13,681] → 13,120 ops/sec [12,854..13,320] ~ overlap (-0.1%) 13,261 ops/sec [13,039..13,659] → 23,881 ops/sec [23,619..24,080] 🟢 +80.1%
short (5 bytes) 147,460 ops/sec [146,370..149,802] → 146,725 ops/sec [145,705..149,583] ~ overlap (-0.5%) 193,467 ops/sec [192,219..202,821] → 327,043 ops/sec [326,067..329,051] 🟢 +69.0%
medium (450 bytes) 92,812 ops/sec [90,175..94,920] → 100,715 ops/sec [95,040..108,624] 🟢 +8.5% 175,839 ops/sec [175,367..176,011] → 176,288 ops/sec [175,136..177,298] ~ overlap (+0.3%)
large (4096 bytes) 21,907 ops/sec [21,175..22,040] → 21,762 ops/sec [21,592..21,986] ~ overlap (-0.7%) 37,583 ops/sec [37,388..37,677] → 38,097 ops/sec [36,795..38,332] ~ overlap (+1.4%)
short (10 chars) 187,478 ops/sec [120,500..189,478] → 124,230 ops/sec [120,371..133,237] ~ overlap (-33.7%) 233,183 ops/sec [231,515..233,636] → 239,199 ops/sec [144,333..241,357] ~ overlap (+2.6%)
medium (900 chars) 96,861 ops/sec [87,991..102,717] → 96,237 ops/sec [86,275..146,351] ~ overlap (-0.6%) 168,823 ops/sec [166,909..170,126] → 173,036 ops/sec [167,069..173,364] ~ overlap (+2.5%)
large (8192 chars) 49,236 ops/sec [48,533..49,555] → 49,509 ops/sec [49,165..49,706] ~ overlap (+0.6%) 50,572 ops/sec [49,883..51,004] → 52,209 ops/sec [50,459..52,887] ~ overlap (+3.2%)
setFromBase64 (450 bytes) 86,672 ops/sec [84,585..88,084] → 87,988 ops/sec [87,644..88,628] ~ overlap (+1.5%) 97,918 ops/sec [97,183..98,160] → 97,035 ops/sec [96,209..97,584] ~ overlap (-0.9%)
setFromHex (450 bytes) 21,215 ops/sec [21,100..21,561] → 35,908 ops/sec [35,488..36,257] 🟢 +69.3% 37,197 ops/sec [36,750..37,353] → 37,001 ops/sec [36,695..37,117] ~ overlap (-0.5%)
toBase64 → fromBase64 (450 bytes) 44,049 ops/sec [43,870..44,144] → 71,896 ops/sec [71,416..72,807] 🟢 +63.2% 53,183 ops/sec [49,365..53,434] → 76,592 ops/sec [76,060..77,602] 🟢 +44.0%
toHex → fromHex (450 bytes) 84,070 ops/sec [51,487..85,117] → 87,513 ops/sec [86,335..87,965] 🟢 +4.1% 94,411 ops/sec [94,043..94,706] → 97,403 ops/sec [96,731..98,676] 🟢 +3.2%
weak-collections.js — Interp: 🔴 8, 7 unch. · avg -26.3% · Bytecode: 🟢 5, 🔴 1, 9 unch. · avg +18.2%
Benchmark Interpreted (main → PR) Δ Bytecode (main → PR) Δ
constructor from 50 entries 9,908 ops/sec [9,509..10,124] → 9,603 ops/sec [9,364..9,836] ~ overlap (-3.1%) 9,639 ops/sec [9,465..10,136] → 9,598 ops/sec [9,227..9,736] ~ overlap (-0.4%)
set 50 object keys 4,721 ops/sec [4,695..4,754] → 2,896 ops/sec [2,853..3,423] 🔴 -38.7% 3,787 ops/sec [3,735..3,807] → 4,066 ops/sec [3,799..4,294] ~ overlap (+7.4%)
get lookups (50 entries) 70,287 ops/sec [70,119..70,508] → 42,940 ops/sec [42,707..43,277] 🔴 -38.9% 78,023 ops/sec [77,014..79,343] → 80,821 ops/sec [80,499..81,178] 🟢 +3.6%
has checks (50 entries) 89,500 ops/sec [88,611..89,852] → 56,987 ops/sec [56,197..58,827] 🔴 -36.3% 99,351 ops/sec [93,800..101,530] → 97,999 ops/sec [96,951..98,843] ~ overlap (-1.4%)
delete entries 4,366 ops/sec [4,300..4,410] → 2,725 ops/sec [2,705..2,740] 🔴 -37.6% 3,649 ops/sec [3,594..3,667] → 3,636 ops/sec [3,634..3,637] ~ overlap (-0.4%)
non-registered symbol keys 10,618 ops/sec [10,554..10,682] → 6,687 ops/sec [6,654..6,713] 🔴 -37.0% 8,880 ops/sec [8,812..9,062] → 8,782 ops/sec [8,759..8,791] 🔴 -1.1%
getOrInsert 4,399 ops/sec [4,378..4,442] → 2,894 ops/sec [2,792..2,940] 🔴 -34.2% 3,438 ops/sec [3,398..3,444] → 3,518 ops/sec [3,502..3,535] 🟢 +2.3%
getOrInsertComputed 2,545 ops/sec [2,532..2,574] → 1,640 ops/sec [1,601..2,535] ~ overlap (-35.6%) 1,792 ops/sec [1,777..1,815] → 1,829 ops/sec [1,809..1,837] ~ overlap (+2.1%)
forced gc live-key retention 73 ops/sec [70..94] → 65 ops/sec [56..75] ~ overlap (-10.5%) 71 ops/sec [47..84] → 96 ops/sec [59..120] ~ overlap (+35.8%)
constructor from 50 values 18,686 ops/sec [18,141..19,426] → 11,979 ops/sec [11,794..12,064] 🔴 -35.9% 12,203 ops/sec [12,117..12,659] → 11,920 ops/sec [11,793..19,381] ~ overlap (-2.3%)
add 50 object values 3,055 ops/sec [2,993..3,063] → 4,834 ops/sec [3,023..4,946] ~ overlap (+58.2%) 4,128 ops/sec [4,072..4,263] → 4,535 ops/sec [3,942..5,244] ~ overlap (+9.8%)
has checks (50 values) 90,141 ops/sec [57,788..90,975] → 61,390 ops/sec [55,841..91,275] ~ overlap (-31.9%) 98,280 ops/sec [97,288..99,589] → 101,059 ops/sec [100,911..101,352] 🟢 +2.8%
delete values 14,637 ops/sec [14,520..14,699] → 9,701 ops/sec [8,837..15,104] ~ overlap (-33.7%) 10,666 ops/sec [10,456..10,913] → 11,097 ops/sec [10,485..17,182] ~ overlap (+4.0%)
non-registered symbol values 9,717 ops/sec [6,694..11,277] → 6,783 ops/sec [6,715..10,865] ~ overlap (-30.2%) 9,430 ops/sec [9,349..10,171] → 15,115 ops/sec [15,027..15,178] 🟢 +60.3%
forced gc pruning smoke 149 ops/sec [137..151] → 75 ops/sec [68..91] 🔴 -49.6% 64 ops/sec [53..73] → 160 ops/sec [160..161] 🟢 +149.7%

Deterministic profile diff

Deterministic profile diff: no significant changes.

Measured on ubuntu-latest x64. Each PR run also builds the main base and benchmarks it back-to-back on the same runner after a warm-up discard, so the ranges compare two runs measured under the same conditions; overlapping min/max ranges are treated as unchanged noise. Percentage deltas are secondary context. See docs/adr/0076.

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

test262 Conformance

Category Run Passed Δ Pass Failed Pass-rate Δ Rate
built-ins 23,643 22,619 +11 1,024 95.7% ±0pp
harness 116 116 ±0 0 100.0% ±0pp
intl402 3,341 3,334 ±0 7 99.8% ±0pp
language 23,711 23,711 ±0 0 100.0% ±0pp
staging 1,482 1,111 +2 371 75.0% +0.1pp
total 52,293 50,891 +13 1,402 97.3% ±0pp

Areas closest to 100%

Area Pass rate Δ vs main Passing
built-ins/TypedArray 99.9% ±0pp 1,445 / 1,446
intl402/Temporal 99.9% ±0pp 2,026 / 2,029
built-ins/Object 99.7% ±0pp 3,401 / 3,411
Per-test deltas (+13 / -0)

Newly passing (13):

  • built-ins/Map/prototype/forEach/deleted-values-during-foreach.js
  • built-ins/Map/prototype/forEach/iterates-values-added-after-foreach-begins.js
  • built-ins/Map/prototype/forEach/iterates-values-deleted-then-readded.js
  • built-ins/Number/prototype/toExponential/undefined-fractiondigits.js
  • built-ins/Set/prototype/forEach/iterates-values-added-after-foreach-begins.js
  • built-ins/Set/prototype/forEach/iterates-values-revisits-after-delete-re-add.js
  • built-ins/Set/prototype/intersection/converts-negative-zero.js
  • built-ins/Set/prototype/isDisjointFrom/set-like-class-mutation.js
  • built-ins/Set/prototype/isSubsetOf/set-like-class-mutation.js
  • built-ins/Set/prototype/symmetricDifference/converts-negative-zero.js
  • built-ins/Set/prototype/union/converts-negative-zero.js
  • staging/set-is-subset-table-transition.js
  • staging/set-methods/set-intersection-other-is-map.js

Steady-state failures are non-blocking; regressions vs the cached main baseline (lower total pass count, or any PASS → non-PASS transition) fail the conformance gate. Measured on ubuntu-latest x64, bytecode mode. Areas grouped by the first two test262 path components; minimum 25 attempted tests, areas already at 100% excluded. Δ vs main compares against the most recent cached main baseline.

@frostney frostney marked this pull request as ready for review June 27, 2026 19:10
@coderabbitai coderabbitai Bot added internal Refactoring, CI, tooling, cleanup performance Performance improvement labels Jun 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/units/Goccia.Values.SetValue.pas (1)

402-424: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle omitted Set arguments as undefined. Lines 402, 423, and 442 skip the operation when no argument is passed, but Set.prototype.has, add, and delete should process an undefined value instead. That makes new Set().add() a no-op and breaks the spec for omitted arguments.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/units/Goccia.Values.SetValue.pas` around lines 402 - 424, Update the
Set prototype methods so omitted arguments are treated as undefined instead of
being skipped. In TGocciaSetValue.SetAdd, TGocciaSetValue.SetHas, and
TGocciaSetValue.SetDelete, remove the length checks that bypass the operation
when AArgs is empty and instead read AArgs.GetElement(0) as undefined when no
argument is provided. Ensure the SameValueZero/ContainsValue logic and the
add/delete paths continue to run with that undefined value so the behavior
matches the spec for Set.prototype.add, Set.prototype.has, and
Set.prototype.delete.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/units/Goccia.Builtins.Globals.pas`:
- Around line 844-848: The Map/Set clone loop in StructuredCloneValue can be
invalidated by user code during accessor reads, so the active cursor must be
retained across the walk. Wrap the AMap.NextEntry and ASet.NextEntry iterations
with RetainIterator/ReleaseIterator so compaction cannot renumber entries
mid-clone, and keep the fix in the StructuredCloneValue code paths that populate
Result from AMap and ASet.

In `@source/units/Goccia.Evaluator.Comparison.pas`:
- Around line 161-166: The deep comparison loops in IsDeepEqualInternal need to
retain both collections’ cursor state and verify the expected-side advance
succeeds before comparing values. Update the Set and Map branches that use
TGocciaSetValue.NextItem and TGocciaMapValue.NextEntry so both cursors advance
in lockstep and the comparison only proceeds when the expected cursor call
returns true, avoiding use of stale RightValue/RightKey/RightValue after
recursive property reads mutate either store.

In `@source/units/Goccia.REPL.Formatter.pas`:
- Around line 88-100: The Map/Set cursor traversal in FormatREPLValue needs to
keep the underlying store alive while iterating, because recursive formatting
can trigger object/stringify reads that mutate the collection and invalidate
Cursor. Update the Map/Set formatting loops that use NextEntry in the REPL
formatter to retain the collection/store for the full traversal and release it
afterward, so compaction cannot occur mid-iteration; apply the same fix in both
affected formatting branches.

In `@source/units/Goccia.Values.Iterator.Concrete.pas`:
- Around line 52-57: The iterator classes are missing destruction-time cleanup,
so abandoned objects can leave retained stores unreleased and keep
ActiveIterators elevated. Add destructors to the affected iterator types and
have them call the existing idempotent ReleaseSource helper, alongside the
current Close/exhaustion paths, so cleanup still happens when an iterator object
is destroyed. Use the concrete iterator classes around DirectNext, AdvanceNext,
Close, and ReleaseSource to locate the affected implementations.

In `@source/units/Goccia.Values.MapValue.pas`:
- Line 306: The argument-length guard in the MapValue methods is preventing
valid undefined keys/values from being processed, which breaks m.get(), m.has(),
m.delete(), and m.set(). Update the relevant logic in
TGoccia.Values.MapValue.pas around the MapValue handlers to read arguments
directly from TGocciaArgumentsCollection.GetElement without checking
AArgs.Length first, since GetElement already yields undefined for out-of-range
indexes. Keep the existing TryGetValue-based flow in the affected methods, but
remove the premature length-based blocking so undefined is handled consistently.

In `@source/units/Goccia.Values.OrderedValueMap.pas`:
- Around line 46-54: Split the shared insertion path in OrderedValueMap so Map
and Set use separate helpers: keep SetEntry for Map-style key/value storage, and
add a dedicated Set insert method in the store that canonicalizes the key and
also stores the canonicalized value. Update the Set caller(s) to use this new
helper so Set.add(-0) preserves the +0 invariant through
values()/entries()/forEach without relying on callers to duplicate
canonicalization. Locate the change around CanonicalizeKey, SetEntry, and the
Set-backed insert path.

In `@source/units/Goccia.Values.SetValue.pas`:
- Around line 726-745: The set difference logic in Goccia.Values.SetValue.pas is
iterating ThisSet directly while SetRecordHas may execute user code, which can
mutate the set during traversal. Update the difference path that uses
ThisSet.NextItem and ResultSet.AddItem to iterate over a stable snapshot taken
before the callback runs, so only the pre-callback contents are considered. Keep
the existing RetainIterator/ReleaseIterator protection, but switch the source of
iteration in this branch away from ThisSet itself.

---

Outside diff comments:
In `@source/units/Goccia.Values.SetValue.pas`:
- Around line 402-424: Update the Set prototype methods so omitted arguments are
treated as undefined instead of being skipped. In TGocciaSetValue.SetAdd,
TGocciaSetValue.SetHas, and TGocciaSetValue.SetDelete, remove the length checks
that bypass the operation when AArgs is empty and instead read
AArgs.GetElement(0) as undefined when no argument is provided. Ensure the
SameValueZero/ContainsValue logic and the add/delete paths continue to run with
that undefined value so the behavior matches the spec for Set.prototype.add,
Set.prototype.has, and Set.prototype.delete.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a1301a0e-9c23-4cdf-b36b-80ba53dfdacf

📥 Commits

Reviewing files that changed from the base of the PR and between 47c7a04 and 955fa12.

📒 Files selected for processing (18)
  • docs/adr/0077-samevaluezero-ordered-collections.md
  • docs/adr/README.md
  • source/shared/OrderedMap.pas
  • source/units/Goccia.Builtins.GlobalMap.pas
  • source/units/Goccia.Builtins.Globals.pas
  • source/units/Goccia.Evaluator.Comparison.pas
  • source/units/Goccia.REPL.Formatter.pas
  • source/units/Goccia.Values.Iterator.Concrete.pas
  • source/units/Goccia.Values.MapValue.pas
  • source/units/Goccia.Values.OrderedValueMap.Test.pas
  • source/units/Goccia.Values.OrderedValueMap.pas
  • source/units/Goccia.Values.SetValue.pas
  • tests/built-ins/Map/prototype/entries.js
  • tests/built-ins/Map/prototype/forEach.js
  • tests/built-ins/Map/prototype/set.js
  • tests/built-ins/Set/prototype/add.js
  • tests/built-ins/Set/prototype/forEach.js
  • tests/built-ins/Set/prototype/values.js

Comment thread source/units/Goccia.Builtins.Globals.pas Outdated
Comment thread source/units/Goccia.Evaluator.Comparison.pas Outdated
Comment thread source/units/Goccia.REPL.Formatter.pas Outdated
Comment thread source/units/Goccia.Values.Iterator.Concrete.pas
Comment thread source/units/Goccia.Values.MapValue.pas Outdated
Comment thread source/units/Goccia.Values.OrderedValueMap.pas
Comment thread source/units/Goccia.Values.SetValue.pas
frostney and others added 2 commits June 27, 2026 20:26
Addresses review findings on the SameValueZero Map/Set store:

- Retain the source store across the structuredClone, deep-equal, and REPL
  cursor walks: those recurse into user getters that can mutate the
  collection mid-walk, which could otherwise let compaction renumber entries
  and invalidate the cursor. Deep-equal now also confirms the expected-side
  cursor advances before comparing.
- Treat an omitted Map/Set argument as the `undefined` key/value instead of a
  no-op: Map get/set/has/delete and Set has/add/delete read the argument
  directly (GetElement already yields undefined for out-of-range), so
  `m.set("k")`, `m.get()`, and `s.add()` behave per spec.
- Move the Set canonicalization invariant into the store via AddSetMember, so
  the element is stored as both key and value without callers pre-canonicalizing.
- Make Set difference/intersection/isSubsetOf/isDisjointFrom iterate a snapshot
  taken before any set-like `has` callback runs (ES2026 §24.2.4): elements
  appended during a mutating callback are no longer visited; the live ones
  re-check membership so elements deleted during a callback are skipped.

Regression tests added for the argument handling and the mutating-callback
set operations, plus a Pascal test for AddSetMember.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot added bug Something isn't working spec compliance Mismatch against official JavaScript/TypeScript specification and removed performance Performance improvement internal Refactoring, CI, tooling, cleanup labels Jun 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/units/Goccia.Values.SetValue.pas`:
- Around line 119-133: SnapshotSetItems currently captures only TGocciaValue
data, so membership checks later in intersection, isSubsetOf, and isDisjointFrom
can mistake a delete-then-readd item for the original snapshot entry. Update
SnapshotSetItems and the related snapshot-using branches to preserve a stable
entry identity or physical slot reference from TGocciaSetValue rather than raw
values, and then compare against that identity when iterating so re-added values
are not treated as still-live snapshot members.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b55dc2f0-4c10-42b0-b6d3-ed85f55645b6

📥 Commits

Reviewing files that changed from the base of the PR and between 955fa12 and af71aa2.

📒 Files selected for processing (11)
  • source/units/Goccia.Builtins.Globals.pas
  • source/units/Goccia.Evaluator.Comparison.pas
  • source/units/Goccia.REPL.Formatter.pas
  • source/units/Goccia.Values.MapValue.pas
  • source/units/Goccia.Values.OrderedValueMap.Test.pas
  • source/units/Goccia.Values.OrderedValueMap.pas
  • source/units/Goccia.Values.SetValue.pas
  • tests/built-ins/Map/prototype/set.js
  • tests/built-ins/Set/prototype/add.js
  • tests/built-ins/Set/prototype/difference.js
  • tests/built-ins/Set/prototype/intersection.js
🚧 Files skipped from review as they are similar to previous changes (7)
  • source/units/Goccia.Builtins.Globals.pas
  • source/units/Goccia.REPL.Formatter.pas
  • source/units/Goccia.Values.OrderedValueMap.pas
  • tests/built-ins/Map/prototype/set.js
  • source/units/Goccia.Evaluator.Comparison.pas
  • source/units/Goccia.Values.OrderedValueMap.Test.pas
  • source/units/Goccia.Values.MapValue.pas

Comment thread source/units/Goccia.Values.SetValue.pas
…nded iteration

The earlier snapshot+ContainsValue recheck for intersection/isSubsetOf/
isDisjointFrom could not distinguish a still-live original member from a
delete-then-readd of the same value during a mutating `has` callback —
membership flips back to true, so the re-added entry was wrongly visited.

Replace it with bounded iteration over the original physical slots: capture
the store's slot count before the callbacks (EntrySlotCount), retain the store
so compaction cannot renumber slots, and walk with NextEntryBounded. Tombstones
below the bound are skipped, appended members land at slots >= the bound and are
ignored, and a delete-then-readd lands past the bound — matching ES2026
§24.2.4 exactly. `difference` keeps the value snapshot because the spec builds
its result from a copy of O.[[SetData]].

Adds EntrySlotCount/NextEntryBounded to the shared TOrderedMap, a Pascal test
for them, and a JS regression test for the delete-then-readd case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot added internal Refactoring, CI, tooling, cleanup performance Performance improvement and removed bug Something isn't working labels Jun 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/units/Goccia.Values.SetValue.pas (1)

777-781: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Root the copied members before invoking other.has.

Snapshot is only a Pascal dynamic array, but SetRecordHas runs user code. If a callback deletes an unprocessed object from ThisSet and triggers GC, the later Snapshot[I] can become a stale value. Since ResultSet is already temp-rooted, populate it with the snapshot first, then remove matches.

🛡️ Proposed fix
       Snapshot := SnapshotSetItems(ThisSet);
       for I := 0 to High(Snapshot) do
-        // Step 6a: If e is not in other, keep it.
-        if not SetRecordHas(OtherRecord, Snapshot[I]) then
-          ResultSet.AddItem(Snapshot[I]);
+        ResultSet.AddItem(Snapshot[I]);
+      for I := 0 to High(Snapshot) do
+        // Step 6a: If e is in other, remove it from the copied data.
+        if SetRecordHas(OtherRecord, Snapshot[I]) then
+          RemoveSetItem(ResultSet, Snapshot[I]);

Based on learnings, GC-rooting issues should only be flagged around real safe points such as user-code/runtime transitions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/units/Goccia.Values.SetValue.pas` around lines 777 - 781, The loop in
SetValue uses SnapshotSetItems(ThisSet) and then calls SetRecordHas, which can
run user code before the snapshot member is copied into a rooted location. To
avoid stale Snapshot[I] values if GC runs during SetRecordHas, change the logic
in SetValue so the snapshot members are first added to the already temp-rooted
ResultSet, and only then remove any items that are present in OtherRecord. Keep
the fix localized around SnapshotSetItems, SetRecordHas, and ResultSet.AddItem.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@source/units/Goccia.Values.SetValue.pas`:
- Around line 777-781: The loop in SetValue uses SnapshotSetItems(ThisSet) and
then calls SetRecordHas, which can run user code before the snapshot member is
copied into a rooted location. To avoid stale Snapshot[I] values if GC runs
during SetRecordHas, change the logic in SetValue so the snapshot members are
first added to the already temp-rooted ResultSet, and only then remove any items
that are present in OtherRecord. Keep the fix localized around SnapshotSetItems,
SetRecordHas, and ResultSet.AddItem.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a2160ed4-4a61-4d56-ae0b-c5a78b6b6df1

📥 Commits

Reviewing files that changed from the base of the PR and between af71aa2 and e738827.

📒 Files selected for processing (4)
  • source/shared/OrderedMap.pas
  • source/units/Goccia.Values.OrderedValueMap.Test.pas
  • source/units/Goccia.Values.SetValue.pas
  • tests/built-ins/Set/prototype/intersection.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/units/Goccia.Values.OrderedValueMap.Test.pas

…allbacks

Set.difference's small branch called the set-like `has` (user code) on each
snapshot member before copying it into the temp-rooted ResultSet. SnapshotSetItems
returns a plain array, which is not a GC root, so a `has` callback that deleted a
not-yet-processed member from the receiver and triggered a GC could free that
member while it was still referenced only by the snapshot, dangling a later
Snapshot[I].

Copy the snapshot into the temp-rooted ResultSet first, then remove the members
present in other. The members stay reachable via ResultSet for the duration of
the callbacks, and this matches the spec more literally (resultSetData is a copy
of O.[[SetData]] that has matching elements removed, §24.2.4.5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot added the new feature New feature or request label Jun 28, 2026
@frostney frostney merged commit f1590b7 into main Jun 28, 2026
15 checks passed
@frostney frostney deleted the claude/affectionate-mahavira-eb0c9a branch June 28, 2026 06:42
frostney added a commit that referenced this pull request Jun 28, 2026
0077 was taken by #890 (SameValueZero-keyed ordered collections), which merged
to main first. Renumber the thread-local cleanup registry ADR to 0078.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Refactoring, CI, tooling, cleanup new feature New feature or request performance Performance improvement spec compliance Mismatch against official JavaScript/TypeScript specification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: back strong Map/Set with a SameValueZero-keyed insertion-ordered hash

1 participant