Backlog 4: beating Git on the Linux tree 61e9ad6ddc2d
refs #41
- Author
- Andy McCall @andymccall <[email protected]>
- When
- · 2026-09-30 20:42 UTC
- Signature
- ✔ signed by Andy McCall
- Hash
61e9ad6ddc2d8a7b7ad698809e7ea5167a08b8b1ee1a62a9a999932de2be4fae- Depends on
- ff0af1ee299d
- Depended on by
- 3d7120efe04a bbe654f95806 bdddd0c53720 3110dc5bea4c edc16b3ab30f 0c15e4678479 7cdc070ca541 6e2b12dfe6ec 332c1241a11c cb9a097def6f 7f75b41c107d 72215f29df4c e82591ec33a4 e266b7d4065c bd9414401fdc e4b8bcf04a86 abdbc768c6e8 c69733a3ce59
- In bookmarks
- agents-in-readme bare-hashes carried-not-removed carry-identical doctor-trust identical-sides keyless-tags main merge-remote-bookmark only-help-103 paths-for-resolutions-and-amend push-notes show-stat subrepo-pin-state switch-says
- In tags
- v0.10.0 v0.11.0 v0.2.0 v0.3.0 v0.3.1 v0.4.0 v0.4.1 v0.4.2 v0.4.3 v0.4.4 v0.4.5 v0.4.6 v0.5.0 v0.5.1 v0.6.0 v0.7.0 v0.8.0 v0.9.0
- Edits
- 1 atom, 1 tree operation, 1 file
Browse the tree at this change
added docs/backlog-overnight-4.md +116 −0
| 1 | + | # Overnight backlog 4: beating Git on the Linux tree |
|
| 2 | + | ||
| 3 | + | Loop 3 made oxy usable on the Linux kernel. This loop aims to make it |
|
| 4 | + | faster than Git wherever the model allows it, with the kernel tree as the |
|
| 5 | + | yardstick. Correctness is never traded for speed: every item keeps its |
|
| 6 | + | tests, and the test builds that compare an incremental structure with a |
|
| 7 | + | full computation stay on. |
|
| 8 | + | ||
| 9 | + | ## The yardstick |
|
| 10 | + | ||
| 11 | + | The Linux tip on Earth (`~claude/bench/linux`, 95 943 files, 1.8 GB): |
|
| 12 | + | `o` is the oxy repository and `g` the Git one. Git is 2.53. |
|
| 13 | + | ||
| 14 | + | | Operation | oxy (2026-09-30) | Git | Target | |
|
| 15 | + | |---|---:|---:|---| |
|
| 16 | + | | init and first commit | 38.5 s, 4.4 GB peak RSS | 38.8 s | under Git, under 2 GB | |
|
| 17 | + | | status, clean | 0.41 to 0.43 s | 0.26 to 0.32 s | at or under Git | |
|
| 18 | + | | status, one file edited | 1.26 to 1.39 s | 0.26 to 0.29 s | at or under Git | |
|
| 19 | + | | diff, one file | 1.19 to 1.45 s | 0.16 to 0.25 s | at or under Git | |
|
| 20 | + | | commit, one file | about 3.1 s | 0.45 s | under 0.5 s | |
|
| 21 | + | | 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 | |
|
| 22 | + | | log -n 100 | 1.39 s | 0.01 s | under 0.05 s | |
|
| 23 | + | | blame MAINTAINERS | 1.18 s | 0.09 s | at or under Git | |
|
| 24 | + | | checkout to the first state and back | 2.2 to 2.8 s | 0.42 to 0.47 s | at or under Git | |
|
| 25 | + | | fsck | 22.1 s | 13.1 s | at or under Git | |
|
| 26 | + | | gc | 39.7 s | 102.8 s | stays ahead | |
|
| 27 | + | | store after gc | 324 MB | 295 MB | at or under Git | |
|
| 28 | + | ||
| 29 | + | Where one-file status time goes now (Earth, loaded): |
|
| 30 | + | - index load: 0.16 to 0.28 s |
|
| 31 | + | - walk: 0.45 to 0.7 s |
|
| 32 | + | - manifest check before the snapshot: 0.1 s |
|
| 33 | + | - placeholders for unchanged files: 0.09 s |
|
| 34 | + | - tracked paths: 0.04 s |
|
| 35 | + | ||
| 36 | + | ## Rules for this loop |
|
| 37 | + | ||
| 38 | + | - **Measure quietly.** Earth runs OxygenFlow jobs, for every push to the |
|
| 39 | + | Oxygen repositories and for metalgear. Measure when `pgrep firecracker` |
|
| 40 | + | finds nothing if you can; otherwise run oxy and Git alternately, three |
|
| 41 | + | times each, and record the load average. Push your commits after a |
|
| 42 | + | measurement, not before, since each push starts jobs. |
|
| 43 | + | - **Build the bench binary** from the synced source: |
|
| 44 | + | `rsync -a --delete --exclude target --exclude .oxy ~/development/oxygen/ claude@earth:bench/oxygen/`, |
|
| 45 | + | then `CARGO_TARGET_DIR=~/bench/idxbin cargo build --release` there. Use |
|
| 46 | + | `~/bench/prof-status.sh`, `~/bench/vs-git.sh` and `~/bench/linux-bench.sh`, |
|
| 47 | + | and extend them rather than writing one-off commands. |
|
| 48 | + | - **Phase timings** (`OXY_TIMINGS=1`) name where time goes. Add phases where |
|
| 49 | + | they are missing before optimising. |
|
| 50 | + | - **An index is derived.** Every new index or cache is keyed by what it |
|
| 51 | + | describes, can be rebuilt from the changes at any time, and has a test |
|
| 52 | + | build check that compares it with a full computation (as the state index, |
|
| 53 | + | tree view and kept file order do). |
|
| 54 | + | - **Formats change freely before 1.0,** but a change format bump follows the |
|
| 55 | + | order in loop 3's backlog (old binary commits it; then the runner and hub |
|
| 56 | + | pins; then `make install`). |
|
| 57 | + | ||
| 58 | + | ## Speed |
|
| 59 | + | ||
| 60 | + | - [ ] B0 **Baseline.** Update `~/bench/linux-bench.sh` for today's build (the |
|
| 61 | + | state index, the manifest cache, clean and dirty status, diff, commit, |
|
| 62 | + | log, blame, checkout, a local clone, fsck, gc, store size) with three runs |
|
| 63 | + | each and oxy and Git alternating. Record the table in `docs/benchmarks.md`. |
|
| 64 | + | - [ ] S1 **Status and diff at Git's speed** (oxygen#41). In order, measuring |
|
| 65 | + | after each: |
|
| 66 | + | - the manifest as a compact binary file read without a parse per line; |
|
| 67 | + | - the walk: compare each file's stat with the manifest without building |
|
| 68 | + | a path string per entry (sorted walk against a sorted manifest), and |
|
| 69 | + | skip the `.oxyignore` machinery inside tracked directories where it |
|
| 70 | + | cannot change the answer; |
|
| 71 | + | - no placeholder `FileState` for unchanged files (audit every user of |
|
| 72 | + | `SnapshotResult.base_snapshot.files` first); |
|
| 73 | + | - load the state index lazily: memory-map it and decode a file's deferred |
|
| 74 | + | history only when that file is built; |
|
| 75 | + | - one pass for status: the manifest check and the snapshot walk share |
|
| 76 | + | their stats. |
|
| 77 | + | - [ ] S2 **Commit at Git's speed.** A commit extends the base it already |
|
| 78 | + | loaded: write the new state index from the old one plus the change, rather |
|
| 79 | + | than loading history again, and do not render what the snapshot rendered. |
|
| 80 | + | Target: one file under 0.5 s, 1 000 files under 0.6 s. |
|
| 81 | + | - [ ] S3 **The change index: log in milliseconds.** Per state, the changes in |
|
| 82 | + | topological order with their generation numbers and where each change's |
|
| 83 | + | metadata is, built from the parent state's index plus the new change. |
|
| 84 | + | `oxy log -n 100` reads 100 entries. Target: under 0.05 s. |
|
| 85 | + | - [ ] S4 **The node index: blame and file history.** Per node, the changes |
|
| 86 | + | that touch it, so blame and `oxy log FILE` cost that file's history. |
|
| 87 | + | Target: blame MAINTAINERS at or under Git. |
|
| 88 | + | - [ ] S5 **Checkout by comparing indexes.** Compare the two states' tree |
|
| 89 | + | indexes by rendered content hash and write only the files that differ. |
|
| 90 | + | Target: at or under Git. |
|
| 91 | + | - [ ] S6 **fsck at or under Git**: parallel object checking and a cheaper |
|
| 92 | + | state pass. |
|
| 93 | + | - [ ] S7 **The first commit ahead of Git, under 2 GB**: stream reading, |
|
| 94 | + | hashing and storing a batch of files at a time, so the whole tree's text |
|
| 95 | + | is never in memory at once. |
|
| 96 | + | ||
| 97 | + | ## Space |
|
| 98 | + | ||
| 99 | + | - [ ] P1 **Store at or under Git after gc** (324 MB against 295 MB): measure |
|
| 100 | + | what takes the space, then try a trained zstd dictionary per repository |
|
| 101 | + | and packing similar objects together. |
|
| 102 | + | ||
| 103 | + | ## Also |
|
| 104 | + | ||
| 105 | + | - [ ] R1 **Rebase into the working copy** (oxygen#42, Andy's choice for the |
|
| 106 | + | overnight loop): when `oxy rebase CHANGE --onto TARGET` would be |
|
| 107 | + | conflicted, offer to leave the conflict in the working copy to resolve and |
|
| 108 | + | commit, as `merge` does, rather than refusing. |
|
| 109 | + | - [ ] Z1 **Closing summary.** Re-run the full benchmark and summarise, for |
|
| 110 | + | Andy, where oxy matches or beats Git on the Linux tree, and where it does |
|
| 111 | + | not and why. |
|
| 112 | + | ||
| 113 | + | ## Log |
|
| 114 | + | ||
| 115 | + | - 2026-09-30 Backlog written from the day's measurements (state index, |
|
| 116 | + | changed-only builds and the manifest cache already in). |