# Athas 0.16, measured: where the time went and how we got it back

Source: https://athas.dev/blog/athas-0-16-performance

Mehmet Özgül · Oct 12, 2026

Typing in a 200,000-line file is 28× cheaper and a repeat git blame takes 0.47 ms instead of 678 ms. Here is every number and how we measured it.

Athas 0.16 changed how the editor holds your text, how an AI reply streams into the chat and how often the app talks to its Rust side. We wanted proof, not a feeling, so we measured the old build against the new one. Both ran on the same Apple M4, with the same inputs and the same keystrokes.

This post goes through the results.

The short version: the biggest wins came from work that no longer happens at all. Before 0.16, several features redid their whole job on every keystroke, even when nothing they cared about had changed. After 0.16 they wait for a pause, or they only look at what changed.

## Typing no longer copies your file

Before 0.16, the editor kept a second copy of your document in the app state, as one long string. Every keystroke rebuilt that string from scratch. In a 200,000-line file, 200 typed characters built **1.37 GB** of throwaway strings. About a dozen parts of the window, from the tab bar to the breadcrumbs, then compared that copy to the previous one.

Now the editor's own text structure is the single source of truth. The app state only keeps a revision number and a dirty flag, and it builds the full string once, when you stop typing, for the features that need it (saving, git blame, the language server).

The cost per keystroke now stays flat as the file grows, and it is lower at every size. At 200,000 lines it drops from 0.72 ms to 0.026 ms. The work moves to one deferred step of up to about 5 ms that runs once per typing burst, instead of on every key.

## Features stop rescanning the whole document

Three features used to redo their full job while you typed.

- **Find.** With the find panel open on a 50,000-line file, every keystroke and every cursor move rescanned the document for matches: 63 ms each. Now the match count updates after you pause, and a keystroke costs 0.2 ms. Over 100 keystrokes, total scanning time drops from 6.2 s to 65 ms.
- **Markdown preview.** The preview parsed the whole document on every key and replaced its HTML: 147 ms per keystroke in a 2,000-line file. Now it parses once after a short pause, and typing costs 0.12 ms.
- **Git blame.** Each blame request walked the file's full history again. On a file with 117 commits that took 678 ms, every time you paused. Blame history is now cached per file and commit. A repeat request takes 0.47 ms, and after an edit Athas reuses the cached history and only blames your buffer against it, in under 1 ms.

The first blame after you switch branches or commit still walks the history once, so that number did not change. Semantic highlighting follows the same idea: it now only builds decorations for the lines near your viewport, about 1,300 instead of 106,000 in a 20,000-line file.

## An AI reply touches less of the app

While an agent streams a reply, Athas updates the chat many times per second. Before 0.16, every update gave the whole list of chats a new identity. Every component that reads that list re-rendered on every frame, and chat preferences were written to `localStorage` on every frame too.

After 0.16, only the message that is growing changes. The rest of the app commits 7 times during the reply instead of 3,507, and preferences are no longer written while streaming. Saving the chat also got lighter. The database used to delete and re-insert all 441 rows of a 300-message chat on every save, and now it updates the one message that changed. Each save sends 6.8 KB from the interface instead of 155 KB.

## Less data moving around

Athas is a Tauri app, so the interface and the Rust side talk over IPC, and the Rust side talks to language servers. Some of that traffic shrank a lot:

| What                                                                            | Before  | After   |
| ------------------------------------------------------------------------------- | ------- | ------- |
| Terminal output, messages per MB of `cat`                                       | 1,026   | 16.5    |
| Language server response after typing 3 characters (servers with delta support) | 423 KB  | 117 B   |
| Saving a 1 MB file, arguments sent to Rust                                      | 2.07 MB | 1.03 MB |
| JSON sent while streaming a 20 s AI reply                                       | 70.8 MB | 0.9 MB  |

The language server row only applies to servers that support delta updates, such as rust-analyzer and clangd. TypeScript, Go and Python servers behave as before.

## How we measured

- **Builds.** Before is commit `c2f72efd5`, after is `1cf65acd0`. Both ran on an Apple M4 with 10 cores and 16 GB of RAM.
- **Interface scenarios** ran in a test harness against each build's own stores, hooks and editor code. A few components were stood in by copies of their selectors. Inputs were fixed-seed random, so both sides typed the same characters in the same places.
  - Runs alternated before, after, before, after, on one worker.
  - Two runs that overlapped other heavy jobs on the machine were thrown out. The charts use the median of the remaining clean runs.
- **The harness has no layout or paint.** Treat the interface timings as relative, not absolute. The counts (re-renders, writes, bytes) are exact and repeat run to run.
- **Rust scenarios** ran natively in release mode, with the same allocator and build profile as the app. Git blame ran on a clone of the Athas repository, with 3,022 commits. The machine was busy during these runs, so treat differences under about 30% as noise. The large ratios hold up.

Every number in this post comes from those runs. All the timings are in the table view of the first chart.

These changes shipped in [Athas 0.16](/docs/changelog). If something still feels slow in your projects, tell us on [Discord](https://discord.gg/DD8F38wFMv).
