Skip to content

Add renderer/style-recalc benchmarks, findings doc and layout profiler - #3795

Closed
mattgperry wants to merge 2 commits into
mainfrom
renderer-benchmarks-findings
Closed

Add renderer/style-recalc benchmarks, findings doc and layout profiler#3795
mattgperry wants to merge 2 commits into
mainfrom
renderer-benchmarks-findings

Conversation

@mattgperry

Copy link
Copy Markdown
Collaborator

Summary

  • dev/html/public/benchmarks: interactive benchmarks comparing renderer strategies (inline style writes, adopted stylesheet, registered CSS variables, WAAPI, CSS transitions, GSAP) in benign and hostile style-recalculation environments, plus style-recalc-harness.html, which isolates which selector/environment patterns ([style*=…] attribute selectors, inherited properties, @container style() queries) turn a single style-attribute write into a full-page recalculation.
  • FINDINGS.md: measured results and methodology — what triggers recalculation bombs, renderer comparisons, and measurement pitfalls (vsync floor masking real cost, rAF throttling in occluded tabs, tween semantics differences between libraries).
  • dev/react/profile-layout.mjs: Playwright/CDP V8 sampling profiler for the layout stress examples, reporting projection-attributed JS time and per-function self-time hotspots. Works with both the current minimal recordStats and newer reporter-returning versions.
  • layout-stress-deep example: deep-tree (depth 30 × 40 chains) layout animation stress test exercising per-ancestor projection cost.

No library code changes — dev/test tooling only.

Test plan

  • Benchmarks and harness run in Chrome (results documented in FINDINGS.md)
  • Profiler smoke-tested against layout-stress-deep and layout-stress-transform on main

- dev/html/public/benchmarks: interactive benchmarks comparing renderer
  strategies (inline style, adopted stylesheet, registered CSS
  variables, WAAPI, CSS transitions, GSAP) in benign and hostile
  style-recalculation environments, plus a harness that isolates which
  selector/environment patterns turn a single style-attribute write
  into a full-page recalculation.
- FINDINGS.md: measured results and methodology, including measurement
  pitfalls (vsync floor, rAF throttling in occluded tabs, tween
  semantics) and layout projection profiling notes.
- dev/react/profile-layout.mjs: Playwright/CDP V8 sampling profiler for
  the layout stress examples, reporting projection-attributed JS time
  and per-function hotspots.
- layout-stress-deep example: deep-tree (30x40) layout animation stress
  test exercising per-ancestor projection cost.

Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds interactive renderer/style-recalculation benchmarks, a findings document, a CDP layout profiler, and a deep projection stress example. One configured renderer currently prevents the benchmark suites from completing.

  • Adds shared inline-style, stylesheet, WAAPI, CSS-transition, and GSAP benchmark strategies.
  • Adds hostile style-invalidation harnesses and documents measured renderer/projection findings.
  • Exposes development statistics and adds a Playwright/V8 sampling profiler.
  • Adds a depth-heavy React layout-projection example.

Confidence Score: 4/5

The benchmark tooling should not merge until the nonexistent varEffect dependency is replaced or implemented, because every advertised automatic renderer suite stops at that strategy.

The new benchmark pages all configure a variable renderer whose bind path invokes an API absent from the loaded Motion entrypoint, preventing complete results; the remaining clock-guidance issue is non-blocking.

Files Needing Attention: dev/html/public/benchmarks/renderer-bench-lib.js, dev/html/public/benchmarks/style-recalc-harness.html

Important Files Changed

Filename Overview
dev/html/public/benchmarks/renderer-bench-lib.js Adds shared strategies and metering, but the configured variable strategy calls a nonexistent export and stops benchmark execution.
dev/html/public/benchmarks/style-recalc-harness.html Adds configurable style-invalidation experiments; its frame timing also uses the nonsynchronized clock prohibited by repository guidance.
dev/react/profile-layout.mjs Adds a standalone CDP/V8 sampling profiler with an explicit fallback when the current stats recorder returns no reporter.
dev/react/src/App.tsx Exposes the existing debug statistics recorder to local profiling automation.
dev/react/src/examples/layout-stress-deep.tsx Adds a deep projection-tree workload that toggles shared width through a click-driven layout animation.
dev/html/public/benchmarks/FINDINGS.md Documents benchmark methodology and conclusions, although the newly committed variable-renderer path cannot currently complete as written.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  P["Benchmark page"] --> R["Shared runner"]
  R --> B["Bind selected strategy"]
  B --> A["Animate values"]
  A --> M["Frame meter"]
  M --> T["Results table"]
  P2["Layout profiler"] --> D["dev/react example"]
  D --> C["Click layout stress case"]
  C --> V["CDP V8 profile"]
  V --> O["JSON profile report"]
Loading

Reviews (1): Last reviewed commit: "Add renderer/style-recalc benchmarks, fi..." | Re-trigger Greptile

const cleanups = []
for (const box of boxes) {
const values = makeValues(props)
cleanups.push(varEffect(box, values))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Undefined variable renderer API

When any automatic benchmark reaches the var strategy, this calls varEffect, but framer-motion/dom exports no such API, causing a TypeError that stops the run before the remaining strategies execute or a complete results table is produced.

Knowledge Base Used: Repo Build Tooling

Comment on lines +316 to +320
const preRender = () => {
renderStart = performance.now()
}
const postRender = () => {
renderMs += performance.now() - renderStart

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unsynchronized benchmark clock

These frame-step measurements, along with the corresponding flush and setup timings in style-recalc-harness.html, use performance.now() instead of the repository-required time.now(), weakening timestamp consistency within Motion's synchronized frameloop.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Review finding: the benchmarks destructured varEffect from
window.Motion, but that only exists on an experimental branch - on
main the var strategies threw at bind time. The lib now falls back to
a standalone implementation of the same mechanism (per-value
registered custom property with inherits: false, style points at
var(--name) once, per-frame writes touch only the custom property),
preferring Motion.varEffect when present.

Co-authored-by: Cursor <cursoragent@cursor.com>
@mattgperry

Copy link
Copy Markdown
Collaborator Author

Moved to the private motiondivision/motion-research repo (benchmarks made standalone via pinned esm.sh imports, findings updated with per-item attribution corrections). Keeping research artifacts out of library history.

@mattgperry mattgperry closed this Aug 18, 2026
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