meter
Measure an image's peak memory against recording length, rate and channels.
The platform claims memory for a run by
:meth:ResourceRequirements.estimate_peak_system_memory_mib: a base the
author sets, plus terms that scale with the recording's hours, its highest
native sample rate and the number of selected channels. A claim that is too
low fails the run out of memory, and only on the long recordings a short
fixture never shows. The meter runs the image on recordings chosen so that
length and padding effects appear, reads each run's peak, and compares it
with the claim.
The points (:func:select_points):
- every length at the middle rate with the largest channel selection;
- the smallest selection, and the lowest and highest rates, at two lengths;
- for each rate, the length just past a power-of-two sample count. Libraries that resample with an FFT pad each channel to the next power of two, so memory steps with length and roughly doubles just past a boundary.
Each run is the platform's run: the same confinement, the declared cores and
thread variables, and the claim in the manifest. Only the container's memory
limit is raised, so that the measurement is not cut short. With a runner that
stages recordings (DockerRunner(stage_recording=True)), the recording is
copied into a volume by a separate container first, so that its page cache
is charged to that copy, as it is to whatever writes the file on the
platform. The kernel reclaims that cache before it runs out of memory, so it
is not memory the claim must cover.
:func:propose fits the measured peaks by least squares to the claim's
terms and proposes terms that leave the required headroom at every point,
with rates above the fitted ones so that the headroom holds beyond the
measured range.
Fit
dataclass
¶
A least-squares fit of the peaks to some of the claim's terms.
describe()
¶
Write the fit as a formula.
MeterPoint
dataclass
¶
One recording the image is measured on.
describe()
¶
24.00 h, 3 ch, 512 Hz and the recording's name when it is real.
MeterReport
dataclass
¶
The outcome of a metering run.
ok
property
¶
Every point was measured and the claim holds at each.
growth()
¶
Find the largest rise of the peak with length, at one rate and selection.
Returns:
| Type | Description |
|---|---|
tuple[float, int, float, int] | None
|
|
tuple[float, int, float, int] | None
|
rises by more than :data: |
tuple[float, int, float, int] | None
|
None otherwise. |
problems()
¶
Every reason the claim does not hold; empty when it does.
to_dict()
¶
Return a JSON-ready view of the report.
MeterRow
dataclass
¶
Proposal
dataclass
¶
Memory terms that leave the required headroom at every measured point.
boundary_hours(rate_hz, shortest, longest)
¶
Return the length just past the largest power-of-two sample count in a range.
Returns:
| Type | Description |
|---|---|
float | None
|
Hours, in whole seconds, :data: |
float | None
|
of one channel at |
float | None
|
between |
configured_terms(resources)
¶
Return the length terms a configuration uses.
describe_claim(resources)
¶
Write the claim as a formula, e.g. 800 + 450*h*kHz + 90*h*ch*kHz MiB.
field_name(term)
¶
Name the resources field that holds a term: base or one of :data:TERMS.
fit_terms(samples, terms=TERMS)
¶
Fit peaks to the base and terms by least squares, every coefficient at least 0.
Every subset of the terms is solved and the one with the smallest residual and no negative coefficient is kept, preferring fewer terms on a tie: a negative term would promise memory back for longer recordings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
samples
|
Sequence[tuple[float, int, float, float]]
|
|
required |
terms
|
Sequence[str]
|
The terms the claim may use, a subset of :data: |
TERMS
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If there is nothing to fit. |
has_length_term(resources)
¶
Whether any term of the claim scales with the recording's length.
measure_point(image, config, runner, point, workdir, *, limit_mib, timeout_seconds=900, seed=0, upstream_events=None)
¶
Write one point's recording and manifest, run the image on it, and read the peak.
meter(*, image, config, runner, limit_mib=DEFAULT_LIMIT_MIB, hours=DEFAULT_HOURS, rates=DEFAULT_RATES, recordings=(), headroom=HEADROOM, timeout_seconds=900, seed=0, upstream_events=None, pull=False, keep=None, progress=None)
¶
Measure an image's peak memory on each point and compare it with the claim.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image
|
str
|
The image reference to run. |
required |
config
|
str | Path
|
The configuration file whose claim is checked. |
required |
runner
|
Runner
|
The container runtime, normally a :class: |
required |
limit_mib
|
int
|
Each run's memory limit, high enough not to cut it short. |
DEFAULT_LIMIT_MIB
|
hours
|
Sequence[float]
|
The recording lengths. |
DEFAULT_HOURS
|
rates
|
Sequence[int]
|
The sample rates of synthetic recordings. |
DEFAULT_RATES
|
recordings
|
Sequence[str | Path]
|
Real EDF recordings to cut or extend instead. |
()
|
headroom
|
float
|
The fraction of the peak the claim must exceed it by. |
HEADROOM
|
timeout_seconds
|
float
|
A run's timeout, plus :data: |
900
|
seed
|
int
|
Makes the synthetic recordings reproducible. |
0
|
upstream_events
|
str | Path | None
|
An events document for a configuration that declares an events input. |
None
|
pull
|
bool
|
Pull the image before inspecting it. |
False
|
keep
|
str | Path | None
|
Keep each point's files in this directory. |
None
|
progress
|
Callable[[str], None] | None
|
Called with a line before and after each run. |
None
|
Returns:
| Type | Description |
|---|---|
MeterReport
|
The report. |
MeterReport
|
why not. |
propose(report, *, terms=None, margin=MARGIN)
¶
Propose memory terms from the measured peaks.
The peaks are fitted to terms: by default the length terms the
configuration uses, or every term when it uses none. The proposal keeps
the fitted shape and scales it up:
- the base is the report's headroom over the fitted base;
- every fitted rate is multiplied by one factor, the smallest that gives
each measured point the headroom, and at least
marginso that the claim grows faster than the peaks beyond the longest recording measured. The points just past a power-of-two sample count usually set it, which carries their padding to the boundaries between the points; - rates are rounded up to a round number (105, 380, 1250), the base to a
multiple of :data:
BASE_STEP_MIB, and the base is raised if a point still lacks the headroom.
Raises:
| Type | Description |
|---|---|
ValueError
|
If a point failed, or nothing was measured. |
regressors(hours, n_channels, rate_hz)
¶
Return what each of the claim's terms is multiplied by, for one recording.
render_proposal(proposal, report)
¶
Render the resources: block for the configuration, measurements as comments.
render_table(report)
¶
Render the measurements, one line per point, and the verdict.
select_points(config, *, hours=DEFAULT_HOURS, rates=DEFAULT_RATES, recordings=())
¶
Choose the recordings to measure, so that length and padding effects show.
Synthetic recordings (no recordings) take every channel at the point's
rate. With recordings the rate is each file's own, and every point is
that file cut or extended.
Returns:
| Type | Description |
|---|---|
tuple[list[MeterPoint], list[str]]
|
The points, without repeats, and notes on what was left out. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If no point can be chosen: no length, no rate the configuration accepts, or a recording that cannot satisfy it. |