What's new in Xo 0.34: edits build faster than go build
Every number below comes from a committed results file in the xo repository, linked where it appears. The release notes list every change.
Edits build in a third of the time
The headline of 0.34: xo build and xo run compile your program
separately from the standard library, on darwin and linux, arm64 and amd64,
and on windows/amd64 (75468ed91, checked on the Windows CI runner,
run 40819530873). std is compiled once per xo into a store and a set of
facts in the backend cache; an edit compiles and links only your code.
| edit, median | 0.33.0 | 0.34 | go build | source |
|---|---|---|---|---|
| llvm-fib, darwin/arm64 | 230.8 ms | 70.2 ms | 87.7 ms | sepcomp-edit-final-2026-10-29.md, line 7 |
| HTTP service, darwin/arm64 | 570.5 ms | 156.0 ms | 245.5 ms | sepcomp-edit-final-2026-10-29.md, line 8 |
| llvm-fib, linux/arm64 | 350.0 ms (whole program) | 196.5 ms | sepcomp-edit-linux-2026-10-28.md, line 7 | |
| HTTP service, linux/arm64 | 804.0 ms (whole program) | 453.5 ms | sepcomp-edit-linux-2026-10-28.md, line 8 | |
| llvm-fib, windows/amd64 | 295 ms (whole program) | 143 ms | 0388-separate-std-compilation.md, line 563 | |
| HTTP service, windows/amd64 | 619 ms (whole program) | 280 ms | 0388-separate-std-compilation.md, line 563 to 564 |
On darwin/arm64 an edit now builds faster than go build of the same edit.
The linux rows are Docker on the Mac and the windows rows an earlier
measurement, both before the check’s record below was on by default.
The programs it builds run as fast as before: every release-vs-go workload is within noise or better against a whole-program build (sepcomp-runtime-2026-10-28.md, line 33 to 54), and so are the HTTP server’s throughput and CPU per request (sepcomp-runtime-2026-10-28.md, line 64 to 65). How we cut the edit loop covers how.
The check. A separately compiled build checks the program against a record of std’s declarations instead of parsing std (102 std files parsed to 9 for fib, sepcomp-edit-stage2-2026-10-28.md, line 29 to 30). At the cut, fib’s check is 17.4 ms against 0.33.0’s 30.5, the HTTP service’s 25.1 against 54.9 (sepcomp-edit-final-2026-10-29.md, line 16).
Faster starts
A separately compiled program first started 6 percent slower than a whole one (sepcomp-runtime-2026-10-28.md, line 24): dyld bound the imports of std functions the link had stripped, and each weak symbol libSystem lacks costs a lookup at every launch (sepcomp-runtime-2026-10-28.md, line 27). Binding only what kept code calls fixed it, and dropping the sanitizer imports a non-race build never uses made every program start faster: an empty program binds 53 symbols at launch, where a separately compiled one bound 123 and a whole one 64 (47b1867e2).
The runtime and the compiler
- A direct hand-off between tasks. A task that parks and wakes the task it just queued switches to it with one stack switch instead of two (decision 0394). Channel ping-pong went from 1.196 times Go’s time to 0.897 (release-vs-go-v0.34.0.md, line 84).
- Reference counting insertion, the compiler’s largest single cost (24 percent of a cold self build’s CPU, insert-rc-2026-10-28.md, line 20), does the same work with better data structures: a cold self build retires 4.4 percent fewer instructions (insert-rc-2026-10-28.md, line 74).
- Threaded dispatch for loops that
matchthrough a jump table (decision 0397): interp 284.8M to 161.7M cycles and 78 to 48 ms at one placement (interp-placement-2026-10-28.md, line 57 to 58), and its spread across code placements from 38 to 6.8 percent (interp-placement-2026-10-28.md, line 40, interp-placement-2026-10-28.md, line 80). - Faster links. macOS binaries take their UUID from the code signature’s page hashes, one pass over the image instead of two (decision 0400: the HTTP server’s separately compiled link 0.898 of its time, ef0ba9fa9); the linkers patch relocations in place (decision 0399, prelude.md, line 25); and the ELF linker writes its DWARF sections whole (a Linux HTTP edit’s link 86 to 61 ms, d830a038c).
Code generation
- Jump padding for Intel’s JCC erratum. Sorting 1M Ints on an i7 ran
1.23 times Go, and the cause was the erratum: whether a fused
compare-and-branch straddles a 32 byte boundary decided 73 to 90 ms across
placements, against Go’s 67 (sort1m-x86-2026-10-28.md, line 21 to 24). Xo’s
assembler now pads jumps and fused pairs off those boundaries: 69 to 72 ms
over 32 placements, median 70 (sort1m-x86-2026-10-28.md, line 47 to 48).
List[Int].sortis now Go’s pdqsort (sort1m-x86-2026-10-27.md, line 13 to 16). - n-body is 1.040 times Go’s time, closer than 0.33’s 1.054 (release-vs-go-v0.34.0.md, line 42); against Go, 18 of the 20 release workloads now run faster.
Standard library
- SHA-3 and SHAKE use the arm64 SHA3 instructions on Apple CPUs (a port of Go’s keccakF1600NEON): SHA3-256 of 1 MiB in 0.948 of Go’s time, from 1.66 (crypto.md, line 3). ML-KEM’s hashing takes the same path.
- regexp compiles in 1.21 times Go’s time, from 1.68 (regexp.md, line 39).
- compress and archive: inflate copies back-references in bulk and builds its Huffman tables in place; unzip runs at 1.04 times Go’s time (compress.md, line 9).
- template parses in 0.948 of its time (template.md, line 3),
and
Fs.copyclones files on macOS (APFS clonefile, prelude.md, line 36). - Breaking: a
uuid.Uuidis its 128 bits as two Ints instead of its text (decision 0387, deprecated in 0.33):u.0becomesu.display(),uuid.Uuid(s)becomesuuid.parse(s)?, and the checker names the replacement.
Tooling, platforms, and the testers’ reports
- First builds for a brand-new user: fib in 954 ms and the HTTP service in
2.62 s from empty caches, against
go build’s 1482 ms and 3.05 s (first-build-2026-10-28.md, line 10 to 11, first-build-2026-10-28.md, line 18 to 19). - The 0.33 testers’
use goreports are fixed: a newer installed Go no longer breaks every binding (decision 0393), one Xo type stands for a Go type two bound packages share (decision 0392),==on Go values handed out by value compares by value, modules with a patch-levelgoline build, and on Windowsuse gofindsxo-gobind.exe, works from PowerShell and cmd, andxo mod tidyno longer doublesC:/paths. - x86-64 Linux with GNU as: a build that assembles through
cc(XO_NOLIBC=0) no longer fails on arm64-only directives (f12984f7d). xo bench --abcounts a round as loaded relative to the machine’s load before the run, so a busy machine no longer drops every round.
Upgrading
Nothing to do: the first build after installing 0.34 builds your program
whole and std’s variants in the background; the next edit builds
separately. XO_SEPCOMP_DEFAULT=0 turns separate compilation off.