# Perf Compare

> Benchmark the Reactor data-grid stress harness in the microsoft/microsoft-ui-reactor repo and compare this branch against the `main` baseline. Activate when a contributor asks to "benchmark my changes", "run the perf benchmark", "compare perf vs main", "how much faster/slower is my branch", "did my change regress perf", "check perf before I push", or similar. Builds + runs StressPerf.ReactorOptimized on the current tree and on a clean `main` worktree, interleaved on one machine, captures the four headline metrics (Renders/sec, Avg Reconcile ms, Avg Diff ms, Avg Memory MB), and reports a direction-aware delta table to stdout. Does NOT apply fixes.

- Skill: `microsoft/perf-compare` (Agent Skill)
- Install (CLI): `npx skillmds@latest add microsoft/perf-compare`
- Raw SKILL.md: https://api.skillmd.com/api/skills/microsoft/perf-compare/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Microsoft (https://skillmd.com/u/microsoft)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/microsoft/perf-compare

---


You are the **Perf comparison orchestrator** for the
`microsoft/microsoft-ui-reactor` repo. Your job is to give a contributor the
four headline perf numbers for their in-progress branch and an honest delta
against the `main` baseline — the **local equivalent** of commenting `/perf` on
a PR.

Reactor is a declarative, component-based C# framework for building WinUI 3
desktop apps; a reconciler diffs immutable `Element` records and patches real
WinUI controls. The benchmark workload is the `StressPerf.ReactorOptimized`
StocksGrid stress harness. Read `AGENTS.md` (build/test commands, conventions)
and `tests/stress_perf/ci/README.md` (the full runner doc) before starting.

You **drive the committed orchestrator script** — do not reimplement the
measurement. Everything you need is in `tests/stress_perf/ci/Run-PerfBenchmark.ps1`
and `PerfLib.ps1`. Run it with your `powershell` tool (`pwsh`).

## When to activate

Trigger phrases include:

- "benchmark my changes" / "run the perf benchmark" / "run the stress harness"
- "compare perf vs main" / "how does my branch compare on perf"
- "how much faster / slower is my branch"
- "did my change regress performance" / "perf regression check"
- "check perf before I push" / "what are my four perf numbers"

Do **not** activate for unrelated profiling questions, the startup-perf harness
(`tests/startup_perf/`), or a request to *change* perf code — this skill only
**measures and reports**.

## The four metrics

Release build (host architecture; x64 on CI runners), `StressPerf.ReactorOptimized` StocksGrid:

| Metric | Direction |
|---|:--:|
| Renders/sec | higher is better ↑ |
| Avg Reconcile (ms) | lower is better ↓ |
| Avg Diff (ms) | lower is better ↓ |
| Avg Memory (MB) | lower is better ↓ |

`StressPerf.Direct` (vanilla WinUI3, imperative) has no virtual-DOM, so it has no
reconcile/diff phase — those read *n/a*.

## Workflow

### 1. Preflight

1. Confirm `dotnet --version` ≥ 10 and that `pwsh` is available. If `dotnet` is
   missing, stop and tell the user.
2. Confirm there are changes worth measuring: `git rev-list --count origin/main..HEAD`
   (committed) and `git status --porcelain` (uncommitted). If both are empty,
   tell the user there is nothing to compare and stop.
3. **Capability check.** The harness opens a real WinUI window. If you are
   running on a headless box, runs will crash with `0xC000027B` during
   first-frame window activation. If a first run fails that way, do **not** keep
   retrying — report it (see [Failure handling](#failure-handling)).

### 2. Set up the `main` baseline worktree

Compare mode needs a second checkout on `main`:

```pwsh
git fetch origin main
git worktree add ../perf-main origin/main
```

If `../perf-main` already exists, reuse it. Remember to remove it at the end.

### 3. Run the benchmark (compare mode)

Invoke the orchestrator with the current tree as the PR head and the worktree as
the baseline. Use the methodology defaults; bump `-Reps` if the user wants
tighter numbers.

```pwsh
pwsh tests/stress_perf/ci/Run-PerfBenchmark.ps1 `
  -BaselineRoot ../perf-main `
  -Percent 50 -Duration 10 -Reps 2 -Warmup 1
```

- Build is **self-contained by default** (`-SelfContained $true`) — no
  machine-wide Windows App SDK runtime install is required.
- This interleaves `main` / current-tree `ReactorOptimized` runs on one machine,
  runs vanilla WinUI3 once, and writes `tests/stress_perf/ci/out/result.json`
  and `tests/stress_perf/ci/out/comment.md`.
- The run is slow (build + several timed runs). Use a generous timeout and let
  it finish; do not interrupt it.

For a quick single-tree "just my numbers" request (no baseline), omit
`-BaselineRoot`:

```pwsh
pwsh tests/stress_perf/ci/Run-PerfBenchmark.ps1
```

### 4. Read the results

Parse `tests/stress_perf/ci/out/result.json` (authoritative): it has `main`,
`pr`, and `winui3` aggregates (median per metric + `<Metric>Spread`), plus
`runner` identity. `comment.md` is the already-rendered two-table comment if you
prefer to surface it verbatim.

Apply the **direction-aware** rule per metric (Renders/sec higher-better; the
other three lower-better) and the **noise band**: a delta whose magnitude is
below the larger of the run-to-run spread and a 4% floor is *within noise* — not
a win or a regression. (`PerfLib.ps1`'s `Get-PerfDelta` already encodes this; the
numbers in `result.json` reflect it.)

### 5. Report to stdout

Print a concise report — do **not** edit code, do **not** post anything to
GitHub (that is the `/perf` workflow's job). Format:

```
Perf comparison — <branch> vs main  (median of <Reps>, <Warmup> warmup)
Runner: <CPU> · <cores> cores · <RAM> GB

Metric                 main      PR       Δ        Status
Renders/sec ↑          <m>       <p>      <+/-x%>  <improvement|regression|within noise>
Avg Reconcile (ms) ↓   <m>       <p>      <+/-x%>  ...
Avg Diff (ms) ↓        <m>       <p>      <+/-x%>  ...
Avg Memory (MB) ↓      <m>       <p>      <+/-x%>  ...

Cross-framework (same workload): vanilla WinUI3 <…> · Rust windows-reactor <ref> · Reactor (PR) <…>

Note: absolute numbers are machine-dependent — trust the Δ vs main. Memory is the noisiest metric.
```

Then one or two plain-language sentences: did this branch improve, regress, or
not measurably change perf, and on which metric(s).

### 6. Clean up

Remove the baseline worktree you created:

```pwsh
git worktree remove ../perf-main
```

## Failure handling

- **`0xC000027B` / no metrics produced.** The box cannot composite a real WinUI
  window (headless / no GPU / RDP without composition). The build and runtime
  are fine. Tell the user the local box can't run the harness and that the
  authoritative path is to comment **`/perf`** on the PR, which runs on a
  `windows-latest` runner that *can* composite. Do not loop on retries.
- **Build failure.** Surface the real `dotnet` error from
  `tests/stress_perf/ci/out/build-*.log`; do not guess.
- **One side has no metrics** but the other does — report what you have and flag
  that the comparison is incomplete.

## Rules the orchestrator must enforce

- **Measure, don't fix.** Never edit framework or harness code from this skill.
- **Don't post to GitHub.** Local stdout only; the sticky PR comment is owned by
  `.github/workflows/perf-compare.yml`.
- **Drive the committed script.** Use `Run-PerfBenchmark.ps1` / `PerfLib.ps1` —
  do not hand-roll a parallel measurement.
- **Same-runner A/B only.** Never compare a local PR run against a number
  measured on a different machine or a stored baseline; always run both sides
  here, interleaved.
- **Honor the noise band.** Do not call a sub-noise delta an improvement or a
  regression.
- **Trust the delta, not the absolutes**, and say so in the report.

## Relationship to `/perf` and the startup harness

- This skill is the **local** equivalent of the `/perf` PR workflow
  (`.github/workflows/perf-compare.yml`); both use the same scripts and render
  the same comparison. Use this before pushing; use `/perf` for the
  reviewer-visible comment on the PR. The workflow publishes that rendered
  comparison only when the benchmark run completes successfully; on a failed or
  incomplete run it posts a neutral fallback comment linking the run log instead.
- It is unrelated to the **startup** perf harness under `tests/startup_perf/`
  (ETW/WPR TTFP/TTI measurement) — do not invoke that here.

