Reduce peak RSS: smaller read buffers + per-role SQLite cache budgets - #80
Merged
Conversation
Two memory optimizations, both measured perf-neutral (interleaved A/B on btrfs, io/cpu-threads=8; see scripts/bench-ram.sh): 1. Read/hash buffer 8 MiB -> 1 MiB (file_scan.c). One static __thread buffer per csum thread, so at --io-threads=8 the old size cost 64 MiB of resident buffers on large-file trees. Files bigger than the buffer are already read in passes, and 1 MiB saturates sequential read throughput (scan is I/O/metadata-bound). Large-file tree: peak RSS 70 -> 15 MiB, wall unchanged (2.00s). 2. Per-connection SQLite page-cache budgets (dbfile). Every connection defaulted to 64 MiB, and on a large hashfile that cache fills toward the cap. Connections that don't run the heavy dedupe joins now get less: the find_dupes search pool (up to cpu_threads connections) 32 MiB, and the walkers (which only read the fs-uuid config, if that) 2 MiB. The loader/reader/writer keep 64 MiB. Hot DB pages stay warm in the shared OS page cache, so the joins keep their speed. 250k-file dup-heavy tree (66 MiB hashfile): peak RSS ~200 -> 167 MiB, wall unchanged (10.9s); the gap widens with hashfile size. Correctness unchanged: scripts/verify.sh green (88 tests + valgrind). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two memory optimizations for large trees, both measured perf-neutral. Investigation started from massif: peak heap was 88% the per-thread read buffer, and the 64 MiB SQLite cache (a lazily-filled ceiling) multiplies across the walker and search-pool connections at scale.
Changes
file_scan.c). Onestatic __threadbuffer per csum thread, so at--io-threads=8the old size cost 64 MiB of resident buffers on large-file trees. Files larger than the buffer are already read in successive passes, and 1 MiB saturates sequential read throughput (scan is I/O/metadata-bound).dbfile). Every connection defaulted to 64 MiB. Connections that don't run the heavy dedupe joins now get less: the find_dupes search pool (up tocpu_threadsconnections) → 32 MiB, and the walkers (which only read the fs-uuid config, if that) → 2 MiB. The loader/reader/writer keep 64 MiB. Hot pages stay warm in the shared OS page cache, so the joins keep their speed. (sqlite3_soft_heap_limit64was ruled out — the distro SQLite isn't built withSQLITE_ENABLE_MEMORY_MANAGEMENT.)Benchmarks (btrfs, io/cpu-threads=8, interleaved A/B via
scripts/bench-ram.sh)200 → 167 MiB (−35)The #2 saving grows with hashfile size (this was only a 66 MiB hashfile; the 870 MiB/1.7M-file reports are where the ≤8 search connections × 64 MiB really bite).
Also
scripts/bench-ram.sh(non-destructiveoans -r, peak RSS + wall).scripts/verify.shgreen (88 tests + valgrind).Follow-ups not in this PR (measured separately): shrinking
seen_inodes(~50 B/file) and tryingPRAGMA mmap_size.🤖 Generated with Claude Code