Skip to main content

SpeedGate

Struct SpeedGate 

Source
pub struct SpeedGate { /* private fields */ }
Expand description

Whether a wall-clock ratio measured in this build is about the SHIPPED codegen.

It is not the optimisation level: [profile.test] already carries opt-level = 2. It is codegen LAYOUT. [profile.test.package.gam-models] sets codegen-units = 16 and the test profile carries no LTO, while [profile.release] is codegen-units = 1 plus thin-LTO, and the whole margin of a compiled-vs-hand row kernel can be cross-CGU inlining. A ratio taken in the test profile therefore measures a different program than the one that ships, and a debug build measures fixed per-call overhead and nothing else. Every speed gate in this workspace opens only there.

That decision is made by the TEST that opens the gate, never by this module: test code may query its own build configuration, library code may not (build.rs bans cfg!(debug_assertions) outside test modules, because a library branch that only runs in one build configuration silently means something else in the other). A gate opened in the dev lane would assert about the wrong program, so the test returns before opening it:

if cfg!(debug_assertions) {
    return; // dev lane: the codegen is not the shipped one
}
let mut gate = SpeedGate::open("RIGID-BERNOULLI-VGH-932");

One speed gate: a named set of paired cells, each printed as it is measured and all asserted together at the end.

This is the ONE shape a wall-clock contract takes in this workspace, and its call site is the marker the release lane derives the gate population from: scripts/speed_gates.py walks the crates for every #[test] whose body calls SpeedGate::open, resolves each to an exact test path in the compiled release binary, runs exactly that set, and refuses a run in which any derived gate did not execute. A gate therefore cannot be forgotten by a name-prefix filter, cannot print ok having asserted nothing, and cannot assert in a lane whose codegen is not the shipped one.

§Shape of a gate

// parity pins run in EVERY build, before the gate opens
if cfg!(debug_assertions) {
    return; // dev lane: skip the measurement, its verdict is about the wrong program
}
let mut gate = SpeedGate::open("RIGID-BERNOULLI-VGH-932");
let timing = paired_interleaved(15, 300_000, seed, production_arm, hand_arm);
gate.faster("y=1", &timing, "production", "hand");
gate.finish();

The profile check is the test’s, not the gate’s (see the module documentation above): a gate that is opened always asserts, and the dev lane does not pay for millions of timed iterations whose result it could not use because the test never opens one there.

§Two contracts, no third

  • SpeedGate::faster — the #932 contract: A (the compiled lowering) must be strictly faster than B (the strongest hand path or the generic tower it specialises). Loss when median_ratio() <= 1.
  • SpeedGate::not_slower — for a cell whose two arms do the same work by construction and where no speed claim is made: A must not be measurably slower than B, where “measurably” is the measurement’s OWN resolution, PairedTiming::ratio_resolution. Loss when median_ratio() + ratio_resolution() < 1. There is no chosen tolerance here: the instrument reports its noise floor, and that is the only denominator a parity bar can honestly be stated in.

A gate that is opened and dropped without SpeedGate::finish panics, and a gate finished with no cells panics: both are gates that verified nothing.

Implementations§

Source§

impl SpeedGate

Source

pub fn open(token: &'static str) -> Self

Open a gate. It always asserts; the test decides whether this build is one whose verdict is meaningful before calling (see the type docs).

token is the stable, grep-able prefix every cell line of this gate is printed under (for example RIGID-BERNOULLI-VGH-932).

Examples found in repository?
examples/paired_timing_report.rs (line 113)
91fn main() {
92    let input: [f64; 17] = std::array::from_fn(|index| 0.35 + 0.07 * (index as f64 + 1.0).sin());
93    for (compiled, canonical) in compiled_bundle4(input).iter().zip(canonical_bundle4(input).iter()) {
94        assert!((compiled - canonical).abs() <= 1e-12 * canonical.abs().max(1.0));
95    }
96    let timing = paired_interleaved(
97        15,
98        2_000,
99        0x9320_AB,
100        batched(64, |nudge| {
101            let mut x = input;
102            x[4] += nudge;
103            compiled_bundle4(x).iter().sum()
104        }),
105        batched(64, |nudge| {
106            let mut x = input;
107            x[4] += nudge;
108            canonical_bundle4(x).iter().sum()
109        }),
110    );
111    println!("{}", timing.summary("compiled_bundle4", "nine_top_channels"));
112    // The two contracts a gate can carry, printed the way a gate prints them.
113    let mut gate = SpeedGate::open("PAIRED-TIMING-REPORT");
114    gate.faster("order=4 bundle", &timing, "compiled_bundle4", "nine_top_channels");
115    gate.not_slower("order=4 bundle", &timing, "compiled_bundle4", "nine_top_channels");
116    gate.finish();
117}
Source

pub fn faster(&mut self, cell: &str, timing: &PairedTiming, a: &str, b: &str)

Record a cell whose contract is “A is strictly faster than B”.

Examples found in repository?
examples/paired_timing_report.rs (line 114)
91fn main() {
92    let input: [f64; 17] = std::array::from_fn(|index| 0.35 + 0.07 * (index as f64 + 1.0).sin());
93    for (compiled, canonical) in compiled_bundle4(input).iter().zip(canonical_bundle4(input).iter()) {
94        assert!((compiled - canonical).abs() <= 1e-12 * canonical.abs().max(1.0));
95    }
96    let timing = paired_interleaved(
97        15,
98        2_000,
99        0x9320_AB,
100        batched(64, |nudge| {
101            let mut x = input;
102            x[4] += nudge;
103            compiled_bundle4(x).iter().sum()
104        }),
105        batched(64, |nudge| {
106            let mut x = input;
107            x[4] += nudge;
108            canonical_bundle4(x).iter().sum()
109        }),
110    );
111    println!("{}", timing.summary("compiled_bundle4", "nine_top_channels"));
112    // The two contracts a gate can carry, printed the way a gate prints them.
113    let mut gate = SpeedGate::open("PAIRED-TIMING-REPORT");
114    gate.faster("order=4 bundle", &timing, "compiled_bundle4", "nine_top_channels");
115    gate.not_slower("order=4 bundle", &timing, "compiled_bundle4", "nine_top_channels");
116    gate.finish();
117}
Source

pub fn not_slower( &mut self, cell: &str, timing: &PairedTiming, a: &str, b: &str, )

Record a cell whose contract is “A is not measurably slower than B”, measurable meaning beyond the paired measurement’s own resolution.

Examples found in repository?
examples/paired_timing_report.rs (line 115)
91fn main() {
92    let input: [f64; 17] = std::array::from_fn(|index| 0.35 + 0.07 * (index as f64 + 1.0).sin());
93    for (compiled, canonical) in compiled_bundle4(input).iter().zip(canonical_bundle4(input).iter()) {
94        assert!((compiled - canonical).abs() <= 1e-12 * canonical.abs().max(1.0));
95    }
96    let timing = paired_interleaved(
97        15,
98        2_000,
99        0x9320_AB,
100        batched(64, |nudge| {
101            let mut x = input;
102            x[4] += nudge;
103            compiled_bundle4(x).iter().sum()
104        }),
105        batched(64, |nudge| {
106            let mut x = input;
107            x[4] += nudge;
108            canonical_bundle4(x).iter().sum()
109        }),
110    );
111    println!("{}", timing.summary("compiled_bundle4", "nine_top_channels"));
112    // The two contracts a gate can carry, printed the way a gate prints them.
113    let mut gate = SpeedGate::open("PAIRED-TIMING-REPORT");
114    gate.faster("order=4 bundle", &timing, "compiled_bundle4", "nine_top_channels");
115    gate.not_slower("order=4 bundle", &timing, "compiled_bundle4", "nine_top_channels");
116    gate.finish();
117}
Source

pub fn finish(self)

Assert that every recorded cell met its contract, naming all that did not. Consumes the gate.

Examples found in repository?
examples/paired_timing_report.rs (line 116)
91fn main() {
92    let input: [f64; 17] = std::array::from_fn(|index| 0.35 + 0.07 * (index as f64 + 1.0).sin());
93    for (compiled, canonical) in compiled_bundle4(input).iter().zip(canonical_bundle4(input).iter()) {
94        assert!((compiled - canonical).abs() <= 1e-12 * canonical.abs().max(1.0));
95    }
96    let timing = paired_interleaved(
97        15,
98        2_000,
99        0x9320_AB,
100        batched(64, |nudge| {
101            let mut x = input;
102            x[4] += nudge;
103            compiled_bundle4(x).iter().sum()
104        }),
105        batched(64, |nudge| {
106            let mut x = input;
107            x[4] += nudge;
108            canonical_bundle4(x).iter().sum()
109        }),
110    );
111    println!("{}", timing.summary("compiled_bundle4", "nine_top_channels"));
112    // The two contracts a gate can carry, printed the way a gate prints them.
113    let mut gate = SpeedGate::open("PAIRED-TIMING-REPORT");
114    gate.faster("order=4 bundle", &timing, "compiled_bundle4", "nine_top_channels");
115    gate.not_slower("order=4 bundle", &timing, "compiled_bundle4", "nine_top_channels");
116    gate.finish();
117}

Trait Implementations§

Source§

impl Drop for SpeedGate

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.