Skip to content

perf: rerun the full benchmark on main and refresh the published results - #199

Merged
unadlib merged 6 commits into
mainfrom
docs/benchmark-rerun
Oct 10, 2026
Merged

unadlib merged 6 commits into
mainfrom
docs/benchmark-rerun

Conversation

@unadlib

@unadlib unadlib commented Oct 10, 2026

Copy link
Copy Markdown
Owner

Part of #168.

This PR reruns the full benchmark suite on main at ce0c25e, the source after #190, #192, #195 and #196 with the four scenarios of #198, and replaces every published figure with the results of that one batch. Until now the published results combined the batch of October 6 and 7 at 2ab58fc with the cells that #195 and #196 measured again and #198 added.

  1. Performance summary (perf-testing/reports/SUMMARY.md). Every result, table and limit comes from this batch, including the shared-process control, the process-order control, memory, the Immer plugin datasets and the local budget gate. The "changes since the previous batch" section now compares this batch with the previously published cells. The PR sections of perf: change the copy of a Set draft directly, and keep the changes of a Set draft that left its key #195, perf: read the type of a draft from its state when applying patches #196 and perf: add benchmark scenarios for Set reads, Map iteration, moved rows and unchanged collections #198 keep their comparisons with main, and their cross-library figures come from this batch. The history gains an entry for the replaced batch, and the archive index describes the new one.
  2. README. The performance section, the headline, the features list and the three tables.
  3. Website. The home page figures and features card, the intro, the performance page and the Comparison with Immer page.
  4. Benchmark guide. The process-order example in the fairness notes.
  5. Bundle sizes. The README table and the installation guide had not been measured since October 7, and perf: change the copy of a Set draft directly, and keep the changes of a Set draft that left its key #195 and perf: read the type of a draft from its state when applying patches #196 grew Mutative since then. Measured the README's way (esbuild 0.24.0, browser ESM, minified, Brotli), create alone bundles to 7.8 kB instead of 7.7 kB, and create with apply, current and original to 8.4 kB instead of 8.2 kB. Immer's figures are unchanged.

Results

Comparator Previously published This batch
Immer 11.1.18 595 / 16 / 3 of 614, 3.59 596 / 16 / 2 of 614, 3.58
Immer with its defaults 153 / 1 / 3 of 157, 6.66 154 / 0 / 3 of 157, 6.62
Mutative 1.3.0 543 / 70 / 1 of 614, 4.28 538 / 73 / 3 of 614, 4.27
Hand-written reducer 18 / 3 / 129 of 150, 0.12 18 / 3 / 129 of 150, 0.12

Cells faster / within 5% / slower, and the geometric mean of comparator time over Mutative time.

  • Headline figures. Matched settings stay at 3.6x; with each library's defaults the figure becomes 6.6x (was 6.7x).
  • Large arrays. 222 of 232 cases at 1,000 and 10,000 rows (4.7x).
  • Moves. Up to 1,051x instead of 1,213x: Immer's shift at 10,000 rows measured 7,426 µs instead of 8,605 µs.
  • With patches. Faster than Immer in 147 of 150 cases (3.3x) and 4.6x Mutative 1.3.0.
  • Cells slower than Immer. Now only the two class-wide-update cells with auto-freeze at 1,000 rows. The concat cell with freeze and patches on is no longer slower.
  • Cells slower than Mutative 1.3.0. Three: add and update-largeObject1 with freeze on at 100 rows, by 7–16%. In each of them the processes of both builds overlapped.

Changes against the previously published cells

  • Drift. Over all 614 cells, the previous/current geometric mean was 1.004 for Mutative, 1.008 for Immer, 1.007 for Mutative 1.3.0 and 1.003 for the hand-written reducer. The last three did not change, so the drift between the sessions is about 1%. 135 of Mutative's cells separated from the previous processes, 128 of them by less than 3%; Immer's and 1.3.0's cells separated as often.
  • add with auto-freeze at 100 rows. It was the only cell that moved by more than 10% and was slower in every process: 19.5 µs against 15.9 µs. Ten alternating rounds of bundles built from 2ab58fc and ce0c25e, with Mutative alone in each process, measured 18.9 µs against 19.4 µs with overlapping ranges, and 1.52 µs for both without freezing. So the fixes of fix: keep inserted base elements as they are and patch regrown indices of array drafts #190 and fix: harden apply() patch paths and fix mark copies, current() snapshots and async recipe types #192 did not slow it; the cell's processes varied by up to 1.3 times in both batches.

Checks

  • The batch ran on a clean main checkout: no changes before or after, production artifact bd8ff3a6…, the same as perf: read the type of a draft from its state when applying patches #196's. It took 7 h 13 min on the M1 Max, and all 21 steps succeeded. Each of the 2,400 timing processes validated its scenarios before timing.
  • The local budget gate (ci.mjs --self-control) passed all 222 decisions of the five groups; latency 0.875–1.189.
  • The website builds, and its link check passes. pnpm format --check and oxfmt --check of perf-testing pass.
  • No source, test or harness file changes.
  • The raw datasets stay in the ignored perf-testing/results/2026-10-10/, as for the previous batch. The archive policy's perf-reports-* release was not created.

Commits

  1. perf: record the benchmark of main at ce0c25e in the performance summary
  2. docs: refresh the README's benchmark figures from the benchmark of ce0c25e
  3. docs: refresh the website's benchmark figures from the benchmark of ce0c25e
  4. docs: refresh the process order example in the benchmark guide
  5. docs: refresh the Mutative bundle sizes in the README and the installation guide

@unadlib unadlib mentioned this pull request Oct 10, 2026
@github-actions

Copy link
Copy Markdown

Coverage after merging docs/benchmark-rerun into main will be

100.00%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   apply.ts100%100%100%100%
   array.ts100%100%100%100%
   constant.ts100%100%100%100%
   create.ts100%100%100%100%
   current.ts100%100%100%100%
   draft.ts100%100%100%100%
   draftify.ts100%100%100%100%
   error.ts100%100%100%100%
   index.ts100%100%100%100%
   interface.ts100%100%100%100%
   internal.ts100%100%100%100%
   makeCreator.ts100%100%100%100%
   map.ts100%100%100%100%
   original.ts100%100%100%100%
   patch.ts100%100%100%100%
   rawReturn.ts100%100%100%100%
   set.ts100%100%100%100%
   unsafe.ts100%100%100%100%
src/utils
   cast.ts100%100%100%100%
   copy.ts100%100%100%100%
   deepFreeze.ts100%100%100%100%
   draft.ts100%100%100%100%
   finalize.ts100%100%100%100%
   forEach.ts100%100%100%100%
   index.ts100%100%100%100%
   mark.ts100%100%100%100%
   marker.ts100%100%100%100%
   proto.ts100%100%100%100%

@github-actions

Copy link
Copy Markdown

Coverage after merging docs/benchmark-rerun into main will be

100.00%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   apply.ts100%100%100%100%
   array.ts100%100%100%100%
   constant.ts100%100%100%100%
   create.ts100%100%100%100%
   current.ts100%100%100%100%
   draft.ts100%100%100%100%
   draftify.ts100%100%100%100%
   error.ts100%100%100%100%
   index.ts100%100%100%100%
   interface.ts100%100%100%100%
   internal.ts100%100%100%100%
   makeCreator.ts100%100%100%100%
   map.ts100%100%100%100%
   original.ts100%100%100%100%
   patch.ts100%100%100%100%
   rawReturn.ts100%100%100%100%
   set.ts100%100%100%100%
   unsafe.ts100%100%100%100%
src/utils
   cast.ts100%100%100%100%
   copy.ts100%100%100%100%
   deepFreeze.ts100%100%100%100%
   draft.ts100%100%100%100%
   finalize.ts100%100%100%100%
   forEach.ts100%100%100%100%
   index.ts100%100%100%100%
   mark.ts100%100%100%100%
   marker.ts100%100%100%100%
   proto.ts100%100%100%100%

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant