Benchmarks
These benchmarks compare the speed of Badness’s formatter, linter, and language server with other LaTeX tools, along with the language server’s memory use. The tools differ in formatting style, lint coverage, and editor features, so the timings alone cannot tell you which tool best suits your work.
Formatter
We compare badness with tex-fmt
and latexindent on individual
documents. Files that badness cannot format are excluded from every tool’s
results.
- badness:
0.23.0 - tex-fmt:
0.5.7 - latexindent:
3.24.7 - backend: hyperfine (min runs: 3)
- host: linux/x86_64, AMD Ryzen 9 7900 12-Core Processor
- generated: 2026-09-21T14:09:42Z
Single-file results
badness on a logarithmic scale. The dashed line marks badness at 1; faster tools fall below it. Color distinguishes documents. Hover over a dot for times in milliseconds.Data table
small.tex (baseline) (1233 bytes, 48 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 2.2280 | 1.5594 | 4.5042 | baseline |
| tex-fmt | 1.8447 | 1.4077 | 5.2116 | 1.2× faster |
| latexindent | 60.4411 | 58.3468 | 63.3773 | 27.1× slower |
cv.tex (6273 bytes, 275 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 2.8968 | 2.1975 | 7.0406 | baseline |
| tex-fmt | 1.8836 | 1.4124 | 5.2885 | 1.5× faster |
| latexindent | 67.6548 | 64.6806 | 72.6389 | 23.4× slower |
masters_dissertation.tex (95383 bytes, 2458 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 21.2434 | 19.9534 | 24.0947 | baseline |
| tex-fmt | 2.5175 | 2.1186 | 5.6226 | 8.4× faster |
| latexindent | 1619.6812 | 1616.0399 | 1624.4684 | 76.2× slower |
phd_dissertation.tex (730369 bytes, 27482 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 206.0519 | 198.8200 | 214.6120 | baseline |
| tex-fmt | 9.3448 | 8.5763 | 12.3872 | 22.0× faster |
| latexindent | 25787.0356 | 25586.9122 | 25980.9774 | 125.1× slower |
Whole-project results
This comparison measures the time to check formatting across the .tex files of
kks32/phd-thesis-template. Both tools compute the formatted output without
writing changes to disk. latexindent is omitted because it has no recursive
directory mode.
badness on a logarithmic scale. The dashed line marks badness at 1; faster tools fall below it. Hover over a dot for times in milliseconds.Data table
project (12 files) (47190 bytes, 1005 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 6.0545 | 4.8906 | 9.2864 | baseline |
| tex-fmt | 2.5443 | 2.1472 | 6.0691 | 2.4× faster |
Linter
We compare badness lint with lacheck and
chktex on the same individual documents. Each
linter checks for a different set of problems. Neither comparison tool has a
recursive directory mode, so this benchmark covers individual files only.
- badness:
0.23.0 - lacheck:
1.30 - chktex:
v1.7.9 - backend: hyperfine (min runs: 3)
- host: linux/x86_64, AMD Ryzen 9 7900 12-Core Processor
- generated: 2026-09-21T14:09:42Z
badness on a logarithmic scale. The dashed line marks badness at 1; faster tools fall below it. Color distinguishes documents. Hover over a dot for times in milliseconds.Data table
small.tex (baseline) (1233 bytes, 48 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 2.5283 | 1.9014 | 4.2127 | baseline |
| lacheck | 6.2830 | 5.7361 | 7.5064 | 2.5× slower |
| chktex | 38.6138 | 33.0979 | 54.0230 | 15.3× slower |
cv.tex (6273 bytes, 275 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 2.9312 | 2.3599 | 4.5520 | baseline |
| lacheck | 6.5533 | 5.9206 | 9.3171 | 2.2× slower |
| chktex | 37.9784 | 33.5403 | 46.2528 | 13.0× slower |
masters_dissertation.tex (95383 bytes, 2458 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 58.9658 | 54.1457 | 70.2911 | baseline |
| lacheck | 8.3681 | 7.7195 | 10.9566 | 7.0× faster |
| chktex | 41.0897 | 36.1493 | 50.0436 | 1.4× faster |
phd_dissertation.tex (730369 bytes, 27482 lines)
| Tool | Mean (ms) | Min (ms) | Max (ms) | Relative |
|---|---|---|---|---|
| badness | 201.2823 | 191.1012 | 207.7070 | baseline |
| lacheck | 21.0009 | 19.8983 | 23.0398 | 9.6× faster |
| chktex | 68.7874 | 62.4486 | 81.9185 | 2.9× faster |
Language Server
We compare Badness with TexLab by opening the same five documents in the complete thesis project. The benchmark measures how long each server takes to start, respond to editor requests, and finish background work, as well as how much memory it uses.
- Badness:
0.23.0 - TexLab:
5.26.0 - corpus:
phd-thesis-template@3ce347686d75(v2.4, 16 source files, 279004 bytes) - session: 3 fresh runs per server; 5 open files (37773 bytes)
- sampling: every 0.15 s; quiet for 5.0 s; 60 s phase timeout
- host: Linux x86_64, AMD Ryzen 9 7900 12-Core Processor (61.9 GiB RAM)
- generated: 2026-09-21T14:17:30Z
- navigation target:
Aup91inChapter1/chapter1.texat line 19
Speed
The startup measurements cover three waits:
- Initialize measures the server’s response to the editor’s initialization request.
- Workspace ready measures the time from process start until background indexing settles.
- Open files ready measures the time from opening the documents until diagnostics arrive and background work settles.
After startup, we time requests for document symbols, hover information, definitions, references, and renaming. The chart shows the median response times. Tooltips and expandable tables include the 95th percentile (p95) and the number of results returned, which can differ between servers.
Readiness
Data table
| Server | Wait | Median | Min | Max |
|---|---|---|---|---|
| Badness | Initialize | 1.000 ms | 1.000 ms | 1.000 ms |
| Badness | Workspace ready | 152.000 ms | 152.000 ms | 152.000 ms |
| Badness | Open files ready | 33.000 ms | 33.000 ms | 35.000 ms |
| TexLab | Initialize | 1.000 ms | 1.000 ms | 7.000 ms |
| TexLab | Workspace ready | 152.000 ms | 152.000 ms | 605.000 ms |
| TexLab | Open files ready | 40.000 ms | 38.000 ms | 42.000 ms |
Warm requests
Data table
| Server | Request | Median | p95 | Returned work | Samples |
|---|---|---|---|---|---|
| Badness | Document symbols | 0.224 ms | 0.315 ms | 5–16 symbols (median 14), 3 KiB | 180 |
| Badness | Hover | 0.115 ms | 0.154 ms | 1 result, 206 B | 180 |
| Badness | Go to definition | 0.096 ms | 0.113 ms | 1 location in 1 file, 175 B | 60 |
| Badness | Find references | 0.103 ms | 0.139 ms | 2 locations in 2 files, 346 B | 60 |
| Badness | Rename | 0.091 ms | 0.119 ms | 2 edits in 2 files, 418 B | 60 |
| TexLab | Document symbols | 0.247 ms | 0.451 ms | 5–33 symbols (median 20), 5 KiB | 180 |
| TexLab | Hover | 0.077 ms | 0.119 ms | 1 result, 207 B | 180 |
| TexLab | Go to definition | 0.068 ms | 0.080 ms | 1 location in 1 file, 371 B | 60 |
| TexLab | Find references | 0.065 ms | 0.077 ms | 2 locations in 2 files, 346 B | 60 |
| TexLab | Rename | 0.066 ms | 0.080 ms | 2 edits in 2 files, 418 B | 60 |
Warm requests show medians across all samples, with p95 in tooltips and the table. Each target ran 20 measured rounds in each of 3 fresh sessions after 2 warmup rounds; symbols and hover span 3 files, while definition, references, and rename use Aup91 in Chapter1/chapter1.tex. Rename constructs the workspace edit but does not apply it.
Memory
The chart shows median memory use across three fresh sessions, including child processes. RSS counts resident memory, including shared pages in each process. The tooltips also show PSS, which divides shared pages among the processes using them to estimate their share of physical memory.
Data table
| Server | Milestone | RSS | PSS | Relative settled RSS |
|---|---|---|---|---|
| Badness | Baseline | 9.1 MB | 7.2 MB | - |
| Badness | Settled | 13.9 MB | 12.0 MB | baseline |
| Badness | Peak | 13.9 MB | 12.0 MB | - |
| TexLab | Baseline | 22.5 MB | 20.7 MB | - |
| TexLab | Settled | 29.6 MB | 27.7 MB | 2.13× |
| TexLab | Peak | 29.6 MB | 27.7 MB | - |
Reproducibility
Run these commands from the repository root:
task bench:download # Fetch the benchmark documents.
task bench # Measure formatter and linter speed.
task bench:lsp # Measure language-server speed and memory.
The scripts build badness in release mode. The formatter and linter comparison
uses the tools available on PATH and skips any that are missing. Install
hyperfine and jq for timing statistics; without them, the
script uses a shell loop that reports only mean times. The language-server
benchmark requires Linux, Python 3, and texlab. task bench:memory is an
alias for task bench:lsp.
The commands write benches/benchmark_results.json and
benches/memory_results.json. These committed files supply the charts, machine
details, and tool versions shown above. Building the documentation reads these
files without running the benchmarks. Neither benchmark runs in CI.
Documents
The individual documents are a committed small.tex baseline and three files
from a pinned tex-fmt release: cv.tex, masters_dissertation.tex, and
phd_dissertation.tex. The thesis project comes from a pinned revision of
kks32/phd-thesis-template. benches/documents/download.sh records both
pins.
The formatter and linter benchmarks skip any document that badness cannot
format. For the project comparison, the script copies a fixed set of .tex
files into a temporary directory, excluding unsupported files from both tools.
This gives both formatters the same input files without interference from Git
ignore rules. The language servers use the complete project, including its
class, style, bibliography, and image files.
Formatter and linter commands
For individual documents, each formatter reads from standard input and writes to standard output:
| Tool | Invocation |
|---|---|
badness | badness format --no-config --stdin-filepath bench.tex |
tex-fmt | tex-fmt --stdin |
latexindent | latexindent -g /dev/null - |
The project comparison includes directory traversal and uses check mode:
| Tool | Invocation |
|---|---|
badness | badness format --no-config --check <dir> |
tex-fmt | tex-fmt --check --recursive <dir> |
Each linter reads the document from its path:
| Tool | Invocation |
|---|---|
badness | badness lint --no-config <file> |
chktex | chktex -q <file> |
lacheck | lacheck <file> |
With hyperfine, each command gets one warmup and at least three measured runs.
The script ignores exit codes because lint findings and formatting differences
can produce nonzero exits. The commands and timing loop are defined in
benches/compare_format.sh.
Language-server sessions
benches/compare_lsp_memory.sh starts three fresh sessions each of
badness lsp and texlab run. In each session, the harness initializes the
server, waits for background work to settle, opens five documents, and collects
diagnostics using the server’s pull or push model. It then requests document
symbols and citation or reference hovers and waits for background work to settle
again.
The timed symbol and hover requests cover three chapter files. Definition,
references, and rename use the Aup91 citation in Chapter1/chapter1.tex,
whose entry is in References/references.bib. References include the
declaration. Rename computes edits without applying them. Each request target
gets two warmup rounds and 20 measured rounds per session. The chart aggregates
these samples across all three sessions. The recorded results also include
response sizes and counts of symbols, locations, edits, and affected files.
The harness samples the server and all descendant processes through Linux
/proc every 150 ms. Background work has settled when CPU use stays below 5% of
one core for five seconds. A phase fails if it does not settle within 60
seconds. Workspace and open-file readiness timings end at the start of their
respective quiet periods.
Memory is recorded after initialization (Baseline) and after the open-file workload settles (Settled). Peak is the largest sample through the timed requests. The chart shows the median of each measurement across the three sessions, and the JSON file retains the measurements from each session.