Performance

This page presents performance benchmarks for Panache, comparing its formatting and linting speed against popular alternatives like Prettier, Pandoc, rumdl, mdformat, Yamark, mado, markdownlint, and markdownlint-cli2 on real Quarto and Markdown documents, and its language-server memory use against Marksman. The benchmarks highlight Panache’s efficiency and suitability for editor and repository-wide work.

Overview

Panache is designed for speed without compromising on correctness. Built in Rust and compiled to native code, it delivers fast formatting with zero startup overhead. In this document, we present benchmarks comparing Panache’s formatting and linting performance against popular alternatives like Prettier, Pandoc, rumdl, mdformat, Yamark, mado, markdownlint, and markdownlint-cli2 on a realistic corpus of Quarto and Markdown documents. R Markdown is outside the current benchmark corpus.

We have split the benchmarks into two suites: per-document benchmarks that run each formatter on each document in the corpus individually, and repository-wide benchmarks that run formatters on entire repositories of tracked documents. The former highlights raw formatting speed on a variety of real-world documents; the latter captures the overhead of processing multiple files in a single run, which is more representative of on-save formatting or large-scale linting. Caches are disabled for all benchmarks to show worst-case performance.

Yamark runs with an empty configuration and external code formatters disabled (--config /dev/null --skip-embedded-formatters). Its default wrapping and style settings apply. Tools differ in supported syntax and formatting policy, so these timings compare their respective workloads. Failed repository runs have null timings in the data and are excluded from the plots.

The numbers on this page are produced by the scripts in benches/ and read from the JSON files written next to this document. The benchmark chunks on this page are intentionally not executed during preview or render, so small content edits reuse the existing JSON files instead of rerunning the benchmarks. Refresh the benchmark data explicitly with the commands at the bottom of this page, then delete docs/_freeze/guide/performance/ and re-render if you want the page to pick up newly generated results.

Formatting

Single-Document

In Figure 1, we compare formatting time per document across panache, Prettier, Pandoc, rumdl, mdformat, and Yamark. Each dot is one document; the y-axis shows time relative to panache (×, lower is faster). Panache sits at 1× by construction (dashed baseline); points above it are slower. Hover a point to see the absolute wall-clock time in milliseconds. Formatters are ordered left-to-right from fastest to slowest on average.

Figure 1: Formatting time per document, comparing panache to Prettier, Pandoc, rumdl, mdformat, and Yamark. Each dot is one document; the y-axis shows time relative to panache (×, lower is faster).

Repository-Wide

In Figure 2, we compare formatting time across panache, Prettier, rumdl, mdformat, and Yamark on tracked Markdown files from several repositories.

Figure 2: Repository-wide formatting benchmarks on standard Markdown repos, comparing panache to Prettier, rumdl, mdformat, and Yamark. Each dot is one repo/tool pair; the y-axis shows time relative to panache within that repo (×, lower is faster).

In Figure 3, we compare formatting time across panache, rumdl, and Yamark on tracked .qmd files from several Quarto repositories.

Figure 3: Repository-wide formatting benchmarks on Quarto repos. Each dot is one repo/tool pair; the y-axis shows time relative to panache within that repo (×, lower is faster).

Each dot is one repo/tool pair. Panache sits at 1× by construction (dashed baseline), and points above it are slower on that repository. Hover a point to see the absolute wall-clock time in milliseconds and the corpus size.

Linting

Single-Document

In Figure 4, we compare linting time per document across panache lint, rumdl check, mado check, markdownlint, and markdownlint-cli2. Each dot is one document; the y-axis shows time relative to panache lint (×, lower is faster). Panache sits at 1× by construction.

Figure 4: Linting time per document, comparing panache lint to rumdl check, mado check, markdownlint, and markdownlint-cli2. Each dot is one document; the y-axis shows time relative to panache lint (×, lower is faster).

Repository-Wide

This suite benchmarks linting on the same standard Markdown repositories as the formatting comparison, using panache lint, rumdl check, mado check, markdownlint, and markdownlint-cli2. In Figure 5, each dot is one repo/tool pair; the y-axis shows time relative to panache lint within that repo (×, lower is faster).

Figure 5: Repository-wide linting benchmarks on standard Markdown repos, comparing panache lint to rumdl check, mado check, markdownlint, and markdownlint-cli2. Each dot is one repo/tool pair; the y-axis shows time relative to panache lint within that repo (×, lower is faster).

In Figure 6, we compare linting time across panache and rumdl on tracked .qmd files from several Quarto repositories.

Figure 6: Repository-wide linting benchmarks on Quarto repos. Each dot is one repo/tool pair; the y-axis shows time relative to panache lint within that repo (×, lower is faster).

Language-Server Memory

This Linux-only benchmark compares the complete process-tree memory used by Panache and Marksman. It opens the five largest tracked Markdown files in a pinned revision of the Rust Book, requests diagnostics and navigation features, and then performs 1,000 fixed-width reference-label renames. Each rename is followed by a definition request, which forces the server to observe the updated label index.

The runner samples RSS and proportional set size (PSS) from /proc every 150 milliseconds. It records a milestone after the process tree remains below 5% of one CPU core for five seconds, and reports medians from three fresh server processes run in alternating order.

Figure 7: Median whole-process-tree RSS during the language-server workload. Each milestone is measured after quiescence, except peak, which is the largest 150 ms sample from the complete session.

Marksman used 8.2 times as much RSS as Panache after the files were open and 7.5 times as much after edit churn. Panache’s median RSS rose from 16.8 MB to 21.5 MB during the 1,000 edits; the three runs finished within 0.2 MB of one another. This workload therefore did not reproduce unbounded growth in Panache.

The comparison measures default architecture, not identical internal work. Marksman indexes the Markdown workspace eagerly, while Panache analyzes open documents and discovers project relationships lazily. Both servers receive the same open, edit, and navigation sequence, while diagnostics use each server’s advertised pull or push mode. Their memory totals reflect those different strategies.

Reproducing

All benchmarks are reproducible. The formatting and linting comparisons require hyperfine; the language-server memory comparison requires Linux with /proc and Marksman. The scripts in benches/ are designed to be run from the repository root:

# Download test documents (idempotent)
cd benches/documents && ./download.sh && cd ../..

# Outside devenv: install Yamark and record its version
# The development environment already provides both.
uv tool install yamark==0.3.0
export PANACHE_BENCH_YAMARK_VERSION=0.3.0

# Run comparison benchmark and write JSON
bash benches/compare_all.sh --json --out docs/guide/performance_data.json

# Run repository-wide Markdown formatting benchmark
bash benches/compare_repo_suite.sh --mode format --track markdown --out docs/guide/performance_repo_markdown_format_data.json

# Run repository-wide Quarto formatting benchmark
bash benches/compare_repo_suite.sh --mode format --track quarto --out docs/guide/performance_repo_quarto_format_data.json

# Run repository-wide Markdown lint benchmark
bash benches/compare_repo_suite.sh --mode lint --track markdown --out docs/guide/performance_repo_markdown_lint_data.json

# Run repository-wide Quarto lint benchmark
bash benches/compare_repo_suite.sh --mode lint --track quarto --out docs/guide/performance_repo_quarto_lint_data.json

# Run per-document lint benchmark
bash benches/compare_lint_single.sh --out docs/guide/performance_lint_single_data.json

# Run the Linux language-server memory benchmark
task bench:lsp-memory

# Or run the human-readable text variant
bash benches/compare_all.sh

# Re-render this page (uses freeze cache by default)
quarto render docs/guide/performance.qmd

# Force the benchmark to re-run by invalidating the freeze cache
rm -rf docs/_freeze/guide/performance
quarto render docs/guide/performance.qmd