Skip to content

fix(engine): release managed threadvars on worker-thread exit#891

Open
frostney wants to merge 6 commits into
mainfrom
claude/zealous-lederberg-4a6dcb
Open

fix(engine): release managed threadvars on worker-thread exit#891
frostney wants to merge 6 commits into
mainfrom
claude/zealous-lederberg-4a6dcb

Conversation

@frostney

@frostney frostney commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • FPC does not auto-finalize managed threadvars at thread exit. Every builtin and value type caches its member-definition arrays (FStaticMembers / FPrototypeMembers : TArray<TGocciaMemberDefinition>, whose records hold strings) in a threadvar, and two units hold input memos (RegExp decode memo perf(regexp): memoize the decoded subject across regex VM calls #805, is-ASCII memo perf(string): ASCII byte-op fast paths for the UTF-16 string helpers #806). These leaked on each worker-thread exit (and, for the member arrays, were never released on the main thread either).
  • New Goccia.ThreadCleanupRegistry: each leaking unit registers a small ClearThreadvarMembers proc once in its initialization; ShutdownThreadRuntime drains the registry on every worker thread, and the registry's finalization drains it on the main thread — so each managed threadvar is released on both teardown paths, matching the issue's expected behaviour and the existing DisposableStack pattern.
  • Scope is wider than the issue text estimated: the identical root cause affects ~64 units (every builtin's FStaticMembers plus ~40 Goccia.Values.* FPrototypeMembers, e.g. Map/Set/Promise/all Intl*/all Temporal*/TypedArray/Iterator), not just the ~20 enumerated. All 64 are fixed (confirmed via #885 grilling).
  • The two memos already finalize on the main thread, so they only needed the worker path: ShutdownThreadRuntime calls their exported ClearRegExpInputMemo / ClearAsciiMemo explicitly (same shape as the 5 caches already routed there).
  • Also closes two pre-existing main-thread finalization gaps surfaced while auditing those caches (the issue listed them as "already correct"): Goccia.ImportMeta had no finalization; Goccia.Temporal.TimeZone's finalization did not call ClearTimeZoneCache.
  • Non-goals / constraints: threadvar lifetime only — no behaviour, semantics, or performance change. Object-reference threadvars (e.g. the symbol registry) are out of scope (FPC never auto-finalizes object refs; separate lifetime concern). Per-unit clear procs are unavoidable: @threadvar resolves to the registering thread's copy, so a generic pointer-based clear can't release another thread's copy. Decision recorded in ADR 0077.

Closes #885

Deferred follow-ups (tracked as separate issues)

Note on the leak gate

The grilled plan called for a "permanent heaptrc -gh zero-leak CI job". build.pas has no -gh mode, and a full-engine worker harness under heaptrc does not report a literal zero (it has legitimate process-lifetime allocations), so a -gh "0 unfreed" assertion would be unreliable. Instead the gate is a deterministic Pascal heap-reclaim test (Goccia.ThreadCleanupLeak.Test) that CI's existing Pascal-test job runs automatically: it populates the member-definition threadvars, drains the registry, and asserts the live heap drops by a meaningful amount. It is cross-platform robust and verified to fail when the drain is disabled and pass when enabled — directly locking in the fix. Happy to add a literal -gh job as a follow-up if preferred.

Testing

  • Verified no regressions in end-to-end JavaScript tests — GocciaTestRunner tests and --mode=bytecode both 10958/10958 (100%). (The fix has no JS-observable behaviour; it is internal threadvar lifetime, so it is confirmed via the Pascal tests below.)
  • Updated documentation — added ADR 0077 + index entry.
  • Verified in native Pascal tests — new registry mechanism + per-worker-drain wiring tests in Goccia.Threading.Test (12/12), and a new Goccia.ThreadCleanupLeak.Test heap-reclaim gate (verified it fails without the fix). All 26 Pascal suites pass. ./format.pas --check clean.
  • Optional: benchmarks — N/A (lifetime-only change, no hot-path impact).

frostney and others added 2 commits June 27, 2026 20:31
FPC does not auto-finalize managed threadvars at thread exit, so every
builtin and value type's cached member-definition arrays
(FStaticMembers / FPrototypeMembers) and the #805/#806 input memos
leaked on each worker-thread exit.

Add Goccia.ThreadCleanupRegistry: each leaking unit registers a small
ClearThreadvarMembers proc once in its initialization; ShutdownThreadRuntime
drains the registry on every worker thread and the registry's finalization
drains it on the main thread, so each managed threadvar is released on both
teardown paths. The two memos keep their finalization and are cleared on the
worker path via explicit ShutdownThreadRuntime calls. Also close two
pre-existing main-thread finalization gaps (ImportMeta had none; TimeZone's
did not call ClearTimeZoneCache).

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:34am

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: e2dd0ebb-7a36-45c8-a17c-ff1dc1e092db

📥 Commits

Reviewing files that changed from the base of the PR and between d162779 and 09c8caf.

📒 Files selected for processing (2)
  • docs/adr/0077-thread-local-cleanup-registry.md
  • source/units/Goccia.Threading.Test.pas
✅ Files skipped from review due to trivial changes (1)
  • docs/adr/0077-thread-local-cleanup-registry.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/units/Goccia.Threading.Test.pas

📝 Walkthrough

Walkthrough

Introduces Goccia.ThreadCleanupRegistry for draining registered threadvar cleanup callbacks, wires worker-thread shutdown to call it, adds main-thread finalization cleanup for two caches, registers cleanup hooks across builtin and value units, and adds regression tests plus ADR documentation.

Changes

Thread-local cleanup registry

Layer / File(s) Summary
Registry API and drain
source/units/Goccia.ThreadCleanupRegistry.pas
New unit defines the cleanup callback type, registration and drain procedures, and drains registered callbacks during unit finalization.
ShutdownThreadRuntime cleanup
source/shared/TextSemantics.pas, source/units/Goccia.RegExp.VM.pas, source/units/Goccia.Threading.pas
ClearAsciiMemo and ClearRegExpInputMemo are exposed for external shutdown use. ShutdownThreadRuntime now clears both memos and drains the registry on worker-thread exit.
Main-thread finalization calls
source/units/Goccia.ImportMeta.pas, source/units/Goccia.Temporal.TimeZone.pas
Both units add finalization-time calls to clear their threadvar-backed caches on process shutdown.
Builtin and value cleanup registrations
source/units/Goccia.Builtins.*.pas, source/units/Goccia.Values.*.pas
Many builtin and value units add the cleanup registry dependency, define ClearThreadvarMembers, and register it during initialization to clear thread-local member caches.
Tests and ADR docs
source/units/Goccia.ThreadCleanupLeak.Test.pas, source/units/Goccia.Threading.Test.pas, docs/adr/0077-thread-local-cleanup-registry.md, docs/adr/README.md
Adds heap-reclaim and threading tests, plus ADR documentation and README linkage for the cleanup registry change.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • #885: This PR directly implements centralized worker-thread cleanup for managed threadvars and the related leak regression test.

Possibly related PRs

Suggested labels

documentation, internal

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing managed threadvar cleanup on worker-thread exit.
Description check ✅ Passed The description includes a summary, testing, related issues, and implementation constraints, matching the template well.
Linked Issues check ✅ Passed The PR implements the requested worker-thread cleanup path for managed threadvars and routes the listed memo caches through it.
Out of Scope Changes check ✅ Passed The added docs, tests, registry, and finalization fixes all support the threadvar cleanup objective and appear in scope.
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.

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

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Suite Timing

Test Runner (interpreted: 10,962 passed; bytecode: 10,962 passed)
Metric Interpreted Bytecode
Total 10962 10962
Passed 10962 ✅ 10962 ✅
Workers 4 4
Test Duration 19.03s 20.84s
Lex (cumulative) 512.2ms 623.5ms
Parse (cumulative) 364.3ms 431.1ms
Compile (cumulative) 899.0ms
Execute (cumulative) 56.2ms 47.04s
Engine Total (cumulative) 932.7ms 48.99s
Lex (avg/worker) 128.1ms 155.9ms
Parse (avg/worker) 91.1ms 107.8ms
Compile (avg/worker) 224.7ms
Execute (avg/worker) 14.1ms 11.76s
Engine Total (avg/worker) 233.2ms 12.25s

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 318.20 MiB 299.08 MiB
GC Allocated During Run 529.02 MiB 473.19 MiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 195 195
GC Collected Objects 2,597,597 2,167,876
Heap Start Allocated 182.0 KiB 182.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.82min 2.75min

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 94.69 MiB 67.56 MiB
GC Allocated During Run 15.88 GiB 9.90 GiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 3,976 3,432
GC Collected Objects 248,976,315 223,859,145
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: 🟢 31 improved · 🔴 43 regressed · 362 unchanged · avg +0.9%
Bytecode: 🟢 37 improved · 🔴 53 regressed · 346 unchanged · avg -0.1%

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

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

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,609 +1 1,034 95.6% ±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,109 ±0 373 74.8% ±0pp
total 52,293 50,879 +1 1,414 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 (+1 / -0)

Newly passing (1):

  • built-ins/Number/prototype/toExponential/undefined-fractiondigits.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 28, 2026 00:03
@coderabbitai coderabbitai Bot added bug Something isn't working internal Refactoring, CI, tooling, cleanup labels Jun 28, 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: 3

🤖 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 `@docs/adr/0077-thread-local-cleanup-registry.md`:
- Line 14: Update the ADR’s test description so it matches the implemented
coverage: `Goccia.ThreadCleanupLeak.Test` only measures a one-shot heap drop
around `RunThreadvarCleanups`, while `Goccia.Threading.Test` covers per-worker
draining separately. Replace the current “repeated worker cycles”/“per-cycle
non-growth” wording with language that reflects these two tests and their actual
assertions, without changing the broader ADR narrative.

In `@source/units/Goccia.Threading.Test.pas`:
- Around line 417-452: The threading tests are registering cleanup callbacks
inside the test bodies, which bypasses the intended startup-only contract of
RegisterThreadvarCleanup in Goccia.ThreadCleanupRegistry. Move the sentinel
registrations for SentinelCleanup, SentinelCleanupSecond, and
SentinelWorkerCleanup into a test-only unit initialization section so they are
set up once at startup, then keep
TTestThreading.TestThreadCleanupRegistryRunsRegistered and
TTestThreading.TestShutdownThreadRuntimeDrainsRegistryPerWorker focused on
resetting counters, draining with RunThreadvarCleanups, and asserting the
expected counts.

In `@source/units/Goccia.Values.ArrayValue.pas`:
- Around line 158-161: ClearThreadvarMembers currently only resets
FPrototypeMembers, but InitializePrototype also pins FPrototypeMethodHost, so
the teardown path still leaves the prototype host alive after thread exit.
Update ClearThreadvarMembers to also unpin and nil out FPrototypeMethodHost, or
refactor the prototype host to a true process-wide singleton; apply the same fix
pattern in InitializePrototype/ClearThreadvarMembers for
Goccia.Values.BigIntValue and Goccia.Values.BooleanObjectValue as well.
🪄 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: f9d3c615-c896-4289-8e08-44042d70db67

📥 Commits

Reviewing files that changed from the base of the PR and between 3d6f4e2 and d162779.

📒 Files selected for processing (74)
  • docs/adr/0077-thread-local-cleanup-registry.md
  • docs/adr/README.md
  • source/shared/TextSemantics.pas
  • source/units/Goccia.Builtins.CSV.pas
  • source/units/Goccia.Builtins.Console.pas
  • source/units/Goccia.Builtins.GlobalArray.pas
  • source/units/Goccia.Builtins.GlobalArrayBuffer.pas
  • source/units/Goccia.Builtins.GlobalBigInt.pas
  • source/units/Goccia.Builtins.GlobalFFI.pas
  • source/units/Goccia.Builtins.GlobalNumber.pas
  • source/units/Goccia.Builtins.GlobalObject.pas
  • source/units/Goccia.Builtins.GlobalPromise.pas
  • source/units/Goccia.Builtins.GlobalReflect.pas
  • source/units/Goccia.Builtins.GlobalRegExp.pas
  • source/units/Goccia.Builtins.GlobalString.pas
  • source/units/Goccia.Builtins.GlobalSymbol.pas
  • source/units/Goccia.Builtins.GlobalURL.pas
  • source/units/Goccia.Builtins.JSON.pas
  • source/units/Goccia.Builtins.JSON5.pas
  • source/units/Goccia.Builtins.JSONL.pas
  • source/units/Goccia.Builtins.Math.pas
  • source/units/Goccia.Builtins.TOML.pas
  • source/units/Goccia.Builtins.TSV.pas
  • source/units/Goccia.Builtins.YAML.pas
  • source/units/Goccia.ImportMeta.pas
  • source/units/Goccia.RegExp.VM.pas
  • source/units/Goccia.Temporal.TimeZone.pas
  • source/units/Goccia.ThreadCleanupLeak.Test.pas
  • source/units/Goccia.ThreadCleanupRegistry.pas
  • source/units/Goccia.Threading.Test.pas
  • source/units/Goccia.Threading.pas
  • source/units/Goccia.Values.ArrayBufferValue.pas
  • source/units/Goccia.Values.ArrayValue.pas
  • source/units/Goccia.Values.BigIntValue.pas
  • source/units/Goccia.Values.BooleanObjectValue.pas
  • source/units/Goccia.Values.FFILibrary.pas
  • source/units/Goccia.Values.FFIPointer.pas
  • source/units/Goccia.Values.FinalizationRegistryValue.pas
  • source/units/Goccia.Values.HeadersValue.pas
  • source/units/Goccia.Values.IntlCollator.pas
  • source/units/Goccia.Values.IntlDateTimeFormat.pas
  • source/units/Goccia.Values.IntlDisplayNames.pas
  • source/units/Goccia.Values.IntlDurationFormat.pas
  • source/units/Goccia.Values.IntlListFormat.pas
  • source/units/Goccia.Values.IntlLocale.pas
  • source/units/Goccia.Values.IntlNumberFormat.pas
  • source/units/Goccia.Values.IntlPluralRules.pas
  • source/units/Goccia.Values.IntlRelativeTimeFormat.pas
  • source/units/Goccia.Values.IntlSegmenter.pas
  • source/units/Goccia.Values.IteratorValue.pas
  • source/units/Goccia.Values.MapValue.pas
  • source/units/Goccia.Values.NumberObjectValue.pas
  • source/units/Goccia.Values.ObjectValue.pas
  • source/units/Goccia.Values.PromiseValue.pas
  • source/units/Goccia.Values.ResponseValue.pas
  • source/units/Goccia.Values.SetValue.pas
  • source/units/Goccia.Values.SharedArrayBufferValue.pas
  • source/units/Goccia.Values.StringObjectValue.pas
  • source/units/Goccia.Values.SymbolValue.pas
  • source/units/Goccia.Values.TemporalDuration.pas
  • source/units/Goccia.Values.TemporalInstant.pas
  • source/units/Goccia.Values.TemporalPlainDate.pas
  • source/units/Goccia.Values.TemporalPlainDateTime.pas
  • source/units/Goccia.Values.TemporalPlainMonthDay.pas
  • source/units/Goccia.Values.TemporalPlainTime.pas
  • source/units/Goccia.Values.TemporalPlainYearMonth.pas
  • source/units/Goccia.Values.TemporalZonedDateTime.pas
  • source/units/Goccia.Values.TextDecoderValue.pas
  • source/units/Goccia.Values.TextEncoderValue.pas
  • source/units/Goccia.Values.TypedArrayValue.pas
  • source/units/Goccia.Values.URLValue.pas
  • source/units/Goccia.Values.WeakMapValue.pas
  • source/units/Goccia.Values.WeakRefValue.pas
  • source/units/Goccia.Values.WeakSetValue.pas

Comment thread docs/adr/0077-thread-local-cleanup-registry.md Outdated
Comment thread source/units/Goccia.Threading.Test.pas
Comment thread source/units/Goccia.Values.ArrayValue.pas
frostney and others added 3 commits June 28, 2026 01:18
Address PR review: RegisterThreadvarCleanup is a write-once-at-init API, but
the registry tests registered their sentinels inside the test bodies. Move the
sentinel registrations (and the nil-guard) into the test program's startup
block, before any worker thread is spawned, and keep the tests to resetting
counters, draining, and asserting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address PR review: the ADR described the leak gate as spawning "repeated worker
cycles" with per-cycle non-growth, which the implemented tests do not do.
Describe the two actual gates instead: Goccia.ThreadCleanupLeak.Test (one-shot
heap-drop on drain) and Goccia.Threading.Test (per-worker drain wiring).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation and removed bug Something isn't working labels Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation internal Refactoring, CI, tooling, cleanup

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Managed threadvars aren't released on worker-thread exit (engine-wide) — per-thread caches leak

1 participant