Skip to content

prover: run the lib test harness on the shipped allocator - #966

Merged
MauroToscano merged 1 commit into
per-table-gpufrom
pt/harness-allocator
Sep 8, 2026
Merged

prover: run the lib test harness on the shipped allocator#966
MauroToscano merged 1 commit into
per-table-gpufrom
pt/harness-allocator

Conversation

@MauroToscano

Copy link
Copy Markdown
Contributor

The instrument was measuring the allocator

Every host-memory number in the campaign's record came from cargo test -p lambda-vm-prover --lib runs. 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. With MALLOC_MMAP_THRESHOLD_=1048576 both 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.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. Eleven lines in prover/src/lib.rs.

Gate (box A, RTX 5090, this branch, recipe unchanged, no env var)

rung prove max RSS anon peak VRAM peak proof bytes
q=41 272.1 s 89,733,468 kB = 85.6 GiB (pre-registered 85.5 ± 1.0) 69.5 GiB (69.4 ± 1.0) 29,040 MiB (29,040 ± 100) 52,500,232, identical
q=20 138.5 s 46,174,056 kB = 44.0 GiB (43.9 ± 1.0) 35.9 GiB 26,608 MiB (26,640 ± 100) 51,175,464, identical

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.

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.
@MauroToscano
MauroToscano merged commit babe3ce into per-table-gpu Sep 8, 2026
9 checks passed
@MauroToscano
MauroToscano deleted the pt/harness-allocator branch September 8, 2026 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant