The timing loop, the repetition model and the interval - #64
Merged
Conversation
The repetition model follows Kalibera and Jones. A pilot runs a small balanced design across builds, processes and iterations, the variance is decomposed across those three levels, and the budget is spent at the level that carries it. Running a workload more times at a level that is already settled buys nothing, which is the part of that paper people skip. The headline statistic is the median with a percentile bootstrap interval. The minimum is carried as its own column rather than folded into anything. The stopping rule is handed one Series and nothing else. It cannot see a rival or a baseline, because the signature does not let it and Stop is non_exhaustive with no variant that carries anything but a scalar. Adding repetitions until a comparison turns significant is how a result gets manufactured, and the defence against it is not having the comparison in scope at the point where the decision is made.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #5.
This is the measurement half of
bench-core. No benchmark lives in the crate, only the loop, the statistics and the repetition model, so that the timing code can be read in one sitting by somebody who wants to check it.The repetition model
Kalibera and Jones is usually remembered as run it more times. The actual contribution is that variance lives at a particular level, and the repetition budget should be spent at the level that carries it. A
Pilotis a small balanced design across builds, processes and iterations.Pilot::decomposesplits the variance across those three levels using the ordinary nested random effects arithmetic, andplangives the budget to the dominant level and two repetitions to each of the others.Two and not one for the settled levels, because one gives no way to notice later that a level started carrying variance after all, which happens the first time somebody changes a compiler flag.
The decomposition is written as plain nested loops rather than iterator chains. It is arithmetic a reader should be able to check against a textbook, and a chain of folds is harder to check than it is to write.
The statistics
The headline is the median with a percentile bootstrap confidence interval. The mean is carried because everyone else reports it, not because it is the better statistic. The minimum gets its own column rather than being folded into anything, because the minimum is a statistic about the luckiest run and printing it where a reader expects a typical value is the most common distortion in this field.
The bootstrap seed is configuration rather than something taken from the clock, so summarising the same samples twice gives the same interval. That matters more than it sounds, because the stopping rule is keyed on the width of that interval and a rule built on a number that moves on its own would stop somewhere different every run.
Two thousand resamples rather than the ten thousand people reach for out of habit. Ten thousand is right for a percentile of a tail. For an interval on a median it moves the endpoints by less than the measurement noise they describe, and the stopping rule pays this cost repeatedly while a run is in progress.
The stopping rule
Stop::should_stoptakes the series being measured and nothing else. It cannot see a baseline, a rival, or yesterday's number, because the signature does not let it.Stopisnon_exhaustiveand no variant carries anything but a scalar, so nobody outside the crate can add a variant that sees a second series.That is the whole design. Adding repetitions until a comparison becomes significant is how a result gets manufactured, and it does not feel like cheating while you are doing it, because you are just collecting more data and more data is good. The defence is not discipline, it is not having the number available at the point where the decision is made.
The issue asks for a test that a comparison driven rule is not reachable. The unreachable half is a type signature, so the test checks the observable half: the same workload measured on a day when the rival was far behind and on a day when it was a hair ahead stops at the same sample and produces the same summary.
Timing::check_everyexists because a bootstrap over the whole series is not free and one more sample rarely moves the interval enough to change the answer. A run ends at a multiple of it, which is worth knowing when reading a sample count back, so there is a test that says so.What is here
stats.rs, the summary, the bootstrap, and a small deterministic generator so the crate does not take a dependency for eight lines of arithmeticrepetition.rs, the pilot, the variance decomposition and the budgetmeasure.rs, the loop, the stopping rule andtimeUnused dependencies were dropped from
bench-corewhile I was in there. The crate now takesserdeandthiserrorand nothing else.