Performance
Table of Contents
The targets are in section 15 of the design document. This page says how each is measured and what the last measurement gave.
1. Measuring
python3 tools/bench-phase1.py cargo test --release -p kalem-tui --test latency -- --ignored --nocapture cargo test --release -p kalem-ui --test latency -- --ignored cargo bench -p org-syntax --bench parse
tools/bench-phase1.pybuilds both release binaries and runs them as a user would. The editors quit after their first frame whenKALEM_EXIT_AFTER_STARTis set; times are the median of three runs, memory is the peak resident size. The documents are the Org manual repeated to 1 MB and 10 MB, and to 100 MB as a.txtfile.- The terminal latency test types 400 keys in the middle of the Org manual and draws a frame after each, into ratatui's test backend. It also saves a 10 MB document five times.
- The graphical latency test does the same in gpui's test platform with the system's text system: from the key to the painted frame. The GPU's work and the wait for the display are not included.
- The parser benchmark measures full parses and an incremental reparse after a keystroke in a paragraph.
2. Results
Measured on 2026-09-28 on an Apple M1 Max (macOS 15.1, Rust 1.98.1), with other work running on the machine.
| Target | Measured | Target |
|---|---|---|
| Cold start, empty document (graphical, to first frame) | 148 ms | under 300 ms |
| Opening a 1 MB document (graphical, to first frame) | 184 ms | under 200 ms |
| 10 MB document, until interactive (graphical) | 783 ms | under 1 s |
| 10 MB document, until interactive (terminal) | 714 ms | under 1 s |
| Keystroke to frame, graphical, p50 / p99 | 8.7 ms / 14.9 ms | under 16 ms / 33 ms |
| Keystroke to frame, terminal, p50 / p99 | 8.0 ms / 13.9 ms | under 16 ms / 33 ms |
| Incremental parse, keystroke in a paragraph | 0.05 ms | under 2 ms |
| Memory, empty document (graphical) | 47 MB | under 80 MB |
| Memory, 10 MB document (graphical) | 252 MB | under 500 MB |
| Binary size (full) | 81.0 MiB (Linux x86-64, CI, 2026-10-06, the viewers built in as compressed components; 59 MB on macOS arm64): missed | under 40 MB |
| Saving a 10 MB document | 20 ms | under 100 ms |
| Terminal frontend startup (terminal-only build) | 13 ms | under 50 ms |
CLI check on a 1 MB file |
91 ms | under 100 ms |
CLI fmt --check on a 1 MB file |
97 ms | under 100 ms |
| Binary size (terminal-only) | 42.5 MiB (Linux x86-64, CI, 2026-10-06, without the viewers and the plugin host): missed | under 15 MB |
| 100 MB plain text file, until interactive (graphical) | 430 ms | under 1 s |
| 100 MB plain text file, until interactive (terminal) | 135 ms | under 1 s |
| Keystroke in a 100 MB plain text file (text, line index and history) | 2.7 ms median, 14 ms p99 | under 16 ms |
Markdown, 10 MB document: first parse (in the background past 2 MiB) / keystroke reparse p50 / p99 (kalem-core --example markdown_timing) |
1.5 s / 10 ms / 23 ms | — / under 16 ms / 33 ms |
LaTeX parse, 1 MB paper (latex-syntax, --example timing) |
32 ms | under 100 ms |
| LaTeX incremental parse, keystroke in a paragraph of a 1 MB paper, p50 / p99 | 0.64 ms / 0.83 ms | under 2 ms |
LaTeX document model, 1 MB paper, first build / after an edit (latex-model, --example timing) |
21 ms / 6.7 ms | --- |
| LaTeX mode, 1 MB paper: opening (parse and state) | 50 ms | under 200 ms |
LaTeX mode, 1 MB paper: a keystroke with the model, the blocks and a screen of 50 lines, p50 / p99 (kalem-core --example latex_timing) |
10.7 ms / 15.8 ms | under 16 ms |
LaTeX mode, 10 MB paper: opening (SIZE=10000000) |
0.49 s | under 1 s |
LaTeX mode, 10 MB paper: a keystroke with the model, the blocks and a screen, p50 (typing / screen / blocks, PARTS=1) |
78 ms (9.9 / 41 / 26) | --- |
SyncTeX, a 210-page paper (338,460 records): a lookup from the source / from the page, p50 (kalem-core --example synctex_timing); reading the file once a build |
31 µs / 4 µs; 0.1 s | under 10 ms |
LaTeX project, keystroke in the Stacks project's 1.8 MB algebra.tex: the project's model, p50 (latex_keystroke) |
2.2 ms (was 21 ms) | under 2 ms |
Every target is met but three: both binary sizes (roadmap R2.1; CI's binary size job fails a change that grows either past its ceiling in tools/binary-size.txt) and the LaTeX project keystroke, a fifth over it on this machine. An edit that only moves the events of a file (typing in a paragraph, the common keystroke) moves the last model's positions instead of numbering the project again; the edit's region comes from the shared nodes of the two trees, and a test of random edits checks the result against a model built from scratch (latex-model/tests/incremental.rs). The files of the project are trusted from disk for a second between checks.
At 10 MB the opening meets its target. A keystroke there takes 78 ms: the incremental parse and the edit take 9.9 ms, but the screen rebuilds the document model (41 ms) and the blocks scan the whole text for their delimiters (26 ms); both grow with the file, and making them incremental is left until a document that size is a real case.
The binary size targets include math fonts, still to come (T2.2.1), so the gap will grow before it shrinks.
3. Where the time goes
A full parse of the Org manual takes 42 ms (about 20 MB/s). Opening an Org document is mostly this parse, so the tightest targets are the ones that parse a 1 MB file: check, fmt and opening in the graphical editor. What made them fit:
- Parser. Element regexes run only on lines that start with a byte they can match. Emphasis closers are indexed from the marker bytes, and block ends from one pass per leading byte. Word boundaries between ASCII characters skip the script table, and plain links check the first byte of the link types.
- Formatter.
kalem fmtparses once and maps positions through the table and tag alignments, where it used to parse four times. - Graphical editor. The file given on the command line is read and parsed on another thread while the window system starts. Only a launch from an app bundle without a file waits for the files the system opens.
- Large files (T2.7a.3). A keystroke in a 100 MB plain text file costs 2.7 ms at the median and 14 ms at the 99th percentile (
cargo run --release -p kalem-core --example text_timing FILE.txt), under a frame, so plain text mode keeps contiguous text with its line index rather than a rope or piece table (T1.3.1a). Files over 4 MB are colored a window of 400 lines at a time, from a fresh parser state 200 lines before it (kalem_highlight::Windowed), and a line longer than 16 KiB shows the 16 KiB around the cursor with…for the rest (kalem_core::view::plain_line_view), in every mode.
The CI job for benchmarks (a benchmark per target, regressions blocking a pull request) is not set up yet.