In run_conversion_pipeline (src/lib.rs), after #141 the contig concurrency comes from the sharded planner while the per-contig thread counts still come from the monolithic one:
let plan = crate::budget::plan_thread_budget(available_cores, chroms.len());
let concurrent_chroms = orchestrator::bench_concurrent_chroms(sharded.concurrent_chroms);
let htslib_threads = plan.htslib_threads; // monolithic path only
plan_sharded sizes concurrent_chroms against a per-contig cost of 1 + reader_workers — correct for the sharded reader, where SHARDED_VCF_HTSLIB_THREADS_PER_READER == 0. The monolithic reader instead costs PIPELINE_THREADS_PER_CHROM + htslib_threads. If the monolithic path is taken, that many contigs run concurrently at the monolithic per-contig thread cost, oversubscribing the allocation.
Concretely at 48 cores / 22 contigs: the sharded planner picks cc=11, and the monolithic path would then want 11 * (4 + htslib_threads) threads against a budget sized for 11 * 4.
Severity
Performance only, and currently unreachable in practice — the sharded branch returns before processing_threads is used. Filing it because the coupling is silent: nothing in the types or the control flow prevents a future change from routing to the monolithic path and inheriting a concurrency that was never planned for it.
Fix sketch
Make the two mutually exclusive rather than parallel locals — pick the planner with the path, so a concurrency figure cannot outlive the cost model it was derived from.
In
run_conversion_pipeline(src/lib.rs), after #141 the contig concurrency comes from the sharded planner while the per-contig thread counts still come from the monolithic one:plan_shardedsizesconcurrent_chromsagainst a per-contig cost of1 + reader_workers— correct for the sharded reader, whereSHARDED_VCF_HTSLIB_THREADS_PER_READER == 0. The monolithic reader instead costsPIPELINE_THREADS_PER_CHROM + htslib_threads. If the monolithic path is taken, that many contigs run concurrently at the monolithic per-contig thread cost, oversubscribing the allocation.Concretely at 48 cores / 22 contigs: the sharded planner picks
cc=11, and the monolithic path would then want11 * (4 + htslib_threads)threads against a budget sized for11 * 4.Severity
Performance only, and currently unreachable in practice — the sharded branch returns before
processing_threadsis used. Filing it because the coupling is silent: nothing in the types or the control flow prevents a future change from routing to the monolithic path and inheriting a concurrency that was never planned for it.Fix sketch
Make the two mutually exclusive rather than parallel locals — pick the planner with the path, so a concurrency figure cannot outlive the cost model it was derived from.