Pure, robust statistics for benchmark mode (mix excessibility.debug --benchmark=N).
Benchmark mode loops a test N times and collects, per run, a map of
measurement key to duration_ms. summarize/2 turns those per-run samples
into a value-free benchmark.json artifact.
Timing contract
Timing is diagnostic, never comparable across environments and never part of the digest. A key being free of outliers means "no relative outlier in these samples," not "fast." Robust stats (median + MAD) are used deliberately instead of mean/stddev so a single slow run cannot skew the picture.
Sample 1 is treated as cold (first-run costs: compilation, connection warmup, cold caches). Samples 2..N are warm. Cold and warm are reported separately per the timing contract; mixing them would poison the median.
Input shape
samples is a list (one entry per run, in run order) of maps:
[
%{"MyLive/mount" => 12.0, "query:sha256:aaa" => 5.0},
%{"MyLive/mount" => 6.0, "query:sha256:aaa" => 3.0},
...
]Keys are opaque strings (the mix task uses "<view>/<callback>" and
"query:<fingerprint>"); values are duration_ms numbers. Keys may vary
between runs — each key is summarized over the runs in which it appears.
Output shape
%{
schema: "excessibility.benchmark/v1",
runs: n,
cold: %{key => %{median: number, mad: number, sample: number}}, # run 1 only
warm: %{key => %{median: number, mad: number, samples: integer}}, # runs 2..n
outliers: [
%{key: String.t(), run: pos_integer(), value: number,
median: number, mad: number, threshold: number}
],
notes: [String.t()]
}outliers is advisory only — a warm sample must clear three gates to
be flagged, so scheduler/timer jitter in the sub-millisecond band is not
reported as actionable evidence:
- the robust statistical threshold
value > median + k * mad(kdefaults to 6,:k); - a minimum absolute effect
value - median >= min_abs_ms(defaults to 1.0 ms,:min_abs_ms); and - a minimum relative effect
value >= median * min_rel_factor(defaults to 1.5,:min_rel_factor).
Set min_abs_ms: 0.0, min_rel_factor: 1.0 to restore pure-statistical
flagging. Each outlier carries weak_evidence: true when its key has fewer
than 5 warm samples, and a run-level notes entry labels the whole
artifact as weak evidence when there are too few warm runs for stable MAD
inference. It never encodes a pass/fail verdict; the raw value, median,
mad, threshold, and 1-based run index are attached so a reader can judge
for themselves.
Summary
Functions
Median Absolute Deviation: median(for x <- xs, do: abs(x - median(xs))).
Median of a list of numbers.
Summarize per-run samples into the robust cold/warm benchmark artifact.
Functions
Median Absolute Deviation: median(for x <- xs, do: abs(x - median(xs))).
A robust measure of spread. Returns nil for an empty list and 0 when all
values are identical.
Median of a list of numbers.
Returns the middle element (odd length) or the mean of the two middle
elements (even length). Returns nil for an empty list.
Summarize per-run samples into the robust cold/warm benchmark artifact.
Options:
:k- outlier multiplier; a warm sample is flagged when it exceedsmedian + k * mad. Defaults to6.