Conversation
…mory packInMemLimit(threads) already can't drop below 2*threads without reintroducing the OpenGene#721 deadlock, so it has no slack left to also cap total buffered memory. Scale the per-pack read count (packSize) down instead once thread count exceeds packInMemLimit's own floor, so packInMemLimit(threads) * packSize(threads) stays roughly constant instead of growing linearly with thread count, with a floor so packs don't shrink far enough for per-pack overhead to dominate. Verified via a 24-run sweep (fix/backpressure-scales-with-threads-721 vs this branch, 3 corpus datasets x 4 thread counts, on a 48-vCPU host): zero output-digest mismatches, timings within noise except a ~5-10% cost at -w 48 on some datasets, the expected tradeoff for materially bounded memory growth at high thread counts. A least-full-queue pack-distribution change was also attempted per the architecture deep-dive, but reverted: the writer's output ordering (both the plain round-robin drain and the parallel-pwrite offset-ring path) both assume pack round r always lands on worker r % threads, so distributing packs to whichever worker queue is least full silently reorders output records. Fixing that would require reworking the writer to track explicit per-chunk sequence numbers rather than inferring order from worker index -- a much larger, riskier change than scoped here, caught via a digest diff before anything shipped.
Owner
Author
|
Reopening against OpenGene/fastp:master directly so maintainers see it (GitHub can't target an unmerged branch in another fork's PR). |
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.
Stacked on OpenGene#723 (base branch is
fix/backpressure-scales-with-threads-721).Summary
packInMemLimit(threads)(added in OpenGene#723) can't safely drop below2*threadswithout reintroducing the OpenGene#721 deadlock, so it has no slack left to also cap
total buffered memory -- that limit already grows linearly with thread count
on purpose. This scales the pack size down instead once thread count
exceeds the point where
packInMemLimit's own floor stops mattering, sopackInMemLimit(threads) * packSize(threads)(roughly the total bufferedread count) stays close to constant instead of growing linearly with
threads, with a floor (100 reads/pack) so packs don't shrink far enough for
per-pack overhead to dominate. See the comment on
packSize()insrc/common.hfor the exact reasoning.This only changes behavior once thread count exceeds 16 (
PACK_IN_MEM_LIMIT / 2) -- below that,packSize()returns the original constant 1000 andnothing changes.
Validation
24-run sweep,
fix/backpressure-scales-with-threads-721(base) vs thisbranch, using the corpus and scripts from OpenGene#725 (3 datasets x 4 thread counts
x 2 branches, on a 48-vCPU host):
-w 1/16(packSize isunchanged there); a small ~5-10% cost at
-w 48on some datasets, theexpected tradeoff of processing more, smaller packs per unit of gzip
output -- traded for materially bounded memory growth at high thread
counts.
Internal unit tests (
fastp test) pass, and SE / interleaved-PE / non-interleaved-PEmodes were each spot-checked at 1/8/32/64 threads against the base branch
with matching output digests.
An idea I tried and reverted
I also attempted distributing packs to whichever worker queue currently
holds the fewest in-flight packs, instead of strict round-robin (the other
follow-up suggested alongside this one). I reverted it: the writer's output
ordering -- both the plain round-robin drain in
WriterThread::output()(
mWorkingBufferList = (mWorkingBufferList+1) % threads) and theparallel-pwrite offset-ring path's
mNextSeq[t]sequencing -- both assumepack round
ralways lands on workerr % threads. Distributing to theleast-full queue breaks that assumption and silently reorders output
records (caught via a digest diff between the two builds before this went
anywhere near a PR). Making that safe would mean reworking the writer to
track explicit per-chunk sequence numbers rather than inferring order from
worker index -- a materially larger and riskier change than scoped here, so
I'm not pursuing it further without more explicit interest in that scope.
🤖 Generated with Claude Code