Skip to content

Parser Benchmarks

V2 benchmark reports preserve source revisions, compiler flags, output checks, and raw measurements. Results describe the recorded workloads and machines.

Native engine comparison

The current local cores, v2 e93394e and v1 4e15141, were rerun with explicit syntax and renderer flags on the original 57 documents (37–113,609 UTF-8 bytes). Each scored column uses the same input set and equivalent HTML for every included engine. Speed relative to v2: higher is faster; v2 = 1.00×.

EngineFresh, 14 agreeing across sixReuse, same 14Fresh, 50 agreeing across fiveReuse, same 50
Ferromark v21.00×1.00×1.00×1.00×
OX-Content original1.00×0.98×
Ferromark v10.71×0.71×0.53×0.54×
md4c0.26×0.21×0.31×0.28×
pulldown-cmark0.46×0.38×0.42×0.39×
Bun native bun_md0.17×0.12×0.18×0.16×

On the all-six agreement subset, v2 runs at 1.000× OX's throughput fresh and 1.018× with reuse. On the broader five-engine subset it runs at 1.90× v1's speed fresh and 1.85× with reuse. These are measured corpus aggregates; ratios close to 1.00 are not evidence of a universal lead.

The all-six subset contains ten comments and four plain-prose views. The broader five-engine subset also covers technical docs, linked encyclopedia excerpts, references, and READMEs. Both agreeing subsets span 37–80,966 bytes. OX has no score in the five-engine columns: its original renderer cannot disable heading IDs, callouts, inline TOCs, or fence metadata cleanup. Its 39 heading-ID-only differences are not normalized away. The seven cases outside five-engine agreement remain measured as diagnostics, including task CSS and link/content differences.

All engines parse and render natively. The CommonMark lane disables optional syntax; the extension lane enables only tables, strikethrough, and task lists. It is not full GFM. Bare URL autolinking, footnotes, frontmatter, line comments, definition lists, MDX, and optional renderer extras are off where configurable. Raw HTML passes through; these explicit benchmark settings differ from library defaults.

Fresh includes parser/renderer setup, complete processing, owned output, and destruction. Reuse retains state where the public API permits; Bun's native bun_md still uses its fresh owned-output API. No JavaScript, WASM, process startup, file I/O, or output normalization is timed. One macOS arm64 executable, one Rust compiler, shared mimalloc and the same pinned dependency lock; three process rounds with six rotating windows per round. Small differences on this shared workstation are not established significance.

Selected groups, fresh, using only the five-engine agreement subset and the same speed scale:

Group, five-engine agreementNV1md4cpulldownBun native
<512 B100.71×0.26×0.55×0.26×
32–128 KiB70.57×0.29×0.37×0.10×
comments110.74×0.28×0.55×0.25×
technical-docs210.51×0.35×0.40×0.18×
plain-prose40.65×0.26×0.30×0.06×

Full results and per-document timings, exact flags, and HTML differences include raw measurements, source hashes, and reproducible configuration. The previous matched-flags run and original six-engine run remain historical evidence. The full 207-case before/after suite uses a separate harness and reports all four stages.

The historical two-engine broad Markdown comparison measures 57 cases from 37 bytes to 114 KB: short comments, real documentation, and Wikipedia-derived prose. It separates input size, content, output agreement, and fresh/reused lifecycles. Full tables and raw data and the earlier synthetic diagnostic comparison use the same pinned parser binaries.