Skip to main content

DifferentialSession

Struct DifferentialSession 

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

Three revision worktrees whose lifetime is a whole calibration run rather than a single observation.

The observation cost the harness cannot avoid is compiling the project under test three times. The cost it can avoid is compiling the project’s dependencies three times per observation, which is what a fresh worktree per observation forces: the operator’s --target-dir is tree-relative (the ledger refuses any target directory that escapes its worktree), so destroying the tree destroys the build directory with it and the next observation starts from zero. Holding the same three trees open across observations and moving them with git checkout keeps those build directories alive. Measured on rust-lang/log, one run: 16.1 s in a fresh tree against 12.0-13.4 s in a held tree switched to a new revision.

Three properties are deliberately preserved, because each of them is load bearing for what the calibration claims:

  • Every run is still really run. A held tree makes a run cheaper, never skipped: cargo test re-links and re-executes the test binaries even when nothing changed (measured: four test binaries execute in a fully warm tree). That is what the ledger’s independent_runs assumption needs, and it is why this is not a result cache.
  • The three trees stay separate. Each keeps its own build directory, so the cross-tree artifact contamination that invalidated an earlier receipt cannot recur, and the three concurrent runs still cannot contend on one tool lock.
  • Nothing survives the run. The worktrees and their root are removed by Self::close, and again by Drop if a caller returns early, so an operator’s repository is left as it was found.

What it does change, stated rather than buried: an observation now starts in a tree that holds an earlier observation’s untracked build output. Tracked content is exact — the checkout is forced, so the tree matches the revision — but a command that writes untracked files into its tree will see them again. Callers that need a pristine tree per observation still have one: run_differential opens and closes a session around a single observation, and choir-bridge calibrate --fresh-worktrees selects that path for a whole run.

Which tree plays which role is decided per observation: a revision is assigned to a tree that is already sitting on it, when one is. Replaying consecutive first-parent merges — the shape of a calibration corpus — makes this pay every observation, because a merge’s first parent is the previous observation’s merge: the tree that just ran that merge becomes the parent-a tree with a no-op checkout, so its command rebuilds nothing at all. Measured on rust-lang/log, one warm tree, solo: 8.9 s for a no-op revision against 15.0 s for a one-merge move.

Implementations§

Source§

impl DifferentialSession

Source

pub fn open(repo: &Path) -> Self

Names a session root under repo. Creates nothing until the first observation; opening cannot fail.

Source

pub fn close(self) -> Result<(), String>

Removes the three worktrees and the session root, reporting the first failure. Drop repeats this as a best-effort backstop, so a caller that returns early still leaves nothing behind — but only close can tell the caller that cleanup failed.

§Errors

A worktree or the session root could not be removed.

Trait Implementations§

Source§

impl Drop for DifferentialSession

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<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.