Skip to content

test: A/B bench harness that drops rounds it could not measure honestly - #171

Open
BenjaminDEMAILLE wants to merge 1 commit into
scverse:mainfrom
BenjaminDEMAILLE:bd/bench-ab
Open

test: A/B bench harness that drops rounds it could not measure honestly#171
BenjaminDEMAILLE wants to merge 1 commit into
scverse:mainfrom
BenjaminDEMAILLE:bd/bench-ab

Conversation

@BenjaminDEMAILLE

Copy link
Copy Markdown
Contributor

test/bench_ab.sh: interleaved A/B of two binaries, which drops the rounds it could not measure honestly instead of averaging over them.

Why

Four traps, each of which I fell into at least once while measuring this aligner over one session. Three are now enforced by the script; the fourth is documented in its header because no script can enforce it.

Idle was checked once, at the start. A previous harness sampled CPU idle before the first run and never again. Three separate measurements then drifted below the threshold mid-series and it kept going, leaving the contaminated rounds to be spotted by eye. This samples idle before and after every run, drops the round if any of the four samples falls short, and prints the count:

round1  binA=3.33s binB=3.42s  idle>=84.92%
round3  binA=3.25s binB=3.22s  DROPPED (idle fell to 71.4%)

kept 4 of 6 rounds (2 dropped)

A median over four surviving rounds now cannot be read as a median over six. No silent truncation.

Load average is the wrong signal. It is an exponential average over minutes, so it stays high long after the offending job has gone. An earlier guard refused to measure at 2.24 on a machine whose sixteen cores were all free. The check is on CPU idle percentage, which answers the question actually being asked.

A median can be smaller than the noise. The script prints the spread within each side next to the difference between the medians, and when the difference is smaller it says so:

median  binA=3.305s  binB=3.25s  delta=-1.7%
spread  binA=0.08s   binB=0.20s

the difference between the medians is smaller than the spread within a side.
that is not a result: report it as unmeasured rather than as a speedup.

That message is not hypothetical. Two changes I measured this week looked like wins on medians alone and were inside the spread; both are now described as unmeasured in their PRs (#169, #170).

Both sides must see identical argv. @PG CL: records the command line verbatim, so running ./old against ./new, or writing to differently-named output directories, makes the BAM differ in the header and in its compressed size. That cost me two false "output is not neutral" alarms. Each side is copied to rustar-aligner inside its own directory and run with --outFileNamePrefix ./.

Timing a total hides the part that changed. Only in the header, since it is a judgement the script cannot make: BAM writing is 1-4% of a yeast run, so a total dominated by alignment cannot resolve a change to the writer at any reachable scale. Run one configuration with --outSAMtype None alongside and read the difference. #164 was closed because that difference, once isolated, showed the change was a regression rather than the improvement its description claimed.

Usage

test/bench_ab.sh <binA> <binB> <genomeDir> <reads.fastq> [threads] [rounds]

BENCH_MIN_IDLE=88   percent CPU idle a round must hold to count
BENCH_MODE="None"   --outSAMtype words (quote the two-word forms)
BENCH_ARGS=""       extra flags passed to both sides

It exits non-zero if every round was dropped, rather than printing a median over nothing.

Scope

Test tooling only: no source change, no dependency, nothing user-facing, so no CHANGELOG entry. The numbers in #167 and #170 were produced with the same method; this makes the method reproducible rather than living in my shell history.

Happy to add a pointer to it from CONTRIBUTING.md's verification section if you want one there, but I did not want to edit that file uninvited.

Related: #168 has the allocation measurements taken with this method, including the result that removing 7.6% of the allocations moved nothing.

Every perf measurement on this project needs the same three guards, and
I got each of them wrong at least once in a single session:

An earlier harness sampled CPU idle once, before the first run. Three
separate measurements then drifted below the threshold mid-series and it
kept going, leaving me to spot the contaminated rounds by eye in the
output. This samples idle before and after every run, drops a round if
any of the four samples falls short, and prints how many it dropped, so
a median over four surviving rounds cannot be mistaken for a median over
six.

The check is on CPU idle rather than load average, because load average
is an exponential average over minutes: it refused to measure at 2.24 on
a machine whose cores were all free.

It reports the spread within each side next to the difference between
the medians, and says so in as many words when the difference is
smaller. Two changes I measured looked like wins on medians alone and
were inside the spread.

Both sides run as ./rustar-aligner with --outFileNamePrefix ./ from
inside their own directory, because the @pg CL: line records argv
verbatim: running ./old against ./new is enough to make the output
differ, which cost me two false "output is not neutral" alarms.

The header documents the fourth trap, which no script can enforce:
timing a total hides the part that changed. BAM writing is 1-4% of a
yeast run, so a total dominated by alignment cannot resolve a change to
the writer. Run the None configuration alongside and read the
difference.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant