Benchmarks
Wall-clock speed of badness against comparable tools, measured with
hyperfine: the formatter against
tex-fmt and
latexindent, and the linter
against the classic TeX Live checkers lacheck
and chktex.
These numbers measure speed only, never output or diagnostic equivalence, and the tools do genuinely different amounts of work:
latexindentis a Perl script that parses LaTeX into a tree and reflows it according a set of highly configurable rules. It is the most featureful formatter here, but also the slowest.tex-fmtbreaks overfull lines greedily but does not reflow: it won’t rewrap lines that already fit, so it moves far less text thanbadness, which reflows each paragraph to the target width.- Among the linters,
lacheckis a small classic checker,chktexis regex-driven, andbadness lintdoes a full CST parse plus its rule set.
The absolute milliseconds are the real latencies—what you actually wait—but they are machine- and run-dependent. And because the tools do different work, a cross-tool difference is not a claim that one tool is faster at the same job.
The figures below are regenerated manually with task bench and committed as a
machine-readable artifact (benches/benchmark_results.json); they are never
re-measured when this site is built or in CI.
Formatter
How the formatter is measured
Each tool is invoked exactly as a user would pipe a document through it:
| Tool | Invocation |
|---|---|
badness | badness format --no-config --stdin-filepath bench.tex |
tex-fmt | tex-fmt --stdin |
latexindent | latexindent -g /dev/null - |
The corpus is real LaTeX: a committed small.tex baseline plus larger documents
(cv.tex, masters_dissertation.tex, phd_dissertation.tex) fetched by
benches/documents/download.sh from a pinned tex-fmt release. Documents
badness cannot yet format (parser diagnostics) are skipped, as are comparison
tools missing from PATH.
The whole-project benchmark below measures recursive folder formatting
rather than a single file: each tool walks a real multi-file LaTeX thesis (the
pinned kks32/phd-thesis-template, its .tex fragments) and formats every
file in read-only --check mode—the folder analog of the stdin -> stdout runs
above (full formatting work, nothing written). Only badness and tex-fmt
appear there: latexindent has no recursive directory mode, so it is excluded
from that comparison by design.
| Tool | Invocation |
|---|---|
badness | badness format --check <dir> |
tex-fmt | tex-fmt --check --recursive <dir> |
The folder benchmark runs against a throwaway copy of the fetched project so
both tools walk an identical, un-gitignored, .tex-only tree (badness format
is .tex-only, while tex-fmt would otherwise also touch .bib/.cls). Any
file badness cannot format yet is dropped from both tools, keeping the
comparison symmetric. This is a different mode from the single-file runs, so
read its ratio on its own terms, not against them.
Setup
- badness:
0.7.0 - tex-fmt:
0.5.7 - latexindent:
3.24.7 - backend: hyperfine (min runs: 3)
- host: linux/x86_64, Intel(R) Core(TM) Ultra 7 155U
- generated: 2026-07-10T00:50:18Z
Single-file results
badness. Each dot is one document formatted by one tool; the vertical position is mean wall-clock time as a ratio to badness on a log scale, so badness lies on the dashed baseline at 1, faster tools fall below it and slower tools rise above. Color distinguishes documents; hover a dot for the exact millisecond figures.Data table
small.tex (baseline) (1233 bytes, 48 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 2.4545 | 0.8573 | 5.6567 | baseline |
| tex-fmt | 2.2980 | 0.8883 | 4.9063 | 1.1× faster |
| latexindent | 84.7619 | 77.0526 | 98.6430 | 34.5× slower |
cv.tex (6273 bytes, 275 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 3.0563 | 1.4521 | 6.5785 | baseline |
| tex-fmt | 2.3656 | 1.0157 | 5.1515 | 1.3× faster |
| latexindent | 105.6388 | 82.3922 | 161.2518 | 34.6× slower |
masters_dissertation.tex (95383 bytes, 2458 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 22.5924 | 19.7107 | 26.9194 | baseline |
| tex-fmt | 3.5818 | 1.7228 | 10.9985 | 6.3× faster |
| latexindent | 2333.9338 | 2330.0066 | 2336.7828 | 103.3× slower |
Whole-project results
badness. Each dot is one tool running a recursive --check over a real multi-file LaTeX project; the vertical position is mean wall-clock time as a ratio to badness on a log scale, so badness lies on the dashed baseline at 1, faster tools fall below it and slower tools rise above. This is a different mode from the single-file charts—read its ratio on its own terms. Hover a dot for the exact millisecond figures.Data table
project (12 files) (47190 bytes, 1005 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 4.6928 | 2.7871 | 9.3865 | baseline |
| tex-fmt | 3.4801 | 1.9159 | 6.7361 | 1.3× faster |
Linter
How the linter is measured
The linter runs over the same single-file corpus. Linters are read-only, so each
tool is handed the document path directly (no stdin plumbing—lacheck only
reliably reads a real file):
| Tool | Invocation |
|---|---|
badness | badness lint --no-config <file> |
chktex | chktex -q <file> |
lacheck | lacheck <file> |
Findings are the normal case, and the tools signal them differently: chktex
exits 2, badness lint exits 1, and lacheck always exits 0. A non-zero
exit here is not a run error, so hyperfine is told to ignore it
(--ignore-failure); the shell-loop fallback does the same.
There is no folder analog for the linter comparison: neither lacheck nor
chktex has a recursive directory mode, so—like latexindent in the formatter
folder benchmark—they would have no counterpart to measure against.
Setup
- badness:
0.7.0 - lacheck:
1.30 - chktex:
v1.7.9 - backend: hyperfine (min runs: 3)
- host: linux/x86_64, Intel(R) Core(TM) Ultra 7 155U
- generated: 2026-07-10T00:50:18Z
Results
badness. Each dot is one document checked by one tool; the vertical position is mean wall-clock time as a ratio to badness on a log scale, so badness lies on the dashed baseline at 1, faster tools fall below it and slower tools rise above. These tools report different problems, so a difference here is not a same-job speed verdict. Color distinguishes documents; hover a dot for the exact millisecond figures.Data table
small.tex (baseline) (1233 bytes, 48 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 2.7615 | 1.5162 | 7.6755 | baseline |
| lacheck | 10.5784 | 7.7071 | 14.5698 | 3.8× slower |
| chktex | 65.4007 | 55.1764 | 93.5242 | 23.7× slower |
cv.tex (6273 bytes, 275 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 3.2469 | 1.8403 | 8.2667 | baseline |
| lacheck | 11.6510 | 7.1851 | 25.7571 | 3.6× slower |
| chktex | 66.1456 | 54.7281 | 81.8926 | 20.4× slower |
masters_dissertation.tex (95383 bytes, 2458 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 24.1258 | 20.7172 | 28.9816 | baseline |
| lacheck | 13.5346 | 10.4407 | 18.2761 | 1.8× faster |
| chktex | 68.4455 | 61.3334 | 90.8754 | 2.8× slower |