Skip to content

VM improvements including fixing AArch64 Int64-to-Double conversion paths#242

Merged
frostney merged 3 commits into
mainfrom
t3code/vm-perf-improvements
Apr 9, 2026
Merged

VM improvements including fixing AArch64 Int64-to-Double conversion paths#242
frostney merged 3 commits into
mainfrom
t3code/vm-perf-improvements

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 9, 2026

Summary

  • Fixes AArch64 Int64 to Double conversion paths that could produce incorrect results with * 1.0 and explicit casts.
  • Updates VM numeric op handling to preserve integer fast paths while avoiding buggy float promotion on affected FPC builds.
  • Reworks Object.defineProperty and Reflect.defineProperty descriptor parsing inline, and removes the shared ToPropertyDescriptor helper.
  • Replaces several * 1.0 conversion sites in console, JSON, profiler reporting, and FFI wrappers with direct numeric construction.
  • Removes redundant descriptor behavior tests that are now covered by the updated built-in implementations.

Testing

  • Not run.
  • Recommended: ./build.pas testrunner && ./build/TestRunner tests
  • Recommended for VM changes: run the relevant bytecode and built-in test subsets, especially tests/built-ins/Object/defineProperty.js and tests/built-ins/Reflect/defineProperty.js.

- Avoid `Int64 * 1.0` and explicit casts that misconvert on FPC AArch64
- Use implicit numeric assignment in VM, JSON, FFI, console, and profiler code
- Document the compiler bug and safe conversion patterns
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e2242a61-811b-46ad-8d50-604930b23794

📥 Commits

Reviewing files that changed from the base of the PR and between caa8355 and ddb9f92.

📒 Files selected for processing (1)
  • docs/code-style.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/code-style.md

📝 Walkthrough

Walkthrough

Documents two distinct FPC 3.2.2 Int64→Double conversion bugs, replaces integer-via-*1.0 float coercions with direct integer literal creation, adds integer fast-paths and adjusted numeric handling in the VM, and simplifies percentage/number expressions across multiple units.

Changes

Cohort / File(s) Summary
Documentation
AGENTS.md, docs/code-style.md
Expanded the AArch64/FPC 3.2.2 note into an “Int64 to Double conversion” section describing two bugs (Delphi-mode Double(Int64Var) bit-reinterpretation and AArch64 mixed Int64 * Double promotion errors). Updated safe-conversion guidance and examples.
Value literal creation
units/Goccia.Builtins.Console.pas, units/Goccia.JSON.pas, units/Goccia.Values.FFILibrary.pas, units/Goccia.Values.FFIPointer.pas, units/Goccia.Values.StringObjectValue.pas
Replaced integer-to-float coercion patterns using * 1.0 with direct integer-based TGocciaNumberLiteralValue.Create(...) calls (Int64/QWord/PtrUInt/CodePoint) to preserve integer semantics and avoid FPC conversion pitfalls.
Profiler / numeric formatting
units/Goccia.Profiler.Report.pas
Removed redundant * 1.0 in percentage calculations, e.g., changed (Count * 1.0 / Total) * 100.0 to (Count / Total) * 100.0.
VM numeric/control-flow changes
units/Goccia.VM.pas
Added VMIntResult(...); introduced strict grkInt+grkInt fast-paths for arithmetic and comparisons, preserved mixed numeric float fallback paths, adjusted OP_TO_PRIMITIVE, boolean jump evaluation, and updated combined opcodes to use integer fast-paths when both operands are integers.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Add bytecode VM profiling support #237: Modifies VM opcode handling and profiler-related code touching units/Goccia.VM.pas and profiling/report logic, overlapping with this PR's VM fast-path and profiler adjustments.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main focus of the PR: VM improvements centered on fixing AArch64 Int64-to-Double conversion paths, which is a core objective across multiple files.
Description check ✅ Passed The description covers the main changes and includes a testing section with checkbox items, but the testing section is incomplete—all checkboxes are unchecked and testing was not run.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


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

@frostney frostney changed the title Fix AArch64 Int64-to-Double conversion paths VM improvements including fixing AArch64 Int64-to-Double conversion paths Apr 9, 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: 2

Caution

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

⚠️ Outside diff range comments (1)
units/Goccia.Profiler.Report.pas (1)

335-408: ⚠️ Potential issue | 🟡 Minor

Line 406 uses inconsistent percentage calculation pattern.

The profiler JSON export at line 406 computes hitRate as Hits * 100.0 / ScalarTotal, while all other percentage calculations in this file (lines 162, 221, 252) use the pattern (numerator / denominator) * 100.0. For consistency and to match the safe pattern applied elsewhere in this PR, update line 406 to use the same formula.

Suggested fix
   if ScalarTotal > 0 then
-    Buf.Append(Format(', "hitRate": %.1f', [Hits * 100.0 / ScalarTotal],
+    Buf.Append(Format(', "hitRate": %.1f', [(Hits / ScalarTotal) * 100.0],
       InvariantFormat));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@units/Goccia.Profiler.Report.pas` around lines 335 - 408, In WriteProfileJSON
(procedure), the scalarFastPath hitRate is calculated with the inconsistent
expression Hits * 100.0 / ScalarTotal; change the Format call that appends ",
\"hitRate\": ..." so the percentage uses the same safe pattern as the rest of
the file: (Hits / ScalarTotal) * 100.0, and make sure the InvariantFormat is
passed to that Format invocation (use the existing Buf.Append(Format(...,
InvariantFormat)) pattern).
🧹 Nitpick comments (1)
units/Goccia.VM.pas (1)

3748-3862: Please add boundary regression cases for the new int fast paths.

These branches change both typed and generic arithmetic/comparison behavior right at the LongInt edges. I’d add bytecode coverage for 2147483647 ± 1, -2147483648 ± 1, and an overflowed multiply so the VMIntResult(...) fallback stays pinned down on AArch64 builds.

Based on learnings: "Every new feature or change must follow the feature workflow: create a branch, implement the feature, annotate spec references, add/update tests, update documentation, and commit together with a clear message."

Also applies to: 4288-4596

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

In `@units/Goccia.VM.pas` around lines 3748 - 3862, Add regression tests that
exercise the new int fast paths (OP_ADD_INT, OP_SUB_INT, OP_MUL_INT and the int
comparison ops like OP_EQ_INT, OP_LT_INT, OP_GT_INT, OP_LTE_INT, OP_GTE_INT)
around LongInt boundaries so the VM falls back correctly to
VMIntResult/VMNumberRegister and RegisterToDouble when overflow occurs on
AArch64; specifically add bytecode tests for 2147483647 + 1 and -2147483648 - 1,
the symmetric ±1 cases for add/sub, and an overflowing multiply that would
exceed LongInt, and assert both the numeric value and the resulting register
kind in FRegisters (i.e., whether it produced an int fast path result or used
the float/result fallback) to pin down behavior across platforms.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AGENTS.md`:
- Around line 396-400: Update the "Platform Pitfall: `Int64` to `Double`
Conversion on AArch64" section so Bug A (`Double(Int64Var)` bit
reinterpretation, referenced by FPC `#35886` and the Delphi-mode front-end
behavior) is described as a Delphi-mode cross-platform issue, while Bug B (wrong
results near ±2³¹) remains labeled as AArch64-only; specifically, change the
introductory sentence that currently groups both under "AArch64" to clearly
separate the two: state that Bug A affects all platforms in {$mode delphi}
(Delphi mode) and Bug B is AArch64-specific, and mirror the phrasing used in
docs/code-style.md for clarity.

In `@docs/code-style.md`:
- Around line 327-333: Add a language tag to the untyped fenced code block that
begins with ``` and contains the Int64(...) examples so markdownlint stops
flagging it; change the opening fence from ``` to ```text (i.e. replace the
existing opening fence with ```text) and leave the block contents (lines
containing Int64(-2147483647) * 1.0 ... Int64(-3000000000) * 1.0) and the
closing ``` unchanged.

---

Outside diff comments:
In `@units/Goccia.Profiler.Report.pas`:
- Around line 335-408: In WriteProfileJSON (procedure), the scalarFastPath
hitRate is calculated with the inconsistent expression Hits * 100.0 /
ScalarTotal; change the Format call that appends ", \"hitRate\": ..." so the
percentage uses the same safe pattern as the rest of the file: (Hits /
ScalarTotal) * 100.0, and make sure the InvariantFormat is passed to that Format
invocation (use the existing Buf.Append(Format(..., InvariantFormat)) pattern).

---

Nitpick comments:
In `@units/Goccia.VM.pas`:
- Around line 3748-3862: Add regression tests that exercise the new int fast
paths (OP_ADD_INT, OP_SUB_INT, OP_MUL_INT and the int comparison ops like
OP_EQ_INT, OP_LT_INT, OP_GT_INT, OP_LTE_INT, OP_GTE_INT) around LongInt
boundaries so the VM falls back correctly to VMIntResult/VMNumberRegister and
RegisterToDouble when overflow occurs on AArch64; specifically add bytecode
tests for 2147483647 + 1 and -2147483648 - 1, the symmetric ±1 cases for
add/sub, and an overflowing multiply that would exceed LongInt, and assert both
the numeric value and the resulting register kind in FRegisters (i.e., whether
it produced an int fast path result or used the float/result fallback) to pin
down behavior across platforms.
🪄 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: 392cb659-72d8-4bc5-b5c0-f9a12c354deb

📥 Commits

Reviewing files that changed from the base of the PR and between a85b773 and 1644c22.

📒 Files selected for processing (9)
  • AGENTS.md
  • docs/code-style.md
  • units/Goccia.Builtins.Console.pas
  • units/Goccia.JSON.pas
  • units/Goccia.Profiler.Report.pas
  • units/Goccia.VM.pas
  • units/Goccia.Values.FFILibrary.pas
  • units/Goccia.Values.FFIPointer.pas
  • units/Goccia.Values.StringObjectValue.pas

Comment thread AGENTS.md Outdated
Comment thread docs/code-style.md Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

Benchmark Results

274 benchmarks

Interpreted: 🟢 262 improved · 🔴 1 regressed · 11 unchanged · avg +5.8%
Bytecode: 🟢 108 improved · 🔴 54 regressed · 112 unchanged · avg +4.9%

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

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 Apr 9, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 4046 4046
Tests Passed 4005 ✅ 4046 ✅
Tests Skipped 41 0
Tests Test Duration 288.6ms 282.4ms
Tests Lex 78.7ms 53.5ms
Tests Parse 98.2ms 102.1ms
Tests Compile 65.2ms
Tests Execute 304.3ms 313.0ms
Tests Engine Total 481.2ms 533.8ms
Benchmarks Total 274 274
Benchmarks Duration 7.59min 6.53min

Measured on ubuntu-latest x64.

- Clarify Bug A is cross-platform Delphi-mode, not AArch64-specific
- Add `text` language tag to fenced code block in docs/code-style.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

🧹 Nitpick comments (1)
docs/code-style.md (1)

315-315: Section title misleadingly implies AArch64-only scope.

The title includes "AArch64" but Bug A (Delphi-mode Double(Int64) bit reinterpretation) affects all platforms in {$mode delphi}. Only Bug B is AArch64-specific. Readers on x86_64 or other platforms may skip this section thinking it doesn't apply to them.

📝 Suggested title revision
-### `Int64` to `Double` Conversion on FPC 3.2.2 AArch64
+### `Int64` to `Double` Conversion on FPC 3.2.2

The individual bug headings already distinguish cross-platform vs AArch64-only scope.

Based on learnings: Bug A is a cross-platform Delphi-mode front-end bug, not AArch64-specific; Bug B is AArch64-only.

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

In `@docs/code-style.md` at line 315, Rename the section titled "Int64 to Double
Conversion on FPC 3.2.2 AArch64" to remove the AArch64-only implication (e.g.,
"Int64 to Double Conversion on FPC 3.2.2") and add a brief clarifying sentence
at the top referencing Bug A and Bug B: state that Bug A (Delphi-mode
Double(Int64) bit-reinterpretation) is cross-platform and affects all targets,
while Bug B is AArch64-specific; ensure the headings for Bug A and Bug B remain
to distinguish scope and update any surrounding references that assume
AArch64-only.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/code-style.md`:
- Line 315: Rename the section titled "Int64 to Double Conversion on FPC 3.2.2
AArch64" to remove the AArch64-only implication (e.g., "Int64 to Double
Conversion on FPC 3.2.2") and add a brief clarifying sentence at the top
referencing Bug A and Bug B: state that Bug A (Delphi-mode Double(Int64)
bit-reinterpretation) is cross-platform and affects all targets, while Bug B is
AArch64-specific; ensure the headings for Bug A and Bug B remain to distinguish
scope and update any surrounding references that assume AArch64-only.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 17efb41f-1259-45d9-aace-942d1d155381

📥 Commits

Reviewing files that changed from the base of the PR and between 1644c22 and caa8355.

📒 Files selected for processing (2)
  • AGENTS.md
  • docs/code-style.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • AGENTS.md

Bug A is cross-platform Delphi-mode; only Bug B is AArch64-specific.
The intro sentence now states each bug's scope upfront.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@frostney frostney merged commit e5195d7 into main Apr 9, 2026
9 checks passed
@frostney frostney deleted the t3code/vm-perf-improvements branch April 9, 2026 12:17
@frostney frostney added the bug Something isn't working label Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant