Unified · Split
| @@ -0,0 +1,282 @@ | ||
| 1 | +# Performance Plan | |
| 2 | + | |
| 3 | +Imported on: 2026-04-30 | |
| 4 | + | |
| 5 | +Fully generated as overview, intiial motivation is to keep track of `gix-index` performance and possible improvements. | |
| 6 | + | |
| 7 | +Source basis: | |
| 8 | + | |
| 9 | +- All `TODO(perf)` markers in this checkout. | |
| 10 | +- The two `TODO(performance)` markers, as they are equivalent in intent. | |
| 11 | +- Nearby generic TODOs that may affect common Git performance paths. | |
| 12 | +- A local comparison against `/Users/byron/dev/github.com/git/git`. | |
| 13 | + | |
| 14 | +Working assumption: priorities are based on ordinary interactive and networked Git use, not on synthetic microbenchmarks alone. | |
| 15 | + | |
| 16 | +## Typical Workflows | |
| 17 | + | |
| 18 | +These workflows define the impact ordering: | |
| 19 | + | |
| 20 | +- `git status`, `git add`, and editor integrations that repeatedly refresh the index, classify the worktree, and scan untracked paths. | |
| 21 | +- `git commit`, `git checkout`, `git switch`, `git merge`, and `git reset --hard`, which depend on fast index reads, tree/index conversion, checkout, and content merge. | |
| 22 | +- `git diff`, `git log -- <path>`, and `git blame`, which traverse trees, objects, and diffs repeatedly. | |
| 23 | +- `git fetch`, `git pull`, and `git clone`, which stress negotiation, object lookup, pack ingestion, duplicate avoidance, and pack/index refresh. | |
| 24 | +- `git push`, `git gc`, `git repack`, and object verification, which stress pack traversal, delta reconstruction, bitmaps, and multi-pack metadata. | |
| 25 | +- `git archive` and export-style commands, which stream trees with attributes and filters. | |
| 26 | + | |
| 27 | +## Priority Backlog | |
| 28 | + | |
| 29 | +### P0: Status, Add, And Index Refresh | |
| 30 | + | |
| 31 | +- [ ] **index-lookup-accelerator**: Make index path lookup acceleration a default fast path, not only an ignore-case helper. | |
| 32 | + - Source TODOs: | |
| 33 | + - `gix-dir/src/walk/classify.rs:396`: build a multi-threaded hash table so lookups are always accelerated, even for case-sensitive paths. | |
| 34 | + - `crate-status.md:844`: always use multi-threaded initialization of the case-insensitive hash table to accelerate index lookups. | |
| 35 | + - `gix-index/src/access/mod.rs:144`: multi-threaded insertion needs a raw table with multiple bucket locks. | |
| 36 | + - Workflow impact: very high for `status`, `add`, checkout collision checks, untracked filtering, and pathspec-heavy commands in large worktrees. | |
| 37 | + - Git comparison: Git's `name-hash.c` keeps `index_state.name_hash` and `dir_hash` for index lookups. It uses lazy initialization, a per-thread threshold (`LAZY_THREAD_COST`), bucket-derived locks, a two-phase directory/name hash build, and exact-then-ignore-case comparison. Git's `read-cache.c` wires name-hash updates into index entry insertion. | |
| 38 | + - Plan: | |
| 39 | + - Factor `gix-index::AccelerateLookup` into a general name/directory lookup accelerator usable for case-sensitive and case-insensitive callers. | |
| 40 | + - Add a parallel build path with sharded insertion or per-thread partial tables merged at the end. | |
| 41 | + - Benchmark `status` and `dirwalk` on large repos with `core.ignoreCase` true and false. | |
| 42 | + | |
| 43 | +- [ ] **status-refresh-heuristics**: Close the status refresh heuristic gap. | |
| 44 | + - Source TODOs: | |
| 45 | + - `gix-status/src/index_as_worktree/function.rs:144`: decide when parallelization is not worth it; Git uses about 500 entries per thread, capped at 20 preload threads. | |
| 46 | + - `src/plumbing/main.rs:433`: make thread-limit tuning configurable; macOS and Linux scale differently. | |
| 47 | + - `gix/src/status/mod.rs:165`: make `Repository::is_dirty()` a dedicated early-stop implementation with parallelism. | |
| 48 | + - Workflow impact: very high for prompts, IDEs, `git status`, and dirty checks in automation. | |
| 49 | + - Git comparison: Git's `preload-index.c` uses `MAX_PARALLEL = 20`, `THREAD_COST = 500`, skips staged/submodule/up-to-date/skip-worktree/fsmonitor-valid entries, and marks entries up to date after `lstat`. | |
| 50 | + - Plan: | |
| 51 | + - Measure the current chunk heuristic against Git-like thresholds on Linux and macOS. | |
| 52 | + - Add a dedicated `is_dirty()` path that stops after the first tree/index or index/worktree change. | |
| 53 | + - Make thread policy configurable through repository/config options rather than command-specific constants. | |
| 54 | + | |
| 55 | +- [ ] **untracked-fsmonitor-cache**: Use untracked-cache and fsmonitor data aggressively where valid. | |
| 56 | + - Source TODOs: | |
| 57 | + - `crate-status.md:846`: accelerated walk with the `UNTR` extension. | |
| 58 | + - `gix-index/src/extension/untracked_cache.rs:29` and `:33`: understand directory stat data and extension semantics fully. | |
| 59 | + - `gix-status/src/index_as_worktree/traits.rs:105`: make streaming I/O interruptible. | |
| 60 | + - Workflow impact: very high for `status` in large repositories with many ignored/untracked files. | |
| 61 | + - Git comparison: Git's `wt-status.c` passes `istate->untracked` into `fill_directory()` and avoids full scans when valid; `read-cache.c` also tweaks fsmonitor and untracked-cache state after index load. | |
| 62 | + - Plan: | |
| 63 | + - Finish documenting and validating `UNTR` semantics. | |
| 64 | + - Integrate valid untracked-cache state into `gix-dir` walks and status collection. | |
| 65 | + - Preserve interruptibility for editor integrations and long-running worktree scans. | |
| 66 | + | |
| 67 | +### P0: Index Read, Decode, And Tree-To-Index | |
| 68 | + | |
| 69 | +- [ ] **index-decode-storage**: Fix index decode storage costs before adding more threads. | |
| 70 | + - Source TODOs: | |
| 71 | + - `gix-index/src/decode/entries.rs:185`: `path_backing.extend_from_slice()` causes large `memmove` time despite apparent capacity. | |
| 72 | + - `crate-status.md:856`: threaded index read spends most time storing paths and currently has little benefit. | |
| 73 | + - `gix-index/src/decode/entries.rs:118`: entries behave like an intrusive path-keyed collection; this likely affects ignore-case and lookup. | |
| 74 | + - `gix-index/src/entry/flags.rs:11`: use persisted path length to save in-memory entry size. | |
| 75 | + - Workflow impact: very high for every command that opens a large repository. | |
| 76 | + - Git comparison: Git's `read-cache.c` memory-maps the index, allocates cache entries from mempools, uses the EOIE and IEOT extensions for threaded extension and entry loading, and stores path bytes inline in `struct cache_entry` (`name[FLEX_ARRAY]`). It also records an index entry offset table when threaded reads are requested. | |
| 77 | + - Plan: | |
| 78 | + - Profile the `path_backing` growth path and replace it with either pre-sized storage, chunked storage, or per-thread path arenas merged without repeated moves. | |
| 79 | + - Evaluate an entry representation that stores path length compactly and gives lookup structures stable references. | |
| 80 | + - Treat parallel decode as second-stage work after path storage stops dominating. | |
| 81 | + | |
| 82 | +- [ ] **tree-index-sort**: Avoid post-sort work when initializing an index from a tree. | |
| 83 | + - Source TODO: | |
| 84 | + - `gix-index/src/init.rs:107`: `remove_file_directory_conflicts()` sorts only to protect against invalid trees; typical valid trees already compare in order. | |
| 85 | + - Workflow impact: high for checkout, archive setup, worktree stream, and operations that synthesize indexes from trees. | |
| 86 | + - Git comparison: Git's cache-tree machinery (`cache-tree.c`) validates tree/index order and can prime an index's cache tree directly from a tree. `read-cache.c` and `cache-tree.c` keep sorted index invariants central. | |
| 87 | + - Plan: | |
| 88 | + - Insert entries in sorted order when walking trees and detect invalid file/directory conflicts during insertion. | |
| 89 | + - Add a fast path for already sorted valid trees and a fall-back verifier for malformed data. | |
| 90 | + - Reuse or extend cache-tree information when constructing an index from `HEAD`. | |
| 91 | + | |
| 92 | +- [ ] **worktree-stream-traversal**: Reduce double traversal in worktree streaming and archive creation. | |
| 93 | + - Source TODOs: | |
| 94 | + - `gix/src/repository/worktree.rs:88`: use the index at `HEAD` if possible. | |
| 95 | + - `gix/src/repository/worktree.rs:89`: non-HEAD trees are effectively traversed twice; object-cache sharing across copied ODB handles is not trivial. | |
| 96 | + - Workflow impact: medium to high for `archive`, export, and tooling that streams a tree with attributes. | |
| 97 | + - Git comparison: Git relies heavily on cache-tree and index state to avoid redundant tree work when the requested tree matches index/HEAD state. | |
| 98 | + - Plan: | |
| 99 | + - Fast-path `HEAD` through an existing index plus cache-tree where available. | |
| 100 | + - For non-HEAD trees, evaluate an explicit shared object/header cache for the two traversal consumers. | |
| 101 | + | |
| 102 | +### P0: Object Database And Pack Access | |
| 103 | + | |
| 104 | +- [ ] **odb-refresh-wait**: Replace spin/yield waiting during dynamic pack-index refresh. | |
| 105 | + - Source TODO: | |
| 106 | + - `gix-odb/src/store_impls/dynamic/load_index.rs:152`: a potentially hot loop should probably be a condition variable. | |
| 107 | + - Workflow impact: high for fetch, clone, object lookup under concurrency, and commands that observe newly written packs. | |
| 108 | + - Git comparison: Git's pack store preparation is centralized in `packfile.c`, and pack loading/re-preparation is explicit. It avoids leaving multiple callers in a spin loop while another caller is loading the same pack metadata. | |
| 109 | + - Plan: | |
| 110 | + - Replace the yield loop with a wait/notify primitive around index-load state transitions. | |
| 111 | + - Measure concurrent object lookup during fetch and after pack creation. | |
| 112 | + | |
| 113 | +- [ ] **loose-object-single-lookup**: Avoid duplicate loose-object lookup work. | |
| 114 | + - Source TODOs: | |
| 115 | + - `gix-odb/src/store_impls/dynamic/find.rs:292`: remove loose DB `contains()` plus `try_find()` double lookup. | |
| 116 | + - `gix-odb/src/store_impls/dynamic/header.rs:166`: same double lookup for headers. | |
| 117 | + - Workflow impact: medium to high for loose-heavy repositories, freshly created objects, and test fixtures. | |
| 118 | + - Git comparison: Git's ODB lookup API returns object info/content from one lookup path and then moves packs toward the front after hits. | |
| 119 | + - Plan: | |
| 120 | + - Add a single-call loose-object API that can report missing, header-only, or full object data. | |
| 121 | + - Preserve the current borrow-safety shape by returning owned metadata where needed. | |
| 122 | + | |
| 123 | +- [ ] **pack-delta-base-cache**: Revisit pack delta reconstruction and base caching. | |
| 124 | + - Source TODO: | |
| 125 | + - `gix-pack/src/data/file/decode/entry.rs:381`: optimize memory-intensive delta-chain reconstruction after more tests exist. | |
| 126 | + - Workflow impact: high for checkout after fetch, clone verification, diff/log over packed history, and blame. | |
| 127 | + - Git comparison: Git's `packfile.c` uses a `delta_base_cache` keyed by pack and base offset with an LRU size limit, a small preallocated delta stack, and delayed insertion into the cache to avoid races while unpacking. | |
| 128 | + - Plan: | |
| 129 | + - Add coverage for long and shared delta chains first. | |
| 130 | + - Compare current buffer swapping with Git's base-cache strategy and adopt an LRU base cache if repeated base materialization is visible in profiles. | |
| 131 | + | |
| 132 | +- [ ] **fetch-duplicate-objects**: Investigate duplicate received objects during fetch. | |
| 133 | + - Source TODO: | |
| 134 | + - `gix/tests/gix/remote/fetch.rs:128`: tests observe substantial duplication when receiving objects. | |
| 135 | + - Workflow impact: high for fetch, pull, and clone. | |
| 136 | + - Git comparison: Git uses bitmaps, multi-pack-index, pack reuse, and object-entry de-duplication in pack-object paths. | |
| 137 | + - Plan: | |
| 138 | + - Determine if duplication is transport negotiation, pack ingestion, replacement refs, or iteration/reporting. | |
| 139 | + - Add a counter-based regression test around unique received object IDs and pack refresh count. | |
| 140 | + | |
| 141 | +### P1: Diff, Blame, Merge, And History Queries | |
| 142 | + | |
| 143 | +- [ ] **blame-rename-diff-cache**: Avoid duplicated tree diff work in blame rename detection. | |
| 144 | + - Source TODO: | |
| 145 | + - `gix-blame/src/file/function.rs:582`: rename tracking repeats tree diff work after the no-rewrite pass. | |
| 146 | + - Workflow impact: medium to high for `git blame`, especially with `-M`, `-C`, and path history over rename-heavy repositories. | |
| 147 | + - Git comparison: Git's `blame.c` first looks for unchanged origins, then renames, then optionally moves/copies. It carries `blame_origin`, `blame_entry`, score thresholds, and optional blame Bloom data rather than blindly repeating the same tree walk for every case. | |
| 148 | + - Plan: | |
| 149 | + - Feed the first path-limited tree diff into a rewrite tracker when an addition or deletion is found. | |
| 150 | + - Cache parent/current tree IDs and blob headers per commit pair. | |
| 151 | + - Evaluate a blame-specific origin cache modeled after Git's two-pass origin/rename structure. | |
| 152 | + | |
| 153 | +- [ ] **merge-conflict-structures**: Audit merge tree conflict data structures. | |
| 154 | + - Source TODO: | |
| 155 | + - `gix-merge/src/tree/mod.rs:376`: a better data structure may be needed for some directory/file conflict representation. | |
| 156 | + - Workflow impact: medium for merges and rebases in large trees. | |
| 157 | + - Git comparison: Git's merge and rename machinery keeps maps for directory rename counts and per-path pairing in `diffcore-rename.c`. | |
| 158 | + - Plan: | |
| 159 | + - Profile rename and directory/file conflict heavy merges. | |
| 160 | + - Replace ad hoc per-conflict representation only if lookup or rewrite costs dominate. | |
| 161 | + | |
| 162 | +- [ ] **diff-preprocess-rescans**: Remove avoidable rescans in diff preprocessing. | |
| 163 | + - Source TODO: | |
| 164 | + - `gix-imara-diff/src/myers/preprocess.rs:132`: do not unnecessarily rescan lines. | |
| 165 | + - Workflow impact: medium for large file diffs and blame. | |
| 166 | + - Git comparison: Git's xdiff stack has dedicated preprocessing and histogram/patience variants. `gix-imara-diff` already carries Git-inspired histogram behavior, so this should remain benchmark-led. | |
| 167 | + - Plan: | |
| 168 | + - Add benchmarks for common source-file diffs and large generated files. | |
| 169 | + - Cache sliding-window counts or use prefix/suffix pruning data already computed during tokenization. | |
| 170 | + | |
| 171 | +### P1: Repository Discovery, Refs, And Path Handling | |
| 172 | + | |
| 173 | +- [ ] **packed-ref-lookup**: Implement packed-buffer aware reference lookup in general handles. | |
| 174 | + - Source TODO: | |
| 175 | + - `gix-ref/src/store/general/handle/find.rs:28`: implement lookup with packed-buffer handling. | |
| 176 | + - Workflow impact: medium for commands that resolve many refs, such as branch listing, fetch negotiation, and revision parsing. | |
| 177 | + - Git comparison: Git's ref backend keeps packed refs as a searchable table and layers loose refs on top. | |
| 178 | + - Plan: | |
| 179 | + - Add a packed-ref buffer search path with loose-over-packed precedence. | |
| 180 | + - Benchmark ref-heavy repos with many tags. | |
| 181 | + | |
| 182 | +- [ ] **literal-path-normalization**: Avoid path normalization through pattern machinery for single literal paths. | |
| 183 | + - Source TODOs: | |
| 184 | + - `gitoxide-core/src/repository/blame.rs:29`, `gitoxide-core/src/repository/merge/file.rs:37`, `tests/it/src/commands/blame_copy_royal.rs:58`: normalize paths without going through patterns. | |
| 185 | + - Workflow impact: medium for porcelain commands over individual paths. | |
| 186 | + - Plan: | |
| 187 | + - Add a literal path normalization helper below pathspec parsing. | |
| 188 | + - Keep pathspec matching for real pattern inputs only. | |
| 189 | + | |
| 190 | +### P2: Allocation And Micro-Hotspots | |
| 191 | + | |
| 192 | +- [ ] **hex-prefix-stack-decode**: Remove heap allocation from odd-length hex prefix parsing. | |
| 193 | + - Source TODO: | |
| 194 | + - `gix-hash/src/prefix.rs:120`: decode odd hex prefixes without heap allocation. | |
| 195 | + - Workflow impact: low to medium, but may matter for abbreviated object lookup and ref/path disambiguation. | |
| 196 | + - Git comparison: Git's object abbreviation logic in `object-name.c` and `packfile.c` works against fixed-size object IDs and pack fanout searches rather than allocating per prefix. | |
| 197 | + - Plan: | |
| 198 | + - Decode into a stack buffer sized by `Kind::longest()` and copy directly into `ObjectId`. | |
| 199 | + - Add a microbenchmark for odd and even prefix parsing. | |
| 200 | + | |
| 201 | +- [ ] **small-attribute-values**: Use a small byte-string representation for attribute values. | |
| 202 | + - Source TODO: | |
| 203 | + - `gix-attributes/src/state.rs:8`: a small byte string could provide an estimated 5 percent improvement. | |
| 204 | + - Workflow impact: low to medium for status, archive, checkout, and filter-heavy repos. | |
| 205 | + - Plan: | |
| 206 | + - Prototype a `smallvec`-backed byte string with display/serde wrappers. | |
| 207 | + - Validate JSON behavior and attribute query benchmarks before adopting. | |
| 208 | + | |
| 209 | +- [ ] **signed-data-reader**: Stream signed commit data without allocation. | |
| 210 | + - Source TODO: | |
| 211 | + - `gix-object/src/commit/mod.rs:33`: implement `std::io::Read` for `SignedData`. | |
| 212 | + - Workflow impact: low to medium, mostly signature verification and tooling over many signed commits. | |
| 213 | + - Plan: | |
| 214 | + - Add a segmented reader over the two non-signature ranges instead of building a `BString`. | |
| 215 | + | |
| 216 | +- [ ] **packetline-borrow-decode**: Remove extra packet-line decoding caused by borrow-checker workarounds. | |
| 217 | + - Source TODO: | |
| 218 | + - `gix-packetline/src/blocking_io/read.rs:110`: avoid additional decoding of the internal buffer. | |
| 219 | + - Workflow impact: low to medium for fetch/clone protocol handling. | |
| 220 | + - Plan: | |
| 221 | + - Revisit API lifetimes and return owned or borrowed line data explicitly. | |
| 222 | + | |
| 223 | +- [ ] **tempfile-registry-map**: Re-evaluate tempfile registry maps. | |
| 224 | + - Source TODO: | |
| 225 | + - `gix-tempfile/src/lib.rs:75`: use a `gix-hashtable` slot-map once available. | |
| 226 | + - Workflow impact: low for typical workflows, higher for checkout/merge paths that create many temporary files. | |
| 227 | + - Plan: | |
| 228 | + - Benchmark the existing mutexed map against a slot-map and the `hp-hashmap` feature. | |
| 229 | + | |
| 230 | +- [ ] **git-date-token-parser**: Replace brute-force Git date parsing. | |
| 231 | + - Source TODO: | |
| 232 | + - `gix-date/src/parse/git.rs:11`: learn from Git's parser instead of generated brute-force parsing. | |
| 233 | + - Workflow impact: low for normal operations, medium for log/rev parsing over many date filters. | |
| 234 | + - Git comparison: Git's `date.c` uses a token scanner (`parse_date_basic`) with alpha, digit, and timezone matchers, then falls back to approxidate. | |
| 235 | + - Plan: | |
| 236 | + - Replace format-chain parsing with a token scanner for Git-specific formats. | |
| 237 | + - Keep fixtures generated from Git for compatibility. | |
| 238 | + | |
| 239 | +## Git-Inspired Candidates Without Current TODOs | |
| 240 | + | |
| 241 | +- [ ] **sparse-index-parity**: Sparse-index parity for worktree-heavy commands. | |
| 242 | + - Git comparison: Git's `sparse-index.c` can collapse full index ranges into sparse directory entries and expand on demand. | |
| 243 | + - Candidate impact: very high for monorepos and sparse checkouts. | |
| 244 | + - Next step: audit which `gix-status`, `gix-dir`, checkout, and merge code paths force full-index behavior. | |
| 245 | + | |
| 246 | +- [ ] **commit-graph-bloom**: Commit-graph generation and Bloom-filter use in history walks. | |
| 247 | + - Git comparison: Git's `revision.c` uses commit-graph generation numbers to bound traversal, and blame can initialize Bloom data. | |
| 248 | + - Candidate impact: high for `log -- <path>`, merge-base, blame, and negotiation. | |
| 249 | + - Next step: verify all revision walkers prefer generation numbers and changed-path filters when available. | |
| 250 | + | |
| 251 | +- [ ] **midx-revindex-bitmaps**: Multi-pack-index reverse-index and bitmap-driven object enumeration. | |
| 252 | + - Git comparison: Git's `midx.c`, `pack-revindex.c`, and `packfile.c` use MIDX lookup, reverse-index chunks, and prefixed-object iteration across MIDX before falling back to individual packs. | |
| 253 | + - Candidate impact: high for fetch, clone, gc, prefix lookup, and object iteration in repos with many packs. | |
| 254 | + - Next step: compare `gix-pack` and `gix-odb` MIDX support against Git's lookup order and reverse-index cache behavior. | |
| 255 | + | |
| 256 | +- [ ] **pack-reuse-delta-policy**: Pack reuse and path-based delta compression policies. | |
| 257 | + - Git comparison: Git's `builtin/pack-objects.c` uses pack reuse, bitmap reuse, delta islands, path-based regions, threaded delta search, and delta-cache limits. | |
| 258 | + - Candidate impact: high for push, fetch serving, gc, and repack. | |
| 259 | + - Next step: treat this as a separate pack-generation roadmap after read-side pack performance is measured. | |
| 260 | + | |
| 261 | +## Measurement Plan | |
| 262 | + | |
| 263 | +- Use Git's `GIT_TRACE_PERFORMANCE=true` and `GIT_TRACE2_PERF=1` baselines on the same repositories as `gix` measurements. | |
| 264 | +- Maintain at least these benchmark repositories: small everyday repo, `git/git`, Linux-sized history, a many-untracked-files worktree, a sparse checkout, and a many-pack repository. | |
| 265 | +- Track wall time, syscalls/stat count, object reads, pack index refreshes, peak RSS, and allocation counts. | |
| 266 | +- For each P0 item, require one benchmark that represents an interactive command and one regression test that protects the intended fast path. | |
| 267 | + | |
| 268 | +## Immediate Next Moves | |
| 269 | + | |
| 270 | +- [ ] **profile-status-worktrees**: Profile `gix status` on large case-sensitive and case-insensitive worktrees and compare against Git's name-hash/preload behavior. | |
| 271 | +- [ ] **profile-index-decode**: Profile index decode to identify why `path_backing` triggers `memmove`, then choose arena/chunked storage before expanding threaded decode. | |
| 272 | +- [ ] **implement-odb-wait-notify**: Replace the dynamic ODB pack-index load wait loop with wait/notify. | |
| 273 | +- [ ] **fetch-dup-counter**: Add a fetch duplicate-object diagnostic counter before changing negotiation or pack ingestion. | |
| 274 | +- [ ] **prototype-prefix-stack**: Prototype stack-only odd-prefix parsing as a low-risk starter change. | |
| 275 | + | |
| 276 | +## Exit Criteria | |
| 277 | + | |
| 278 | +- [ ] **status-scale-exit**: `status` and dirty checks scale with index size and untracked-file count within an agreed multiplier of Git on the benchmark corpus. | |
| 279 | +- [ ] **index-throughput-exit**: Index read throughput improves on large indexes without increasing peak memory disproportionately. | |
| 280 | +- [ ] **object-lookup-wait-exit**: Concurrent object lookup during fetch/clone no longer spins while another thread loads pack metadata. | |
| 281 | +- [ ] **pack-benchmark-coverage-exit**: Object lookup and pack delta reconstruction have benchmark coverage before cache/data-structure rewrites land. | |
| 282 | +- [ ] **todo-triage-exit**: The remaining performance TODOs are either implemented, benchmarked and rejected, or converted into narrower tracked tasks. | |