Skip to content
zit Timeline
Login

Timeline

Parents and children of check-in [c075a01cfa]

2026-08-04
16:05
pack: diff pass-1 trees against the primary parent, and cache what it re-reads

Issues #27 and #28.

deriveCandidateHints walked each check-in's whole S-card tree and its parent's, for every check-in. Shared subtrees can never pair -- an unchanged node has the same rid on both sides, so pairAgainstParent skips it -- and their blobs were already recorded at introduction, which Pass 2 relies on only as a distinct set (it compacts with dedupSortedRids). So the walk now descends lockstep and prunes any subtree whose S node is the same artifact in both. O(check-ins x tree) becomes O(check-ins x changed paths): sub-manifest loads drop 56 percent on libfossil, to near the distinct-node floor, and the ratio grows with tree size.

The visits that remain were re-inflating. contentOfEntry memoized only .delta frames, so a .zlib sub-manifest re-inflated on every visit -- which is why a pack profile is dominated by flate.Decompress.streamInner rather than by tree-walk arithmetic. PlaintextCache gains memoize_undeltaed, off by default because a linear sweep that reads each artifact once would only duplicate bytes, and opted into by this walk, which revisits.

Output is byte-identical: the wapp store hashes unchanged, and a 189,251 artifact fossil-corpus repack is a fixpoint at 176,143,384 bytes with the same 146,059 re-deltaed.

test 3495/3495, gate, dst 6x1000, e2e 718/718, git-import-e2e, git-import-cli. check-in: a89b63ad05 user: dmestas tags: sub-manifest

13:31
logstore: memoize pickBase candidate reads

pickBase reconstructed each of its five candidates through the delta chain on every call, and the candidate window slides by one artifact per write, so consecutive calls re-read almost the same set. A store-owned memo cuts frame reads 49.7 percent on pack.

The memo is a LogStore field rather than a caller-owned cache threaded through pickBase and putArtifact. A memo keyed by frame offset goes stale when rollbackArtifactBatch truncates and later writes reuse those offsets, and only the store sees that happen -- a caller-owned cache would hand callers an obligation they cannot discharge. It also changes no signature.

assertBaseMemoBelowEnd enforces the invariant at all three post-open truncation sites. Removing the clear from rollbackArtifactBatch makes it fire from a general import-path test, not only its own regression test. check-in: c075a01cfa user: dmestas tags: sub-manifest

13:30
delta: replace the encoder's hash map with a fixed-capacity index

wapp import, user CPU: safe 1076s -> 540s, fast 95s -> 53s. Byte-identical output; the 14 pinned vectors are unchanged.

std.AutoHashMap(u64,u32) + Wyhash is gone. The window is now a comptime 16-byte array read through readInt, and the table is open-addressed with capacity from a bounded counter, so it is never more than half full -- that is the probe's termination argument, asserted rather than assumed.

Most of the safe-side win is not bounds checks. Wyhash.init declares a 48-byte buf as undefined, which runtime_safety poison-fills on every call, and the encoder hashed twice per position -- once for the window, once again inside the map on the resulting key. Removing the map removed both.

The probe's explicit bound sits inside `if (std.debug.runtime_safety)`: present in ReleaseSafe where it can trap, compiled out of ReleaseFast. Written unconditionally it cost 12.2% of the fast import; gated it costs nothing in either mode. check-in: bfb63af209 user: dmestas tags: sub-manifest