Read-budget (N) benchmark — 2,000 reads is enough (default cut ~100× from 200k) #71
lhqing
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
A developer lesson. How many reads does
probe→resolveactually need to call a library'schemistry correctly, and what do size and speed cost for reads beyond that? The default was
DEFAULT_MAX_READS = 200 000, set arbitrarily. This measured whether it should be smaller. It should:the default is now 2 000 (
PROBE_VERSION2026.7.2, issue #63).Related: issue #63 (the fingerprint that made this measurable), issue #66 (the CPU/MEM profile).
TL;DR
to N = 200 000 — every dataset calls its production chemistry at every N, exit 0, not one flip.
smaller and ~faster than the old 200 000, still robust to the caveats below.
the (advisory) score drifts slightly.
Why this matters
N(the bounded read budget) sets three things at once:tie or a barcode-absent refusal can appear.
seqforge preflight) — subsampled FASTQs + info files + pinned content-address #63 package slices N reads per file; the slice is ~linear in N.probeandresolveare ~linear in N (issue perf: profile of the deterministic compile path — probe + resolve dominate and scale linearly with N #66).So the smallest N that is still correct is a strict win on the other two axes. 200 000 was never
measured; this was that measurement.
Method
260612-worm/mapping/<ACC>, one representativerun (an SRX's FASTQs) each.
resolve_runs(max_reads=N)→ top chemistry, advisory score, rung,exit code; and
preflight(reads=N)→ the slice's package size.manifest.yaml(the full-pipeline ground truth), notthe benchmark itself. A smaller N "matches" if it yields the same top chemistry and exit 0.
3M-february-2018, …), run in parallel across 48 cores. Noharvest— theread budget never touches prose extraction.
Results — the call is N-invariant, and the slice is ~100× smaller
Families: 10×
10x-3p-gex-v3, 2×10x-3p-gex-v2(GSE126954, GSE136049), 1×bd-rhapsody-wta(GSE274290). Every one is production-consistent at every N. The chosen default, N = 2 000, sits
just above the 1k column — ~2× its size, still ~100× under 200k.
(GSE126954 is 10x-v2 from the bytes despite being a "sci-RNA-seq" study — the production manifest
confirms v2; the benchmark is scored against that manifest, not the paper's wording.)
The speed angle (from #66)
probeandresolvescale ~linearly with N, so the read cut is also a ~100× compute win. One cautionon the raw profiler numbers: pyinstrument (statistical sampling) inflates tight pure-Python loops,
so its per-function self-times over-state the absolute cost — but its ranking of the hotspots is
reliable. Two things came out of it:
per_cycle_composition(top of the probe tree) was vectorized — 8–13× faster, byte-for-byteidentical to the old loop (it feeds the observation hash, so identity was the bar). Merged.
— vectorizing them byte-identically is reviewed-follow-up work, not merged.
Net: lowering the default is a free ~100× before any code change; the vectorization compounds it on the
explicit large-
--max-readspath.Recommendation → shipped
Default N = 2 000,
DEFAULT_MAX_READS, applied globally (probe,resolve,run,manifest fill, remote probe). A caller who wants to read more of a full-size FASTQ passes a larger--max-reads(the explicit opt-in;run/manifest fillgained the knob).PROBE_VERSION→ 2026.7.2.Caveats
reading the whole file). Fine for chemistry; the theoretical failure mode is a file whose first N
reads are unrepresentative. Not seen here. (An HQ/quality-ranked sample was considered and dropped —
it needs 10× the reads for no gain the benchmark could find.)
more reads. The 2× cushion is for exactly this; the real fingerprint package slices all runs.
DEFAULT_MAX_READSchanges observationvalues → content addresses, so it is a
PROBE_VERSIONbump and a re-hash. That was the intended,accepted step this benchmark justified — not a silent change.
Appendix — reproduce
bench_n.py(representative-run sweep) andanalyze_bench.py(the tables) under the seqforge env.Both operate on real FASTQs bounded by N; neither reads a whole file.
All reactions