Overnight backlog 4: beating Git on the Linux tree
Loop 3 made oxy usable on the Linux kernel. This loop aims to make it faster than Git wherever the model allows it, with the kernel tree as the yardstick. Correctness is never traded for speed: every item keeps its tests, and the test builds that compare an incremental structure with a full computation stay on.
The yardstick
The Linux tip on Earth (~claude/bench/linux, 95 943 files, 1.8 GB):
o is the oxy repository and g the Git one. Git is 2.53.
| Operation | oxy (2026-09-30) | Git | Target |
|---|---|---|---|
| init and first commit | 38.5 s, 4.4 GB peak RSS | 38.8 s | under Git, under 2 GB |
| status, clean | 0.41 to 0.43 s | 0.26 to 0.32 s | at or under Git |
| status, one file edited | 1.26 to 1.39 s | 0.26 to 0.29 s | at or under Git |
| diff, one file | 1.19 to 1.45 s | 0.16 to 0.25 s | at or under Git |
| commit, one file | about 3.1 s | 0.45 s | under 0.5 s |
| status, diff and commit, 1 000 files | 3.9, 2.8 and 4.9 s | 0.41, 0.95 and 0.57 s | at or under Git |
| log -n 100 | 1.39 s | 0.01 s | under 0.05 s |
| blame MAINTAINERS | 1.18 s | 0.09 s | at or under Git |
| checkout to the first state and back | 2.2 to 2.8 s | 0.42 to 0.47 s | at or under Git |
| fsck | 22.1 s | 13.1 s | at or under Git |
| gc | 39.7 s | 102.8 s | stays ahead |
| store after gc | 324 MB | 295 MB | at or under Git |
Where one-file status time goes now (Earth, loaded):
- index load: 0.16 to 0.28 s
- walk: 0.45 to 0.7 s
- manifest check before the snapshot: 0.1 s
- placeholders for unchanged files: 0.09 s
- tracked paths: 0.04 s
Rules for this loop
- Measure quietly. Earth runs OxygenFlow jobs, for every push to the
Oxygen repositories and for metalgear. Measure when
pgrep firecrackerfinds nothing if you can; otherwise run oxy and Git alternately, three times each, and record the load average. Push your commits after a measurement, not before, since each push starts jobs. - Build the bench binary from the synced source:
rsync -a --delete --exclude target --exclude .oxy ~/development/oxygen/ claude@earth:bench/oxygen/, thenCARGO_TARGET_DIR=~/bench/idxbin cargo build --releasethere. Use~/bench/prof-status.sh,~/bench/vs-git.shand~/bench/linux-bench.sh, and extend them rather than writing one-off commands. - Phase timings (
OXY_TIMINGS=1) name where time goes. Add phases where they are missing before optimising. - An index is derived. Every new index or cache is keyed by what it describes, can be rebuilt from the changes at any time, and has a test build check that compares it with a full computation (as the state index, tree view and kept file order do).
- Formats change freely before 1.0, but a change format bump follows the
order in loop 3's backlog (old binary commits it; then the runner and hub
pins; then
make install).
Speed
-
B0 Baseline. Update
~/bench/linux-bench.shfor today's build (the state index, the manifest cache, clean and dirty status, diff, commit, log, blame, checkout, a local clone, fsck, gc, store size) with three runs each and oxy and Git alternating. Record the table indocs/benchmarks.md. -
S1 Status and diff at Git's speed (oxygen#41). In order, measuring
after each:
- the manifest as a compact binary file read without a parse per line;
- the walk: compare each file's stat with the manifest without building
a path string per entry (sorted walk against a sorted manifest), and
skip the
.oxyignoremachinery inside tracked directories where it cannot change the answer; - no placeholder
FileStatefor unchanged files (audit every user ofSnapshotResult.base_snapshot.filesfirst); - load the state index lazily: memory-map it and decode a file's deferred history only when that file is built;
- one pass for status: the manifest check and the snapshot walk share their stats.
- S2 Commit at Git's speed. A commit extends the base it already loaded: write the new state index from the old one plus the change, rather than loading history again, and do not render what the snapshot rendered. Target: one file under 0.5 s, 1 000 files under 0.6 s.
-
S3 The change index: log in milliseconds. Per state, the changes in
topological order with their generation numbers and where each change's
metadata is, built from the parent state's index plus the new change.
oxy log -n 100reads 100 entries. Target: under 0.05 s. -
S4 The node index: blame and file history. Per node, the changes
that touch it, so blame and
oxy log FILEcost that file's history. Target: blame MAINTAINERS at or under Git. - S5 Checkout by comparing indexes. Compare the two states' tree indexes by rendered content hash and write only the files that differ. Target: at or under Git.
- S6 fsck at or under Git: parallel object checking and a cheaper state pass.
- S7 The first commit ahead of Git, under 2 GB: stream reading, hashing and storing a batch of files at a time, so the whole tree's text is never in memory at once.
Space
- P1 Store at or under Git after gc (324 MB against 295 MB): measure what takes the space, then try a trained zstd dictionary per repository and packing similar objects together.
Also
-
R1 Rebase into the working copy (oxygen#42, Andy's choice for the
overnight loop): when
oxy rebase CHANGE --onto TARGETwould be conflicted, offer to leave the conflict in the working copy to resolve and commit, asmergedoes, rather than refusing. - Z1 Closing summary. Re-run the full benchmark and summarise, for Andy, where oxy matches or beats Git on the Linux tree, and where it does not and why.
Log
- 2026-09-30 Backlog written from the day's measurements (state index, changed-only builds and the manifest cache already in).