# 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](https://github.com/xo-lang/xo), linked where it appears.
The [release notes](/releases/) 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](https://github.com/xo-lang/xo/commit/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](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-edit-final-2026-10-29.md#L7) |
| HTTP service, darwin/arm64 | 570.5 ms | 156.0 ms | 245.5 ms | [sepcomp-edit-final-2026-10-29.md, line 8](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-edit-final-2026-10-29.md#L8) |
| llvm-fib, linux/arm64 | 350.0 ms (whole program) | 196.5 ms | | [sepcomp-edit-linux-2026-10-28.md, line 7](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-edit-linux-2026-10-28.md#L7) |
| HTTP service, linux/arm64 | 804.0 ms (whole program) | 453.5 ms | | [sepcomp-edit-linux-2026-10-28.md, line 8](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-edit-linux-2026-10-28.md#L8) |
| llvm-fib, windows/amd64 | 295 ms (whole program) | 143 ms | | [0388-separate-std-compilation.md, line 563](https://github.com/xo-lang/xo/blob/main/knowledgebase/decisions/0388-separate-std-compilation.md#L563) |
| HTTP service, windows/amd64 | 619 ms (whole program) | 280 ms | | [0388-separate-std-compilation.md, line 563 to 564](https://github.com/xo-lang/xo/blob/main/knowledgebase/decisions/0388-separate-std-compilation.md#L563-L564) |

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](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-runtime-2026-10-28.md#L33-L54)), and so are the HTTP server's
throughput and CPU per request ([sepcomp-runtime-2026-10-28.md, line 64 to 65](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-runtime-2026-10-28.md#L64-L65)).
[How we cut the edit loop](/blog/edit-loop-a-third/) 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](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-edit-stage2-2026-10-28.md#L29-L30)). 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](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-edit-final-2026-10-29.md#L16)).

## Faster starts

A separately compiled program first started 6 percent slower than a whole
one ([sepcomp-runtime-2026-10-28.md, line 24](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-runtime-2026-10-28.md#L24)): 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](https://github.com/xo-lang/xo/blob/main/bench/results/sepcomp-runtime-2026-10-28.md#L27)). 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](https://github.com/xo-lang/xo/commit/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](https://github.com/xo-lang/xo/blob/main/bench/results/release-vs-go-v0.34.0.md#L84)).
- **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](https://github.com/xo-lang/xo/blob/main/bench/results/insert-rc-2026-10-28.md#L20)), 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](https://github.com/xo-lang/xo/blob/main/bench/results/insert-rc-2026-10-28.md#L74)).
- **Threaded dispatch** for loops that `match` through 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](https://github.com/xo-lang/xo/blob/main/bench/results/interp-placement-2026-10-28.md#L57-L58)), and its spread across code
  placements from 38 to 6.8 percent ([interp-placement-2026-10-28.md, line 40](https://github.com/xo-lang/xo/blob/main/bench/results/interp-placement-2026-10-28.md#L40),
  [interp-placement-2026-10-28.md, line 80](https://github.com/xo-lang/xo/blob/main/bench/results/interp-placement-2026-10-28.md#L80)).
- **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](https://github.com/xo-lang/xo/commit/ef0ba9fa9));
  the linkers patch relocations in place (decision 0399, [prelude.md, line 25](https://github.com/xo-lang/xo/blob/main/std/changes/prelude.md#L25)); and the ELF linker writes its DWARF
  sections whole (a Linux HTTP edit's link 86 to 61 ms, [d830a038c](https://github.com/xo-lang/xo/commit/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](https://github.com/xo-lang/xo/blob/main/bench/results/sort1m-x86-2026-10-28.md#L21-L24)). 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](https://github.com/xo-lang/xo/blob/main/bench/results/sort1m-x86-2026-10-28.md#L47-L48)).
  `List[Int].sort` is now Go's pdqsort ([sort1m-x86-2026-10-27.md, line 13 to 16](https://github.com/xo-lang/xo/blob/main/bench/results/sort1m-x86-2026-10-27.md#L13-L16)).
- **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](https://github.com/xo-lang/xo/blob/main/bench/results/release-vs-go-v0.34.0.md#L42)); 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](https://github.com/xo-lang/xo/blob/main/std/changes/crypto.md#L3)). ML-KEM's hashing takes the same path.
- **regexp** compiles in 1.21 times Go's time, from 1.68
  ([regexp.md, line 39](https://github.com/xo-lang/xo/blob/main/std/changes/regexp.md#L39)).
- **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](https://github.com/xo-lang/xo/blob/main/std/changes/compress.md#L9)).
- **template** parses in 0.948 of its time ([template.md, line 3](https://github.com/xo-lang/xo/blob/main/std/changes/template.md#L3)),
  and `Fs.copy` clones files on macOS (APFS clonefile,
  [prelude.md, line 36](https://github.com/xo-lang/xo/blob/main/std/changes/prelude.md#L36)).
- **Breaking:** a `uuid.Uuid` is its 128 bits as two Ints instead of its
  text (decision 0387, deprecated in 0.33): `u.0` becomes `u.display()`,
  `uuid.Uuid(s)` becomes `uuid.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](https://github.com/xo-lang/xo/blob/main/bench/results/first-build-2026-10-28.md#L10-L11), [first-build-2026-10-28.md, line 18 to 19](https://github.com/xo-lang/xo/blob/main/bench/results/first-build-2026-10-28.md#L18-L19)).
- The 0.33 testers' `use go` reports 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-level `go` line
  build, and on Windows `use go` finds `xo-gobind.exe`, works from
  PowerShell and cmd, and `xo mod tidy` no longer doubles `C:/` paths.
- x86-64 Linux with GNU as: a build that assembles through `cc`
  (`XO_NOLIBC=0`) no longer fails on arm64-only directives ([f12984f7d](https://github.com/xo-lang/xo/commit/f12984f7d)).
- `xo bench --ab` counts 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.


---

- Previous: [Edit, build, run: how Xo 0.34 cut the edit loop to a third](/blog/edit-loop-a-third/index.md)

