Skip to content

Report bytecode timing phases separately#198

Merged
frostney merged 3 commits intomainfrom
t3code/bytecode-timing-labels
Apr 7, 2026
Merged

Report bytecode timing phases separately#198
frostney merged 3 commits intomainfrom
t3code/bytecode-timing-labels

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Apr 7, 2026

Summary

  • Split bytecode timing into distinct lex, parse, compile, and execute phases across the loader, test runner, and benchmark runner.
  • Added CompileTimeNanoseconds to shared timing records and surfaced it in JSON output and human-readable reports.
  • Updated PR workflow reporting to show the new timing breakdown in test results.

Testing

  • Concrete checks expected: run ./build.pas testrunner && ./build/TestRunner tests.
  • Concrete checks expected: run ./build.pas benchmarkrunner && ./build/BenchmarkRunner benchmarks --format=json to verify the new timing fields.
  • Concrete checks expected: run ./build.pas loader && ./build/ScriptLoader example.js --mode=bytecode to confirm bytecode timing output includes lex/parse/compile/execute phases.

Summary by CodeRabbit

  • New Features

    • Adds finer-grained timing metrics (lex, parse, compile, execute) to console and JSON test/benchmark outputs.
    • Console output now shows a per-test breakdown: Lex | Parse | Compile | Execute | Total when available.
  • Improvements

    • Renamed summary labels for clarity ("Test Duration" and "Engine Total").
    • More accurate and consistent timing calculations across modes and aggregate reports.

- Track lex, parse, compile, and execute timings in runner results
- Update benchmark, test, and script-loader reports to show the new breakdown
- Include compile timing in JSON output and PR summary tables
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

Warning

Rate limit exceeded

@frostney has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 45 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 9 minutes and 45 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a459f15-526b-4edd-876a-027756113123

📥 Commits

Reviewing files that changed from the base of the PR and between 1debb77 and 2d8c497.

📒 Files selected for processing (1)
  • ScriptLoader.dpr
📝 Walkthrough

Walkthrough

Introduces fine-grained timing for lex, parse, compile, and execute phases across benchmark, test, script-loading, and reporting code; updates JSON output and console formatting; and adjusts the GitHub Actions PR comment to display the new per-phase timing metrics.

Changes

Cohort / File(s) Summary
Workflow Configuration
.github/workflows/pr.yml
PR comment rendering updated to extract and display lexTimeNanoseconds, parseTimeNanoseconds, compileTimeNanoseconds, and executeTimeNanoseconds (with fallback) and relabel previous rows (Test Duration, Engine Total).
Benchmark & Test Runners
BenchmarkRunner.dpr, TestRunner.dpr
Split bytecode timing into distinct lex, parse, and compile measurements; compute lex/parse/compile/execute/total nanoseconds per file and fix aggregate compile accumulation.
Script Loader
ScriptLoader.dpr, units/Goccia.ScriptLoader.JSON.pas
Added compilation timing capture for bytecode-from-source, changed execute-time to be post-compile, and extended timing JSON to emit compile_ms / compileTimeNanoseconds.
Engine & Data Model
units/Goccia.Engine.pas
Added CompileTimeNanoseconds: Int64 to TGocciaScriptResult and initialize it to 0 in execution path.
Reporting
units/Goccia.Benchmark.Reporter.pas
Console summary now prints `Lex

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hop through code with stopwatch paws,

lex then parse, no more timing flaws;
compile trimmed neat, execute clear and bright,
totals add up, each phase in sight —
a rabbit's tally, measured just right.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description includes a proper Summary section documenting the changes and implementation approach, but Testing section lacks the required checkbox format and specific test verification confirmations specified in the template. Reformat Testing section with checkboxes (e.g., '- [ ] Verified...') and clearly mark which checks have been completed, as required by the template structure.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Report bytecode timing phases separately' clearly and specifically describes the main change: refactoring bytecode timing to expose individual lex, parse, compile, and execute phases instead of lumped metrics.
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.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/bytecode-timing-labels

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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 (2)
ScriptLoader.dpr (1)

272-276: ⚠️ Potential issue | 🔴 Critical

Missing CompileTimeNanoseconds assignment in interpreted mode causes uninitialized data in JSON output.

The ExecuteInterpreted function assigns all timing fields except CompileTimeNanoseconds. In Free Pascal, local record fields are not automatically zero-initialized, so Result.Timing.CompileTimeNanoseconds will contain garbage from the stack. This will cause incorrect JSON output (e.g., "compile_ms": <random large number>) when running in interpreted mode.

🐛 Proposed fix: Initialize CompileTimeNanoseconds to 0
   Result.ResultValue := ScriptResult.Result;
   Result.Timing.LexTimeNanoseconds := ScriptResult.LexTimeNanoseconds;
   Result.Timing.ParseTimeNanoseconds := ScriptResult.ParseTimeNanoseconds;
+  Result.Timing.CompileTimeNanoseconds := 0;
   Result.Timing.ExecuteTimeNanoseconds := ScriptResult.ExecuteTimeNanoseconds;
   Result.Timing.TotalTimeNanoseconds := ScriptResult.TotalTimeNanoseconds;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ScriptLoader.dpr` around lines 272 - 276, ExecuteInterpreted leaves
Result.Timing.CompileTimeNanoseconds uninitialized, producing garbage in JSON;
update the ExecuteInterpreted function to explicitly set
Result.Timing.CompileTimeNanoseconds := 0 (alongside the other timing
assignments such as Result.Timing.LexTimeNanoseconds, ParseTimeNanoseconds,
ExecuteTimeNanoseconds, TotalTimeNanoseconds) so the CompileTimeNanoseconds
field is zeroed in interpreted mode and JSON output is correct.
TestRunner.dpr (1)

144-152: ⚠️ Potential issue | 🔴 Critical

Initialize CompileTimeNanoseconds in TGocciaEngine.Execute.

The Execute function fails to initialize CompileTimeNanoseconds in the result record. It sets LexTimeNanoseconds, ParseTimeNanoseconds, ExecuteTimeNanoseconds, and TotalTimeNanoseconds, but leaves CompileTimeNanoseconds uninitialized. When Result.Timing := EngineResult copies the entire record in TestRunner.dpr (line 148), the garbage value propagates to the test result. The exception path avoids this by calling MakeEmptyTestResult, which properly initializes all fields to zero.

Add initialization after line 813 in units/Goccia.Engine.pas:

 Result.FileName := FFileName;
+Result.CompileTimeNanoseconds := 0;
 StartTime := GetNanoseconds;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TestRunner.dpr` around lines 144 - 152, TGocciaEngine.Execute currently
leaves CompileTimeNanoseconds uninitialized in its result record; update Execute
to explicitly set CompileTimeNanoseconds := 0 (or initialize it from the correct
measured value if intended) before returning the EngineResult so that
Result.Timing := EngineResult does not copy garbage. Locate the Execute method
in TGocciaEngine (units/Goccia.Engine.pas), mirror the pattern used for
LexTimeNanoseconds/ParseTimeNanoseconds/ExecuteTimeNanoseconds/TotalTimeNanoseconds,
and ensure the field is initialized similarly (or use MakeEmptyTestResult as a
reference for zeroing timing fields) so tests receive a valid
CompileTimeNanoseconds value.
🤖 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 `@ScriptLoader.dpr`:
- Around line 272-276: ExecuteInterpreted leaves
Result.Timing.CompileTimeNanoseconds uninitialized, producing garbage in JSON;
update the ExecuteInterpreted function to explicitly set
Result.Timing.CompileTimeNanoseconds := 0 (alongside the other timing
assignments such as Result.Timing.LexTimeNanoseconds, ParseTimeNanoseconds,
ExecuteTimeNanoseconds, TotalTimeNanoseconds) so the CompileTimeNanoseconds
field is zeroed in interpreted mode and JSON output is correct.

In `@TestRunner.dpr`:
- Around line 144-152: TGocciaEngine.Execute currently leaves
CompileTimeNanoseconds uninitialized in its result record; update Execute to
explicitly set CompileTimeNanoseconds := 0 (or initialize it from the correct
measured value if intended) before returning the EngineResult so that
Result.Timing := EngineResult does not copy garbage. Locate the Execute method
in TGocciaEngine (units/Goccia.Engine.pas), mirror the pattern used for
LexTimeNanoseconds/ParseTimeNanoseconds/ExecuteTimeNanoseconds/TotalTimeNanoseconds,
and ensure the field is initialized similarly (or use MakeEmptyTestResult as a
reference for zeroing timing fields) so tests receive a valid
CompileTimeNanoseconds value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 80e51cee-5f0a-4b3e-9706-c877fa1286f6

📥 Commits

Reviewing files that changed from the base of the PR and between cdaca31 and 7023ce6.

📒 Files selected for processing (7)
  • .github/workflows/pr.yml
  • BenchmarkRunner.dpr
  • ScriptLoader.dpr
  • TestRunner.dpr
  • units/Goccia.Benchmark.Reporter.pas
  • units/Goccia.Engine.pas
  • units/Goccia.ScriptLoader.JSON.pas

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 3701 3701
Tests Passed 3660 ✅ 3701 ✅
Tests Skipped 41 0
Tests Test Duration 206.4ms 200.7ms
Tests Lex 69.1ms 47.0ms
Tests Parse 89.5ms 91.4ms
Tests Compile 54.3ms
Tests Execute 220.1ms 225.7ms
Tests Engine Total 378.7ms 418.3ms
Benchmarks Total 274 274
Benchmarks Duration 7.36min 6.23min

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Benchmark Results

274 benchmarks

Interpreted: 🟢 14 improved · 🔴 236 regressed · 24 unchanged · avg -4.1%
Bytecode: 🟢 26 improved · 🔴 114 regressed · 134 unchanged · avg -1.0%

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

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.

- Set `CompileTimeNanoseconds` to `0` for interpreted execution paths
- Keep timing records consistent in `ScriptLoader` and engine results
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)
ScriptLoader.dpr (1)

354-357: ⚠️ Potential issue | 🟡 Minor

Missing CompileTimeNanoseconds := 0 initialization.

ExecuteBytecodeFromFile sets all timing fields except CompileTimeNanoseconds. In Object Pascal, record result fields are not auto-initialized, so this may produce garbage values in JSON output (compile_ms) or console display.

Since precompiled .gbc files have no compilation phase at load time, this should be explicitly set to 0 for consistency with ExecuteInterpreted.

Proposed fix
       Result.Timing.LexTimeNanoseconds := 0;
       Result.Timing.ParseTimeNanoseconds := 0;
+      Result.Timing.CompileTimeNanoseconds := 0;
       Result.Timing.ExecuteTimeNanoseconds := ExecEnd - LoadEnd;
       Result.Timing.TotalTimeNanoseconds := ExecEnd - StartTime;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ScriptLoader.dpr` around lines 354 - 357, In ExecuteBytecodeFromFile set
Result.Timing.CompileTimeNanoseconds := 0 so the CompileTime field is explicitly
initialized; locate the Result.Timing assignments in ExecuteBytecodeFromFile
(the block setting LexTimeNanoseconds, ParseTimeNanoseconds,
ExecuteTimeNanoseconds, TotalTimeNanoseconds) and add CompileTimeNanoseconds :=
0 to ensure JSON/console output (compile_ms) is not garbage for precompiled .gbc
loads.
🧹 Nitpick comments (1)
ScriptLoader.dpr (1)

474-480: Consider accounting for CompileTimeNanoseconds in error-path timing.

The exception handler computes ExecuteTimeNanoseconds as Total - Lex - Parse, which would now include compile time if an error occurs during or after execution. For consistency with the new phase separation:

Proposed fix
       if Report.Timing.TotalTimeNanoseconds >
-         Report.Timing.LexTimeNanoseconds + Report.Timing.ParseTimeNanoseconds then
+         Report.Timing.LexTimeNanoseconds + Report.Timing.ParseTimeNanoseconds +
+         Report.Timing.CompileTimeNanoseconds then
         Report.Timing.ExecuteTimeNanoseconds :=
           Report.Timing.TotalTimeNanoseconds -
           Report.Timing.LexTimeNanoseconds -
-          Report.Timing.ParseTimeNanoseconds;
+          Report.Timing.ParseTimeNanoseconds -
+          Report.Timing.CompileTimeNanoseconds;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ScriptLoader.dpr` around lines 474 - 480, The exception handler's timing math
currently sets Report.Timing.ExecuteTimeNanoseconds := TotalTimeNanoseconds -
LexTimeNanoseconds - ParseTimeNanoseconds which incorrectly folds compile time
into execute time on error; modify the logic in the exception path that updates
Report.Timing.ExecuteTimeNanoseconds (the block using GetNanoseconds - StartTime
and the subsequent if) to also subtract Report.Timing.CompileTimeNanoseconds
(i.e., Compute Execute = Total - Lex - Parse - Compile) and ensure any
conditional that checks Total > Lex + Parse is updated to account for
CompileTimeNanoseconds so ExecuteTimeNanoseconds never absorbs compile time.
🤖 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 `@ScriptLoader.dpr`:
- Around line 354-357: In ExecuteBytecodeFromFile set
Result.Timing.CompileTimeNanoseconds := 0 so the CompileTime field is explicitly
initialized; locate the Result.Timing assignments in ExecuteBytecodeFromFile
(the block setting LexTimeNanoseconds, ParseTimeNanoseconds,
ExecuteTimeNanoseconds, TotalTimeNanoseconds) and add CompileTimeNanoseconds :=
0 to ensure JSON/console output (compile_ms) is not garbage for precompiled .gbc
loads.

---

Nitpick comments:
In `@ScriptLoader.dpr`:
- Around line 474-480: The exception handler's timing math currently sets
Report.Timing.ExecuteTimeNanoseconds := TotalTimeNanoseconds -
LexTimeNanoseconds - ParseTimeNanoseconds which incorrectly folds compile time
into execute time on error; modify the logic in the exception path that updates
Report.Timing.ExecuteTimeNanoseconds (the block using GetNanoseconds - StartTime
and the subsequent if) to also subtract Report.Timing.CompileTimeNanoseconds
(i.e., Compute Execute = Total - Lex - Parse - Compile) and ensure any
conditional that checks Total > Lex + Parse is updated to account for
CompileTimeNanoseconds so ExecuteTimeNanoseconds never absorbs compile time.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78c8ca12-fb88-43f9-9ea2-790082477471

📥 Commits

Reviewing files that changed from the base of the PR and between 7023ce6 and 1debb77.

📒 Files selected for processing (2)
  • ScriptLoader.dpr
  • units/Goccia.Engine.pas
🚧 Files skipped from review as they are similar to previous changes (1)
  • units/Goccia.Engine.pas

- zero `CompileTimeNanoseconds` in the load path when compilation is skipped
- subtract compile time when deriving execute time for error reports
@frostney frostney merged commit 0d9870c into main Apr 7, 2026
9 checks passed
@frostney frostney deleted the t3code/bytecode-timing-labels branch April 7, 2026 14:02
@frostney frostney added enhancement new feature New feature or request and removed enhancement labels Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant