prover: run the lib test harness on the shipped allocator - #966
Merged
Conversation
Every host-memory number in the campaign's record was taken from `cargo test -p lambda-vm-prover --lib` runs, and that binary had no `#[global_allocator]`, so it ran on glibc malloc while the only shipped binary, `bin/cli`, runs jemalloc. The two return freed memory to the kernel differently, and it showed: at the wrap's q=41 rung the harness read 98.6 GiB of max RSS at the tip against 86.7 GiB at the parent of #956, a difference that came entirely from 12 GiB of freed column buffers glibc kept resident in an arena, not from anything the prover held. The regression attributed to #956 was a measurement of the allocator. Install jemalloc as the lib test harness's global allocator, the same one `bin/cli/src/main.rs` installs, so a harness measurement is a production-allocator measurement. `tikv-jemallocator` was already a dev-dependency; `tests/calibration.rs` already installs it for its own crate. Integration tests remain separate crates. What the control runs established, with `MALLOC_MMAP_THRESHOLD_=1048576` standing in for the allocator change (box A, one run per cell). MEMORY: the saving is large, reproducible and controlled. From a high-retention start it is about 13 GiB at q=41 (98.6 to 85.5 GiB, tip) and about 6 GiB at q=20 (51.0 to 43.9 GiB); from a low-retention start it is about 1.2 GiB (86.7 to 85.5 GiB at the parent of #956, 45.1 to 43.9 GiB at q=20). The parent and the tip read 604 kB apart under the knob, which is the control. TIME: measured within plus or minus 3 percent with no consistent sign across the four cells, one run each, three of them inside what a single run resolves; no time effect is claimed in either direction, and establishing one needs paired repeats per shape. One observation, not a mechanism: under the knob the runs agree closely with each other (140.1 to 140.2 s at q=20, 275 to 278 s at q=41) while the runs without it scatter.
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.
The instrument was measuring the allocator
Every host-memory number in the campaign's record came from
cargo test -p lambda-vm-prover --libruns. That binary had no#[global_allocator], so it ran on glibc malloc, while the only shipped binary,bin/cli, runs jemalloc. At the wrap's q=41 rung the harness read 98.6 GiB of max RSS at the tip against 86.7 GiB at the parent of #956. The 2 Hz RssAnon curves showed the two runs identical step for step except at one point: at the end of the widest table's aux build the parent released a 12.02 GiB buffer and a 7.4 GiB buffer together, the tip released only the 7.4 GiB one and carried the 12.02 GiB to process exit. That buffer is the aux build's host column copy of the main trace (3,076 vectors of 4 MiB), freed by the code in both runs; glibc kept it resident in an arena in one and not the other, depending on allocation history. WithMALLOC_MMAP_THRESHOLD_=1048576both heads read 85.5 GiB, 604 kB apart, with identical proof bytes. The regression attributed to #956 was a measurement of the allocator; #956's 2.8 GiB device saving stands.The fix
Install jemalloc as the lib test harness's global allocator, the one
bin/cli/src/main.rsinstalls, so a harness measurement is a production-allocator measurement.tikv-jemallocatorwas already a dev-dependency;tests/calibration.rsalready installs it for its own crate. Integration tests remain separate crates. Eleven lines inprover/src/lib.rs.Gate (box A, RTX 5090, this branch, recipe unchanged, no env var)
Every cell landed inside the band pre-registered before the run. The decay caveat (jemalloc purges freed extents on a ~10 s decay) did not bite: the next large allocation follows the 12 GiB free by 17 s at q=41.
The practical argument: repeatable, not just lower
Under the shipped allocator the readings agree run to run: the q=20 jemalloc reading and the q=20 glibc-plus-knob reading are within 0.1 GiB of each other (44.0 vs 43.9), and the q=41 pair likewise (85.6 vs 85.5). The glibc-without-knob readings of the same shapes scattered by 6 GiB at q=20 (45.1 vs 51.0 across two heads whose code did not allocate the difference) and by 13 GiB at q=41 (86.7 vs 98.6 across two adjacent commits that differ only in device code). A host-memory measurement that moves by 13 GiB with allocation history cannot rank two commits; one that repeats to 0.1 GiB can. That is worth more than the saving.
Measured with the env-var stand-in, for the record (box A, one run per cell)
MEMORY: from a high-retention start the saving is about 13 GiB at q=41 (98.6 → 85.5 GiB, tip) and about 6 GiB at q=20 (51.0 → 43.9 GiB); from a low-retention start about 1.2 GiB (86.7 → 85.5 at the parent of #956; 45.1 → 43.9 at q=20). The parent and the tip read 604 kB apart under the knob: the control.
TIME: measured within ±3% with no consistent sign across the four cells (−1.6, +1.4, −0.1, +2.8 %), one run each, three of them inside what a single run resolves. No time effect is claimed in either direction; establishing one needs paired repeats per shape.
What changes in the record
85.6 GiB (q=41) and 44.0 GiB (q=20) replace 98.6 and 45.1 as the wrap harness baselines, with the allocator named beside them. The "q=110 killed at 117.8 GiB" ceiling is not affected by any allocator: at q=110 the recipe's host R1 builds a 96 GiB BLAKE3 LDE on a 48 GiB trace before any aux build exists, and it is retired as an envelope number on those grounds.