Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,25 @@ The benchmark mounts a large bound list and then runs repeated manipulations:
Each framework runs the same operation pattern; timings are measured with
`performance.now()`.

Use the **Workload** selector to choose the original **Bound list** or
**Nested components (3 per row)**. Both workloads run the same keyed list
operations. The component workload renders a row card, label component, and
value component for each row, with reactive props, class fallthrough, and
parent-scoped slot content. Regor uses an app-local row component, a label
registered in each row's context, and a globally registered value component.
Vue renders the equivalent component tree and DOM.

At 500 rows this mounts 1,500 component instances; at 2,000 rows it mounts 6,000.
Component definitions are created before mount timing. Regor prepares its
component templates there; Vue's runtime template compilation occurs during
mount. Rendering is checked after mount and each operation, outside the timed
sections. A rendering mismatch fails the run.

Regor's swap uses batched `splice` calls, shuffle commits the shuffled order
with `splice`, and object replacement updates each existing element ref with
`rows[i](ref(replacements[i]))`. This preserves reactive updates rather than replacing the
ref functions stored at array indexes.

## Run

1. Build benchmark bundle:
Expand All @@ -34,11 +53,15 @@ yarn bench:serve

`http://localhost:4177/`

4. Click **Run Benchmark**.
4. Select a workload and click **Run Regor**, **Run Vue**, or **Run Both**.

The mount-only benchmark at `http://localhost:4177/benchmarks/initial-load.html`
has the same workload selector.

## Notes

- `Vue@latest` requires internet access.
- Run multiple times and compare medians.
- Keep browser/devtools conditions consistent for fair comparisons.

- The Node-based [minidom suite](minidom/README.md) also includes component
mounting, prop updates, and repeated subtree remounting.
17 changes: 13 additions & 4 deletions benchmarks/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
}
body {
margin: 0;
font-family: Consolas, "Courier New", monospace;
font-family: Consolas, 'Courier New', monospace;
background: radial-gradient(circle at 0% 0%, #1e293b 0, var(--bg) 45%);
color: var(--fg);
}
Expand Down Expand Up @@ -98,12 +98,20 @@
<section class="panel">
<h1>DOM Benchmark: Regor vs Vue (latest)</h1>
<p>
Workload: mount a bound list, then run repeated list/data manipulations
(`for` binding updates, splices, push/shift, property updates).
Workload: mount a bound list, then run repeated list/data
manipulations (`for` binding updates, splices, push/shift, property
updates).
</p>
<button id="run-regor">Run Regor</button>
<button id="run-vue">Run Vue</button>
<button id="run">Run Both</button>
<label style="margin-left: 12px; color: var(--muted)">
Workload
<select id="workload">
<option value="list" selected>Bound list</option>
<option value="components">Nested components (3 per row)</option>
</select>
</label>
<label style="margin-left: 12px; color: var(--muted)">
Row count
<select id="row-count">
Expand Down Expand Up @@ -173,7 +181,8 @@ <h1>DOM Benchmark: Regor vs Vue (latest)</h1>
</div>
</div>
<div class="note">
Tip: run a few times and compare medians in devtools Performance panel.
Tip: run a few times and compare medians in devtools Performance
panel.
</div>
</section>
</main>
Expand Down
7 changes: 7 additions & 0 deletions benchmarks/initial-load.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ <h1>Initial Load Benchmark: Regor vs Vue</h1>
<button id="run-regor">Run Regor</button>
<button id="run-vue">Run Vue</button>
<button id="run">Run Both</button>
<label style="margin-left: 12px; color: var(--muted)">
Workload
<select id="workload">
<option value="list" selected>Bound list</option>
<option value="components">Nested components (3 per row)</option>
</select>
</label>
<label style="margin-left: 12px; color: var(--muted)">
Row count
<select id="row-count">
Expand Down
28 changes: 28 additions & 0 deletions benchmarks/minidom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@

Regor performance suite running on `tests/minidom`, with baseline comparison and optional regression gate.

## Component Workloads

The suite includes three component-heavy scenarios in addition to the original
list and branch scenarios:

- `mount_nested_components`: mount a keyed list of row cards, each containing
a local label component, a globally registered value component, and slot content.
- `mutate_component_props`: mount that list, then update every row's label,
value, and active class through reactive props.
- `remount_component_tree`: mount that list, then hide and show the entire
subtree three times using `r-if`.

Each tree has three component instances per row: 1,500 at the default 500 rows,
or 6,000 at 2,000 rows. Component definitions and data setup are excluded from
timing. As in the original scenarios, the primary time includes initial mount
plus the scenario's operations; unmount and cleanup are reported separately.
Output checks verify every row's props, classes, order, and slot content outside
both timed sections. A mismatch fails the run.

These measurements use minidom's selector and DOM implementations. Use the
browser benchmark's **Nested components** workload to measure browser DOM costs.

## Quick Start

Run with default row count (`500`):
Expand Down Expand Up @@ -62,6 +84,7 @@ What this does:
- `perf-<rows>-<timestamp>.summary.txt`

Tip:

- Use `PERF_SAMPLES` and `PERF_WARMUPS` to tune profile stability.
- Example:
- `PERF_SAMPLES=30 PERF_WARMUPS=8 yarn perf:profile 2000`
Expand Down Expand Up @@ -93,6 +116,11 @@ If a baseline does not exist, create it first:
yarn perf:record 500
```

When adding scenarios, existing baselines report them as missing and skip their
regression checks. Run `yarn perf:record <rows>` on the version you want to compare
against before evaluating subsequent changes. Recording replaces the baseline
for every scenario at that row count.

## Environment Variables

- `PERF_SAMPLES` (default `20`)
Expand Down
112 changes: 80 additions & 32 deletions benchmarks/minidom/perf-baseline.1000.json
Original file line number Diff line number Diff line change
@@ -1,60 +1,108 @@
{
"meta": {
"version": 1,
"createdAt": "2026-02-24T07:24:04.513Z",
"createdAt": "2026-10-18T21:12:13.447Z",
"platform": "win32",
"arch": "x64",
"node": "v22.16.0",
"node": "v22.22.2",
"rowCount": 1000,
"samples": 20,
"warmups": 5
},
"results": {
"mount_rfor_rtext": {
"median": 73.023,
"p90": 80.194,
"mean": 74.312,
"min": 68.114,
"max": 83.47,
"median": 19.946,
"p90": 22.29,
"mean": 20.268,
"min": 18.218,
"max": 23.051,
"samples": 20,
"unmount": {
"median": 7.723,
"p90": 8.965,
"mean": 8.021,
"min": 7.033,
"max": 11.321,
"median": 2.377,
"p90": 3.258,
"mean": 2.493,
"min": 1.942,
"max": 3.66,
"samples": 20
}
},
"mutate_rows_fields": {
"median": 87.079,
"p90": 92.333,
"mean": 87.982,
"min": 84.215,
"max": 101.536,
"median": 26.691,
"p90": 28.762,
"mean": 27.269,
"min": 25.111,
"max": 33.975,
"samples": 20,
"unmount": {
"median": 8.655,
"p90": 9.838,
"mean": 8.678,
"min": 7.588,
"max": 10.206,
"median": 2.915,
"p90": 3.777,
"mean": 3.038,
"min": 2.421,
"max": 4.007,
"samples": 20
}
},
"toggle_if_loop": {
"median": 11.49,
"p90": 11.779,
"mean": 11.277,
"min": 10.591,
"max": 12.1,
"median": 2.922,
"p90": 3.538,
"mean": 3.014,
"min": 2.517,
"max": 3.742,
"samples": 20,
"unmount": {
"median": 0.309,
"p90": 0.337,
"mean": 0.31,
"min": 0.259,
"max": 0.411,
"median": 0.201,
"p90": 0.253,
"mean": 0.196,
"min": 0.092,
"max": 0.286,
"samples": 20
}
},
"mount_nested_components": {
"median": 111.82,
"p90": 119.062,
"mean": 113.184,
"min": 107.991,
"max": 122.338,
"samples": 20,
"unmount": {
"median": 13.151,
"p90": 14.278,
"mean": 13.005,
"min": 11.269,
"max": 14.454,
"samples": 20
}
},
"mutate_component_props": {
"median": 128.921,
"p90": 135.131,
"mean": 129.951,
"min": 123.638,
"max": 144.569,
"samples": 20,
"unmount": {
"median": 13.973,
"p90": 16.402,
"mean": 14.368,
"min": 12.345,
"max": 16.669,
"samples": 20
}
},
"remount_component_tree": {
"median": 487.153,
"p90": 509.412,
"mean": 489.175,
"min": 461.947,
"max": 515.111,
"samples": 20,
"unmount": {
"median": 57.912,
"p90": 65.122,
"mean": 59.324,
"min": 53.72,
"max": 76.936,
"samples": 20
}
}
Expand Down
Loading
Loading