# Startup benchmark Run the Windows release harness from the repository root: ```powershell ./tools/benchmark.ps1 -Runs 100 -Warmup 10 -Output benchmarks/latest.jsonl ``` Each measured launch appends one fixed-version JSON object after the harness has delivered and verified `U+E000`, observed the following Scintilla paint, waited two input-free seconds after `FullyReady`, sampled private working set, and closed the child process. Warmup launches are not written. The summary reports sorted p50 and p95 values for all nine startup milestones. Use `-EnforceReference` on the documented reference machine. It fails when warm rendered-input TTI p50 is at least 25,000 microseconds or p95 is at least 40,000 microseconds. Compare two saved distributions with: ```powershell cargo run --release --bin fastpad-bench -- compare baseline.jsonl candidate.jsonl ``` A milestone is reported as a regression only when candidate p95 increases by at least the larger of 2,000 microseconds or 10 percent and the deterministic 10,000-resample bootstrap 95 percent confidence interval for the p95 delta excludes zero. ## Markdown preview Preview costs are measured in-process with ignored tests: ```powershell cargo test --release --test markdown_preview -- --ignored --test-threads=1 --nocapture ``` Targets: preview open on 100 KB < 50 ms p95; one-paragraph update in a 1 MB document < 2 ms p95; keystroke cost with the side-by-side preview open within 10% (+100 µs) of no preview; no private memory growth across repeated open/close cycles (median of five closes within 2 MB of a reference close taken after two warm-up cycles). The first open loads Direct2D, DirectWrite, Direct3D, and the GPU driver for the rest of the session, about 45 MB of private bytes that closing does not return. Startup with a Markdown file is compared against the pre-preview baseline with `./tools/benchmark.ps1 -LaunchFile benchmarks/fixtures/sample.md` on both builds and `fastpad-bench compare`. A `--launch-file` run types its benchmark character into the initial Untitled tab, not into the launch file, which opens in a tab of its own; the rendered-input event is still required, but the buffer check is skipped, and the harness marks the Untitled tab saved before closing so no save prompt blocks the exit. Release binary size (`FastPad.exe`, release profile with `release-package`, as `tools/package.ps1` builds it): 511,488 bytes before the preview, 957,440 bytes with it (+445,952 bytes). A plain `cargo build --release` binary with the preview is 960,512 bytes. HTML rendering (phase 1), measured 2026-09-17 on the same machine against `origin/main` (ef32e96): - HTML-heavy 100 KB preview open: 16.9 ms p95 (the Markdown-only 100 KB open measured 17.1 ms in the same run). - One-paragraph update in a 1 MB document: 1.67 ms and 1.50 ms p95 in two quiet runs; the baseline build measured 1.62 ms. A run while other work shared the machine measured 2.27 ms. - Update inside a `
` spanning a 1 MB document: 1.38 s p95. No target: the element makes the whole document one block, so every edit parses it again and lays out that one block in full. - SVG decode of `assets/fastpad-icon.svg` at 256 px: median 8.0 ms, max 9.6 ms (a second run measured median 13.1 ms, max 17.3 ms). - Release binary with `release-package`: 1,008,128 bytes before, 1,111,552 bytes after (+103,424 bytes). - Startup with `benchmarks/fixtures/sample.md` (100 runs each, `fastpad-bench compare`): no milestone regressed; every p95 delta was between -4.9 ms and -1.3 ms. Deviations from the design spec, recorded without a ruling: - The image cache is keyed by path only, not by path and modification time. An image edited while its document is shown keeps the decoded pixels; it refreshes when the preview is reopened. ## Note sidebar `library-scan` also times the sidebar's pure work over the generated notebook (the median of five runs each): - `tree_build_ms`: `NoteTree::build` over every note. The target is under 20 ms for 10,000 notes. - `tree_rows_expanded_ms`: flattening with every folder expanded (500 notes per folder). The target is under 16 ms. - `quick_open_ms`: one Ctrl+P keystroke (`nt 12`) scored over every note. The target is under 5 ms. `--enforce-reference` fails the run when any of them reaches its target. Startup with the sidebar is measured with `--notes-folder DIR --sidebar-view notebook` against the same folder with `--sidebar-view none`, and compared with `fastpad-bench compare`. No milestone may regress, and the idle private working set with a 10,000-note notebook may grow by at most 1 MB over the `feat/note-library` build with the same notes. Generate that build's folder with its own `library-scan DIR --count 10000`: the sidebar build writes a version 2 `library.ini`, which the library build reports as damaged and never rewrites. Run the two builds back to back, in pairs, because the machine drifts between runs. ## Note search `library-scan` also generates a second notebook (10,000 notes of about 4 KB, 500 per folder) in a scratch folder under the build's `target` directory (`target\bench-notes`, git-ignored), not under `%TEMP%`, whose antivirus scanning of fresh files would dominate the timings. It warms the OS cache with one untimed search and times the text search of the note search spec (§14), as the median of five runs each: - `text_search_first_batch_ms`: from starting the worker to its first batch, for a phrase in every note. The target is under 50 ms. - `text_search_full_ms`: the whole search for a phrase in one note in fifty (200 hits, below the 500-note cap), so every note is read. The target is under 400 ms. - `text_search_batch_ui_ms`: the UI thread's part of one batch, inserting 50 hits into 450 shown results by binary search. The `InvalidateRect` that follows only queues a paint and isn't timed. The target is under 2 ms. `--enforce-reference` fails the run when any of them reaches its target; without it the numbers are only printed. A full search at or above 400 ms on the reference machine is the point where §14 says an index would be justified. The note search build's idle private working set, with the Search view open on a 10,000-note notebook and nothing typed, may grow by at most 0.5 MB over the `feat/note-sidebar` build with the same notebook. The `regex` crate may add at most 1.5 MB to the release exe.