pub enum Pacer {
Doubling,
Bounded {
ceiling: NonZeroUsize,
live_factor: NonZeroUsize,
},
}Expand description
How Heap::collect_inner chooses the next paced collection threshold.
Fixed at construction (there is no Cell): a collector that could change
its own schedule mid-run would make “when does this program collect” a
function of history rather than of the heap it was built with, and every
pacing test would become order-dependent.
Both arms exist in one binary so the A/B behind Pacer::from_env is a
single build rather than two, and the branch is taken once per collection
— never on the allocation path.
Variants§
Doubling
ADR-011’s original heuristic: max(previous × 2, INITIAL), unbounded.
Retained as the measured-against arm; see ADR-112.
Bounded
max(min(previous × 2, ceiling), live × live_factor, INITIAL).
Constructible only through Pacer::bounded, which clamps both fields,
so “a ceiling below the first threshold” and “zero headroom” have no
spelling.
Fields
ceiling: NonZeroUsizeThe largest the ratchet term may reach. It does not bound the
whole expression; see Pacer::next_threshold.
live_factor: NonZeroUsizeThe multiple of the measured live set the threshold must leave room for, whatever the ceiling says.
Implementations§
Source§impl Pacer
impl Pacer
Sourcepub const DEFAULT: Pacer
pub const DEFAULT: Pacer
What a Heap paces with when nothing says otherwise.
One named constant rather than a literal at the sites that need it, so “what does this workspace’s collector actually do” has exactly one answer to read.
Sourcepub const fn bounded(ceiling: usize, live_factor: usize) -> Pacer
pub const fn bounded(ceiling: usize, live_factor: usize) -> Pacer
The bounded rule, with both parameters clamped into the range in which they mean something.
A ceiling below INITIAL_COLLECT_THRESHOLD is raised to it: the first
threshold is already INITIAL, so a lower ceiling would describe a
heap that had exceeded its own bound before its first allocation. A
live_factor of zero is raised to one: it would delete the mandatory
term, which is the whole anti-thrash half of the rule. Neither clamp is
a convenience — they are why the two illegal states have no spelling
(a_bounded_pacer_cannot_be_built_with_a_ceiling_below_the_first_threshold).
Sourcepub fn next_threshold(self, previous: usize, live: usize) -> usize
pub fn next_threshold(self, previous: usize, live: usize) -> usize
The threshold the collection that just finished sets for the next one.
previous is the threshold that was in force; live is the block bytes
Heap::sweep just measured.
The ceiling clamps the ratchet term only, and never the whole
expression. min(ceiling) applied to the result would make a program
whose live set legitimately exceeds the ceiling collect on essentially
every allocation, which is a thrash bug and not a memory bound. The
ceiling bounds speculative growth — the part of the threshold that is
a guess about the future; live × live_factor is mandatory headroom,
a statement about the present, and it must be allowed to exceed the
ceiling. ADR-112 decision 2 is the argument, and
a_bounded_pacer_gives_a_large_live_set_its_headroom is the test that
fails if someone folds the ceiling over the max.
The rule is monotonically non-decreasing up to the ceiling — once
previous >= ceiling, min(previous × 2, ceiling) == ceiling — so no
separate growth floor is needed: the ratchet-to-ceiling is the floor,
and it is what keeps a shrinking live set from dragging the threshold
back down toward it
(a_shrinking_live_set_does_not_lower_the_threshold_below_the_ceiling).