Skip to content

Fix performance regressions from #496#508

Open
frostney wants to merge 3 commits intomainfrom
t3code/fix-bytecode-regressions
Open

Fix performance regressions from #496#508
frostney wants to merge 3 commits intomainfrom
t3code/fix-bytecode-regressions

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented May 3, 2026

Summary

  • restore typed bytecode arithmetic for provably numeric locals and strict typed parameters
  • clear stale inferred numeric hints on assignment/compound assignment, including captured mutable local safeguards
  • stop emitting per-iteration for...of exception handlers; emit no handler for plain loops and one loop-level handler only when iterator close is needed
  • add compiler regression tests for typed arithmetic and handler counts

Verification

  • ./build.pas tests loader benchmarkrunner
  • ./build/Goccia.Compiler.Test (27/27 passed)
  • ./build/GocciaTestRunner tests --asi --unsafe-ffi --no-progress (8484/8484 passed)
  • ./format.pas --check
  • git diff --check
  • opcode spot checks: inferred locals and strict typed params emit OP_ADD_FLOAT; folded 2 + 3 * 4 emits no add/mul; nbody emits no OP_PUSH_HANDLER/OP_POP_HANDLER

- preserve numeric type hints through assignments and captures
- enable typed arithmetic for inferred locals and annotated params
- avoid unnecessary for-of handlers while keeping abrupt-close paths
@vercel
Copy link
Copy Markdown

vercel Bot commented May 3, 2026

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 May 3, 2026 11:28pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 3, 2026

📝 Walkthrough

Walkthrough

Adds helpers to infer/store non-strict local type hints, centralizes strict-local type checks, updates assignment and compound-assignment paths to maintain/clear hints, reorders parameter strict-type binding, and emits for...of iterator-close handlers only when the loop body requires closing.

Changes

Local Type Inference & Assignment

Layer / File(s) Summary
Type inference helpers
source/units/Goccia.Compiler.Expressions.pas, source/units/Goccia.Compiler.Statements.pas
Added InferredExpressionType (fallback to InferLocalType), and adjusted ExpressionType to return a local's TypeHint only for const, strictly-typed, or non-captured locals.
Helper APIs for hints & checks
source/units/Goccia.Compiler.Expressions.pas
Added SetNonStrictLocalTypeHint, RefreshNonStrictLocalTypeHint, and EmitStrictLocalTypeCheck to centralize non-strict hint writes and strict compatibility checks.
Assignment wiring
source/units/Goccia.Compiler.Expressions.pas
CompileAssignment uses EmitStrictLocalTypeCheck for strict locals; non-strict locals get inferred RHS type via InferredExpressionType and store it via SetNonStrictLocalTypeHint. For global-backed locals, RefreshNonStrictLocalTypeHint is used after OP_SET_GLOBAL.
Compound-assignment behavior
source/units/Goccia.Compiler.Expressions.pas
Short-circuit set paths clear non-strict hints to sltUntyped. Non-short-circuit compound assigns compute a ResultType (arithmetic may promote to sltFloat only when numeric-compatibility is known); strict checks use this ResultType. Non-global-backed locals record inferred ResultType for non-strict hints; global-backed locals record sltUntyped.
Variable declaration inference
source/units/Goccia.Compiler.Statements.pas
CompileVariableDeclaration uses InferredExpressionType for initializer-driven inference when no annotation is present; annotation usage is gated by ACtx.StrictTypes.
Parameter annotation binding order
source/units/Goccia.Compiler.Expressions.pas
ApplyParameterTypeAnnotations now sets ATemplate.SetLocalType(...) before AScope.SetLocalStrictlyTyped(...) in the strict-path, changing template-vs-scope binding order.
Tests for local/type behavior
source/units/Goccia.Compiler.Test.pas
Added tests validating inferred numeric-local arithmetic, annotated-parameter typing, stale numeric-hint invalidation across assignment variants, strict checks for global-backed cases, and captured-local mutation behavior.

Iterator-Close Optimization for For-Of

Layer / File(s) Summary
Control-flow analysis
source/units/Goccia.Compiler.Statements.pas
Added StatementNeedsIteratorClose to decide whether loop bodies require iterator close/return emission (detects abrupt-exit constructs, recurses into blocks/try, treats empty/continue-only as not needing close).
Conditional code generation
source/units/Goccia.Compiler.Statements.pas
CompileForOfStatement computes NeedsIteratorClose and conditionally allocates the close-error register, creates pending-finally iterator-close entries, and emits or skips OP_ITER_CLOSE/handler push-pop and cleanup based on the analysis.
Tests for for-of behavior
source/units/Goccia.Compiler.Test.pas, tests/language/for-of/for-of-iterators.js
Added unit and runtime tests asserting handler push/pop counts and that iterator return() is invoked when a body expression throws; also verifies no handler emission for bodies that don't require close.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

bug

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main objective: fixing performance regressions from issue #496, which aligns with the core changes across all modified files.
Description check ✅ Passed The pull request description includes a comprehensive summary of changes, detailed verification steps with concrete test results, and opcode spot checks, fully satisfying the template requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Review rate limit: 3/5 reviews remaining, refill in 18 minutes and 15 seconds.

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

@coderabbitai coderabbitai Bot added implement-feedback performance Performance improvement internal Refactoring, CI, tooling, cleanup labels May 3, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

Suite Timing

Test Runner (interpreted: 8,515 passed; bytecode: 8,515 passed)
Metric Interpreted Bytecode
Total 8515 8515
Passed 8515 ✅ 8515 ✅
Workers 4 4
Test Duration 1.80s 1.90s
Lex (cumulative) 235.0ms 144.7ms
Parse (cumulative) 244.7ms 230.8ms
Compile (cumulative) 487.1ms
Execute (cumulative) 1.62s 1.61s
Engine Total (cumulative) 2.10s 2.47s
Lex (avg/worker) 58.8ms 36.2ms
Parse (avg/worker) 61.2ms 57.7ms
Compile (avg/worker) 121.8ms
Execute (avg/worker) 405.9ms 402.1ms
Engine Total (avg/worker) 525.8ms 617.8ms

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 176.01 MiB 170.87 MiB
GC Peak Live 176.02 MiB 170.88 MiB
GC Allocated During Run 180.22 MiB 175.01 MiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 1 1
GC Collected Objects 83 83
Heap Start Allocated 142.9 KiB 142.9 KiB
Heap End Allocated 1.34 MiB 1.34 MiB
Heap Delta Allocated 1.20 MiB 1.20 MiB
Heap Delta Free 465.4 KiB 465.4 KiB
Benchmarks (interpreted: 407; bytecode: 407)
Metric Interpreted Bytecode
Total 407 407
Workers 4 4
Duration 2.40min 2.28min

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 2.75 MiB 2.74 MiB
GC Peak Live 97.40 MiB 63.41 MiB
GC Allocated During Run 13.81 GiB 9.12 GiB
GC Limit 7.81 GiB 7.81 GiB
GC Collections 2,830 2,646
GC Collected Objects 257,437,616 217,538,313
Heap Start Allocated 1.11 MiB 1.11 MiB
Heap End Allocated 1.11 MiB 1.11 MiB
Heap Delta Allocated 128 B 128 B

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

Benchmark Results

407 benchmarks

Interpreted: 🟢 10 improved · 🔴 333 regressed · 64 unchanged · avg -6.2%
Bytecode: 🟢 84 improved · 🔴 80 regressed · 243 unchanged · avg +1.7%

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

Deterministic profile diff

float16array

  • 🔴 OP_MUL_FLOAT: 0 → 700 (NEW)
  • 🔴 OP_ADD_FLOAT: 0 → 500 (NEW)
  • 🔴 OP_SUB_FLOAT: 0 → 200 (NEW)
  • 🔴 OP_SUB: 200 → 0 (REMOVED)
  • 🔴 OP_POP_HANDLER: 1,500 → 15 (-99.0%)
  • 🔴 OP_PUSH_HANDLER: 1,500 → 15 (-99.0%)
  • 🔴 OP_TO_PRIMITIVE: 2,200 → 1,200 (-45.5%)
  • 🔴 OP_MUL: 1,000 → 300 (-70.0%)
  • 🔴 OP_ADD: 1,100 → 600 (-45.5%)
  • Total instructions: 25,531 → 21,561 (-15.5%)

for-of

  • 🔴 OP_POP_HANDLER: 150 → 6 (-96.0%)
  • 🔴 OP_PUSH_HANDLER: 150 → 6 (-96.0%)
  • Total instructions: 2,331 → 2,043 (-12.4%)

generators

  • 🔴 OP_ADD_FLOAT: 0 → 100 (NEW)
  • 🔴 OP_POP_HANDLER: 300 → 3 (-99.0%)
  • 🔴 OP_PUSH_HANDLER: 300 → 3 (-99.0%)
  • 🔴 OP_TO_PRIMITIVE: 800 → 600 (-25.0%)
  • Total instructions: 5,780 → 4,986 (-13.7%)

typed-arrays

  • 🔴 OP_ADD_FLOAT: 0 → 500 (NEW)
  • 🔴 OP_MUL_FLOAT: 0 → 100 (NEW)
  • 🔴 OP_SUB_FLOAT: 0 → 100 (NEW)
  • 🔴 OP_SUB: 100 → 0 (REMOVED)
  • 🔴 OP_TO_PRIMITIVE: 1,600 → 600 (-62.5%)
  • 🔴 OP_POP_HANDLER: 800 → 8 (-99.0%)
  • 🔴 OP_PUSH_HANDLER: 800 → 8 (-99.0%)
  • 🔴 OP_ADD: 800 → 300 (-62.5%)
  • Total instructions: 14,613 → 12,029 (-17.7%)

Measured on ubuntu-latest x64. Benchmark ranges compare cached main-branch min/max ops/sec with the PR run; overlapping ranges are treated as unchanged noise. Percentage deltas are secondary context.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

test262 Conformance

Metric Value Δ vs main
Total tests 47,420 ±0
Eligible tests 38,704 (81.6%) ±0
Eligible tests passing 20,672 (53.4%) -330 (-0.9pp)
Total tests passing 20,672 (43.6%) -330 (-0.7pp)

Areas closest to 100%

Area Pass rate Δ vs main Passing
language/types 92.6% ±0pp 75 / 81
built-ins/Set 91.1% ±0pp 346 / 380
built-ins/Map 87.5% ±0pp 175 / 200

Compatibility roadmap skips

Target Skipped
excluded-syntax 6,315
modules 1,552
atomics-shared-memory 874
binary-data-dataview 854
realms 247

Non-blocking. 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
source/units/Goccia.Compiler.Expressions.pas (2)

604-608: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update the local type hint before the global-backed early return.

This branch exits before the non-strict refresh at Lines 620-625. Because ExpressionType in source/units/Goccia.Compiler.Statements.pas still trusts Local.TypeHint for non-captured locals, a top-level let x = 1; x = 'a'; x + 1 can keep taking the typed-numeric path after the string assignment.

Suggested fix
     if Local.IsGlobalBacked then
     begin
+      if not Local.IsStrictlyTyped then
+      begin
+        ValueType := InferredExpressionType(ACtx.Scope, AExpr.Value);
+        ACtx.Scope.SetLocalTypeHint(LocalIdx, ValueType);
+        ACtx.Template.SetLocalType(Slot, ValueType);
+      end;
       EmitInstruction(ACtx, EncodeABx(OP_SET_GLOBAL, ADest,
         ACtx.Template.AddConstantString(AExpr.Name)));
       Exit;
     end;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Compiler.Expressions.pas` around lines 604 - 608, Before
exiting the Local.IsGlobalBacked branch, update the local's type hint using the
same non-strict refresh logic used later (lines ~620-625) so ExpressionType
doesn't keep stale Local.TypeHint for non-captured locals; i.e., after emitting
OP_SET_GLOBAL
(EmitInstruction/EncodeABx/OP_SET_GLOBAL/ADest/ACtx.Template.AddConstantString(AExpr.Name))
perform the same Local.TypeHint refresh/assignment that the later non-strict
path does (use the same logic that updates Local.TypeHint based on AExpr) before
the Exit.

2265-2317: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Short-circuit and global-backed compound assignments still leave stale hints behind.

These exits never touch the local's inferred type. After x ||= 'a', x &&= obj, or a global-backed x += y, the slot can still be recorded as numeric from an earlier write, and later ExpressionType-driven codegen will keep emitting typed arithmetic for a value that is no longer provably numeric. For the short-circuit forms, the safe fallback is usually sltUntyped unless both branches are known compatible.

Also applies to: 2386-2398

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Compiler.Expressions.pas` around lines 2265 - 2317, The
short-circuit and global-backed compound assignment paths don't update the
local's inferred type, leaving stale TypeHint (e.g., numeric) after assignments
like x ||= 'a' or global-backed x += y; locate the branches handling
IsShortCircuitAssignment(AExpr.Operator) where ResolveLocal(AExpr.Name) and
GetLocal(LocalIdx) are used (the global-backed block that emits
OP_GET_GLOBAL/OP_SET_GLOBAL and the local Slot block that emits OP_SET_LOCAL),
and after performing the actual assignment (both in the global-backed else
branch and the local else branch after ACtx.CompileExpression/AEmit
OP_SET_LOCAL) clear or update the local's inferred type (e.g., set
GetLocal(LocalIdx).TypeHint := sltUntyped or call the appropriate setter) so
subsequent codegen doesn't rely on an invalid prior type; do the same in the
short-circuit exit paths where you PatchJumpTarget or Exit to ensure the local's
TypeHint is corrected whenever the assignment may change type.
🧹 Nitpick comments (2)
source/units/Goccia.Compiler.Test.pas (2)

633-635: ⚡ Quick win

Avoid depending on the current nested-function order.

GetFunction(0) assumes the arrow function is always emitted first. If the compiler starts materializing another helper template ahead of it, this test becomes flaky even though the generated bytecode is still correct.

♻️ Suggested guard
+    Expect<Integer>(Module.TopLevel.FunctionCount).ToBe(1);
     Func := Module.TopLevel.GetFunction(0);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Compiler.Test.pas` around lines 633 - 635, The test
assumes the arrow function is at GetFunction(0) which is brittle; instead locate
the emitted function by content not index. Replace the direct GetFunction(0)
usage by scanning Module.TopLevel functions (e.g. iterate
Module.TopLevel.Functions or use a FindFunctionByName helper) to pick the
function where CountOp(FuncCandidate, OP_ADD_FLOAT) > 0 (or where the arrow
function name matches), assign that to Func, then run
Expect<Integer>(CountOp(Func, OP_ADD_FLOAT)).ToBe(1) and
Expect<Integer>(CountOp(Func, OP_ADD)).ToBe(0); update references to
GetFunction(0) accordingly so the test no longer depends on emission order.

613-615: ⚡ Quick win

Pin the optimization flags in these regression tests.

The opcode checks are still running with const propagation and dead-branch elimination enabled. That makes the tests easier to satisfy accidentally if another optimization pass changes, instead of exercising the typed-arithmetic / handler-emission paths directly.

♻️ Suggested adjustment
-    False, False, False, False);
+    False, False, False, False, False, False);

Apply the same explicit False values to the other new regression cases as well.

Also applies to: 629-631, 645-647, 660-666, 679-681, 694-696

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Compiler.Test.pas` around lines 613 - 615, The regression
tests call CompileSource without pinning optimization flags, so enable passing
explicit False for the optimization booleans; update each CompileSource
invocation (e.g., the call assigning Module := CompileSource('let a = 1; let b =
2; let c = a + b; c;', False, False, False, False)) and apply the same four
False arguments to the other new regression cases (the calls around the regions
noted: the calls currently at the locations corresponding to 629-631, 645-647,
660-666, 679-681, 694-696) so const-propagation and dead-branch-elimination are
disabled and the tests exercise typed-arithmetic/handler-emission paths
directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/units/Goccia.Compiler.Statements.pas`:
- Around line 774-855: The predicate StatementNeedsIteratorClose is too
permissive (returns False for unrecognized statements) so some exception paths
skip OP_ITER_CLOSE; make it conservative by default: change the final Result
from False to True (so any statement not explicitly proven safe will require
iterator close), and review/adjust any explicit False exits (e.g. the
BlockStatement and SwitchStatement exits) only if you can prove they cannot
raise; refer to the function StatementNeedsIteratorClose and the
CompileForOfStatement paths that rely on it (also re-check similar logic around
lines referenced in the comment).

---

Outside diff comments:
In `@source/units/Goccia.Compiler.Expressions.pas`:
- Around line 604-608: Before exiting the Local.IsGlobalBacked branch, update
the local's type hint using the same non-strict refresh logic used later (lines
~620-625) so ExpressionType doesn't keep stale Local.TypeHint for non-captured
locals; i.e., after emitting OP_SET_GLOBAL
(EmitInstruction/EncodeABx/OP_SET_GLOBAL/ADest/ACtx.Template.AddConstantString(AExpr.Name))
perform the same Local.TypeHint refresh/assignment that the later non-strict
path does (use the same logic that updates Local.TypeHint based on AExpr) before
the Exit.
- Around line 2265-2317: The short-circuit and global-backed compound assignment
paths don't update the local's inferred type, leaving stale TypeHint (e.g.,
numeric) after assignments like x ||= 'a' or global-backed x += y; locate the
branches handling IsShortCircuitAssignment(AExpr.Operator) where
ResolveLocal(AExpr.Name) and GetLocal(LocalIdx) are used (the global-backed
block that emits OP_GET_GLOBAL/OP_SET_GLOBAL and the local Slot block that emits
OP_SET_LOCAL), and after performing the actual assignment (both in the
global-backed else branch and the local else branch after
ACtx.CompileExpression/AEmit OP_SET_LOCAL) clear or update the local's inferred
type (e.g., set GetLocal(LocalIdx).TypeHint := sltUntyped or call the
appropriate setter) so subsequent codegen doesn't rely on an invalid prior type;
do the same in the short-circuit exit paths where you PatchJumpTarget or Exit to
ensure the local's TypeHint is corrected whenever the assignment may change
type.

---

Nitpick comments:
In `@source/units/Goccia.Compiler.Test.pas`:
- Around line 633-635: The test assumes the arrow function is at GetFunction(0)
which is brittle; instead locate the emitted function by content not index.
Replace the direct GetFunction(0) usage by scanning Module.TopLevel functions
(e.g. iterate Module.TopLevel.Functions or use a FindFunctionByName helper) to
pick the function where CountOp(FuncCandidate, OP_ADD_FLOAT) > 0 (or where the
arrow function name matches), assign that to Func, then run
Expect<Integer>(CountOp(Func, OP_ADD_FLOAT)).ToBe(1) and
Expect<Integer>(CountOp(Func, OP_ADD)).ToBe(0); update references to
GetFunction(0) accordingly so the test no longer depends on emission order.
- Around line 613-615: The regression tests call CompileSource without pinning
optimization flags, so enable passing explicit False for the optimization
booleans; update each CompileSource invocation (e.g., the call assigning Module
:= CompileSource('let a = 1; let b = 2; let c = a + b; c;', False, False, False,
False)) and apply the same four False arguments to the other new regression
cases (the calls around the regions noted: the calls currently at the locations
corresponding to 629-631, 645-647, 660-666, 679-681, 694-696) so
const-propagation and dead-branch-elimination are disabled and the tests
exercise typed-arithmetic/handler-emission paths directly.
🪄 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

Run ID: 688aae8e-4e14-4ce6-a665-597babf8bcef

📥 Commits

Reviewing files that changed from the base of the PR and between 2f73103 and 56caf1c.

📒 Files selected for processing (3)
  • source/units/Goccia.Compiler.Expressions.pas
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.Compiler.Test.pas

Comment thread source/units/Goccia.Compiler.Statements.pas
Make for-of iterator-close analysis conservative for throwing statement forms, refresh stale inferred type hints in global-backed and short-circuit assignment paths, and tighten compiler regression tests.
@coderabbitai coderabbitai Bot added the bug Something isn't working label May 3, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
source/units/Goccia.Compiler.Expressions.pas (1)

627-632: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the missing strict-type guard before OP_SET_GLOBAL.

These global-backed branches still bypass OP_CHECK_TYPE, so a strict top-level local can be reassigned to an incompatible value without trapping, even though the non-global local and upvalue paths enforce it.

💡 Minimal direction
     if Local.IsGlobalBacked then
     begin
+      if Local.IsStrictlyTyped and (Local.TypeHint <> sltUntyped) and
+         not TypesAreCompatible(InferLocalType(AExpr.Value), Local.TypeHint) then
+        EmitInstruction(ACtx, EncodeABC(OP_CHECK_TYPE, ADest,
+          UInt8(Ord(Local.TypeHint)), 0));
       EmitInstruction(ACtx, EncodeABx(OP_SET_GLOBAL, ADest,
         ACtx.Template.AddConstantString(AExpr.Name)));
       RefreshNonStrictLocalTypeHint(ACtx, LocalIdx, AExpr.Value);
       Exit;
     end;

Mirror the same check in the short-circuit and non-short-circuit compound-assignment branches, validating the computed result register before the final OP_SET_GLOBAL.

Also applies to: 2294-2313, 2411-2422

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Compiler.Expressions.pas` around lines 627 - 632, The
global-backed branch (when Local.IsGlobalBacked) emits OP_SET_GLOBAL without the
strict-type guard, allowing reassignment of a strict top-level local to an
incompatible value; update the branch in Goccia.Compiler.Expressions (the block
emitting EncodeABx(OP_SET_GLOBAL, ADest,
ACtx.Template.AddConstantString(AExpr.Name)) and calling
RefreshNonStrictLocalTypeHint) to mirror the short-circuit and non-short-circuit
compound-assignment paths by inserting the same OP_CHECK_TYPE (or equivalent
strict-type validation) for the computed result register before emitting
OP_SET_GLOBAL so the value is validated against the local's strict type (and
still call RefreshNonStrictLocalTypeHint for non-strict cases); apply the same
change to the other two similar blocks noted (around lines 2294-2313 and
2411-2422).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/units/Goccia.Compiler.Test.pas`:
- Around line 195-201: FindFunctionWithOp currently skips checking the root
ATemplate and only inspects its children; change the search to first test the
template itself (e.g., if CountOp(ATemplate, AOp) > 0 then Exit(ATemplate))
before iterating children, and when recursing with FindFunctionWithOp(Candidate,
AOp) check the returned Result and Exit(Result) immediately if non-nil so the
found candidate bubbles up (use symbols ATemplate, CountOp, AOp, Candidate,
FindFunctionWithOp).

---

Outside diff comments:
In `@source/units/Goccia.Compiler.Expressions.pas`:
- Around line 627-632: The global-backed branch (when Local.IsGlobalBacked)
emits OP_SET_GLOBAL without the strict-type guard, allowing reassignment of a
strict top-level local to an incompatible value; update the branch in
Goccia.Compiler.Expressions (the block emitting EncodeABx(OP_SET_GLOBAL, ADest,
ACtx.Template.AddConstantString(AExpr.Name)) and calling
RefreshNonStrictLocalTypeHint) to mirror the short-circuit and non-short-circuit
compound-assignment paths by inserting the same OP_CHECK_TYPE (or equivalent
strict-type validation) for the computed result register before emitting
OP_SET_GLOBAL so the value is validated against the local's strict type (and
still call RefreshNonStrictLocalTypeHint for non-strict cases); apply the same
change to the other two similar blocks noted (around lines 2294-2313 and
2411-2422).
🪄 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

Run ID: 7fba2814-7c7d-430a-84f9-d6fd8561b8a3

📥 Commits

Reviewing files that changed from the base of the PR and between 56caf1c and 4163a72.

📒 Files selected for processing (4)
  • source/units/Goccia.Compiler.Expressions.pas
  • source/units/Goccia.Compiler.Statements.pas
  • source/units/Goccia.Compiler.Test.pas
  • tests/language/for-of/for-of-iterators.js

Comment thread source/units/Goccia.Compiler.Test.pas
Include root templates in function opcode lookup and emit strict type guards before global-backed assignment stores, including short-circuit and compound assignment paths.
@frostney
Copy link
Copy Markdown
Owner Author

frostney commented May 3, 2026

Addressed the latest CodeRabbit findings in 9e7b7b2f:

  • FindFunctionWithOp now checks the root ATemplate before scanning child templates and returns recursive matches immediately.
  • Global-backed direct assignment now emits the strict type guard before OP_SET_GLOBAL.
  • Global-backed short-circuit assignment now emits the same strict type guard before storing the RHS globally.
  • Global-backed compound assignment now validates the computed result register before OP_SET_GLOBAL; the local compound path also uses the same computed-result guard so strict compound assignment semantics stay consistent.
  • Added compiler regression tests for the three global-backed strict assignment paths.

Verification run:

  • ./build.pas tests loader
  • ./build/Goccia.Compiler.Test (35/35 passed)
  • ./build/GocciaTestRunner tests/language/types-as-comments --mode=bytecode --asi --no-progress (114/114 passed)
  • ./build/GocciaTestRunner tests --asi --unsafe-ffi --no-progress (8485/8485 passed)
  • ./build/GocciaTestRunner tests --asi --unsafe-ffi --mode=bytecode --no-progress (8485/8485 passed)
  • ./format.pas --check
  • git diff --check

I replied directly to the inline CodeRabbit thread as well. Marking the thread resolved still fails because the authenticated GitHub user has exhausted its GraphQL rate limit.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
source/units/Goccia.Compiler.Expressions.pas (1)

2450-2469: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Check strict local compound-assignment results before writing back to the slot.

EmitInstruction(..., Slot, Slot, RegVal) mutates the local before EmitStrictLocalTypeCheck(...) runs. In strict mode, a += "x" will throw, but a surrounding catch can still observe the already-corrupted local afterward. Compute into a temp register, validate there, then commit with OP_SET_LOCAL.

Proposed fix
-    if IsKnownNumeric(LocalType) and
-       IsKnownNumeric(ValType) and
-       IsArithmeticCompoundAssign(AExpr.Operator, ArithOp) and
-       TryFloatOp(ArithOp, FloatOp) then
-    begin
-      EmitInstruction(ACtx, EncodeABC(FloatOp, Slot, Slot, RegVal))
-    end
-    else
-      EmitInstruction(ACtx, EncodeABC(Op, Slot, Slot, RegVal));
+    RegResult := ACtx.Scope.AllocateRegister;
+    if IsKnownNumeric(LocalType) and
+       IsKnownNumeric(ValType) and
+       IsArithmeticCompoundAssign(AExpr.Operator, ArithOp) and
+       TryFloatOp(ArithOp, FloatOp) then
+    begin
+      EmitInstruction(ACtx, EncodeABC(FloatOp, RegResult, Slot, RegVal))
+    end
+    else
+      EmitInstruction(ACtx, EncodeABC(Op, RegResult, Slot, RegVal));
     ResultType := sltUntyped;
     if IsArithmeticCompoundAssign(AExpr.Operator, ArithOp) and
        IsKnownNumeric(LocalType) and IsKnownNumeric(ValType) then
       ResultType := sltFloat;
-    EmitStrictLocalTypeCheck(ACtx, LocalIdx, Slot, ResultType);
+    EmitStrictLocalTypeCheck(ACtx, LocalIdx, RegResult, ResultType);
+    EmitInstruction(ACtx, EncodeABx(OP_SET_LOCAL, RegResult, Slot));
     if not ACtx.Scope.GetLocal(LocalIdx).IsStrictlyTyped then
     begin
       SetNonStrictLocalTypeHint(ACtx, LocalIdx, ResultType);
     end;
     if ADest <> Slot then
       EmitInstruction(ACtx, EncodeABC(OP_MOVE, ADest, Slot, 0));
     ACtx.Scope.FreeRegister;
+    ACtx.Scope.FreeRegister;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/units/Goccia.Compiler.Expressions.pas` around lines 2450 - 2469, The
current EmitInstruction writes the compound-assignment result directly into the
local slot (via EncodeABC(..., Slot, Slot, RegVal)) before
EmitStrictLocalTypeCheck runs, allowing strict-mode exceptions to leave the
local mutated; instead compute into a temporary register, run
EmitStrictLocalTypeCheck against that temp, and only on success commit the temp
into the local with a separate set-local instruction; update the block that
handles numeric and non-numeric compound assigns (the code using LocalType,
ValType, ArithOp, FloatOp, Slot, RegVal) to emit to a temp reg (e.g. RegTemp)
via EncodeABC/EmitInstruction, call EmitStrictLocalTypeCheck(ACtx, LocalIdx,
RegTemp, ResultType) and then EmitInstruction to write RegTemp into the local
(and still call SetNonStrictLocalTypeHint when needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@source/units/Goccia.Compiler.Expressions.pas`:
- Around line 2450-2469: The current EmitInstruction writes the
compound-assignment result directly into the local slot (via EncodeABC(...,
Slot, Slot, RegVal)) before EmitStrictLocalTypeCheck runs, allowing strict-mode
exceptions to leave the local mutated; instead compute into a temporary
register, run EmitStrictLocalTypeCheck against that temp, and only on success
commit the temp into the local with a separate set-local instruction; update the
block that handles numeric and non-numeric compound assigns (the code using
LocalType, ValType, ArithOp, FloatOp, Slot, RegVal) to emit to a temp reg (e.g.
RegTemp) via EncodeABC/EmitInstruction, call EmitStrictLocalTypeCheck(ACtx,
LocalIdx, RegTemp, ResultType) and then EmitInstruction to write RegTemp into
the local (and still call SetNonStrictLocalTypeHint when needed).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 98ce0440-77aa-437d-9474-0198f9f44340

📥 Commits

Reviewing files that changed from the base of the PR and between 4163a72 and 9e7b7b2.

📒 Files selected for processing (2)
  • source/units/Goccia.Compiler.Expressions.pas
  • source/units/Goccia.Compiler.Test.pas

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

Labels

bug Something isn't working implement-feedback internal Refactoring, CI, tooling, cleanup performance Performance improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant