penpot/common/dev/bench/component_sync_baseline.edn
alonso.torres d1104c608b ⚡ Make component sync about twice as fast
A profile of the benchmark worlds showed where a sync spends its time.

- The token sync of shapes that apply no tokens took 38% of a sync in
  the 1000-copy world: for every synced shape, generate-update-tokens
  walked every sync attribute, resolved its group and its token
  attributes, and only then found that neither the main shape nor the
  copy applies a token. It now returns at once, and the token
  attributes of each sync attribute are computed once, at load.
- A sync looks through every object of each page and component to
  find the copies it updates, so in a file with many shapes and few
  copies the search costs more than the sync itself. The new benchmark
  world main-edit-among-many-shapes syncs a main edit into 10 copies in
  a page with 20,000 plain rects; its baseline, measured before the
  change below, joins the recorded one with a note, and its result
  joins the golden master. generate-sync-container called the
  uses-assets? multimethod on every object through a lazy filter; the
  predicate is now chosen once per container (asset-user?) and the
  objects walked with reduce-kv into a vector, in the same order.
- compare-children searched the remaining children (d/seek) for every
  child out of place, and dropped a child taken out of order with a
  lazy remove, so a reorder took quadratic time and nested one lazy
  sequence per child taken. It now looks children up by their
  reference or id (index-positions) and skips those taken out of order
  when the walk reaches them, with the same callbacks in the same
  order. The sync of a reorder stays slow: when a main with 1,000
  children moves its first one to the end, each copy still gets 999
  moves (F85), and applying them is what costs (4.75 s to 4.26 s for
  10 copies). Fewer moves change what a sync writes, so that waits for
  Phase 4c.

No behavior change: the golden masters and the suites are unchanged.

Benchmark (lower quantile, ms; baseline / now / ratio):
  deepest-edit-through-three-levels    35.2 /  18.6 / 0.53
  edit-through-a-chain-of-dependents   68.9 /  36.4 / 0.53
  library-update-into-copies           83.1 /  43.0 / 0.52
  main-edit-among-many-shapes           4.8 /   2.3 / 0.48
  main-edit-into-10-copies              1.8 /   0.9 / 0.48
  main-edit-into-100-copies            15.4 /   8.4 / 0.54
  main-edit-into-1000-copies          153.1 /  90.1 / 0.59
  variant-switch-into-copies           32.1 /  20.0 / 0.63
(main-edit-among-many-shapes: baseline measured before the search
change.)

AI-assisted-by: claude-opus-5-5
2026-10-28 15:53:06 +01:00

28 lines
1.0 KiB
Clojure

;; Component sync benchmark baseline, written by `scripts/bench-components record`.
{:commit "1e6615a132",
:date "2026-10-09",
:machine
{:cpu "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz",
:cores 12,
:os "Linux amd64",
:jvm "26.0.2"},
:results
{:deepest-edit-through-three-levels
{:mean-ms 36.343, :lower-q-ms 35.215, :passes 4},
:edit-through-a-chain-of-dependents
{:mean-ms 75.119, :lower-q-ms 68.931, :passes 3},
:library-update-into-copies
{:mean-ms 89.295, :lower-q-ms 83.083, :passes 1},
;; added in Phase 3 (2026-10-14), measured on the code before the object
;; scan of a sync changed, on the same machine
:main-edit-among-many-shapes
{:mean-ms 5.465, :lower-q-ms 4.837, :passes 1},
:main-edit-into-10-copies
{:mean-ms 1.847, :lower-q-ms 1.79, :passes 1},
:main-edit-into-100-copies
{:mean-ms 16.234, :lower-q-ms 15.415, :passes 1},
:main-edit-into-1000-copies
{:mean-ms 158.728, :lower-q-ms 153.086, :passes 1},
:variant-switch-into-copies
{:mean-ms 33.1, :lower-q-ms 32.052, :passes 1}}}