perf: prevent bin QC block on all binner jobs finishing#1062
Conversation
piplus2
left a comment
There was a problem hiding this comment.
I see the advantage of splitting the stat into many processes but still there's the risk of having thousands of them.
| ch_binning_results_gzipped_final = ch_binning_results_gzipped_final.mix(ch_semibin_bins) | ||
| } | ||
|
|
||
| // group bins into per-sample process and not flood clusters with thousands of seqkit jobs |
There was a problem hiding this comment.
what about the possible creation of thousands of jobs as reported here?
There was a problem hiding this comment.
Yes, that's the drawback. In practice, the worst case (all binners and all assemblers enabled) works out to about samples * 30 jobs, and most realistic configurations should be around samples * 10 or less.
I think that's an acceptable tradeoff, and arguably better than the hard lock we have today: because some binners are way slower than others, the current behavior can force you to wait 10+ hours for the last binner to finish before the pipeline can continue. Grouping per-sample avoids that serial bottleneck.
There was a problem hiding this comment.
That's still going to generate hundreds of jobs... On a standard HPC most of them will go on queue. Since it's a big change, I'd suggest to perform some tests on real scenarios. Also, I'm not sure that the way the stats results are currently processed in the subsequent steps of the workflow are consistent with these changes. From my understanding they expect a single tsv per sample.
There was a problem hiding this comment.
For reference, ch_seqkitstats_results stores one item per bin (it splits the seqkit CSV per row), so the downstream result ends up exactly the same regardless of how many tsv files seqkit produces, nothing expects a single tsv per sample.
I think the numbers of jobs is a valid concern, but most HPC handle small jobs without issues, and also they will not be spawned at the same time, as the binning jobs finish instead.
As an additional note, the whole post-binning workflow already runs at the same level of granularity (sample * assembler * binner): BUSCO, CheckM(2), GUNC, GTDB-Tk, CATPACK, Tiara, etc. So in practice, seqkit will not dispatch a lot of jobs compared to other tools in the pipeline.
I'm running some mag benchmarks now and hit this: my mag run has been about ~15h just waiting for COMEbin to finish, after it failed once.
There was a problem hiding this comment.
So @piplus2 is sort of fair in his worry of flooding a HPC with lots of jobs - while you are right @dialvarezs that it is sort of the same for all the others, the problem is the seqkitstats is a very short/fast job which is something HPC admins do not like (batch them! Use an array!) as it adds a bunch of unnecessary strain on the scheduler database etc (apparently anyway).
Even if I think that would probably not toooo terrible if Diego's estimate is correct (samples * 30), as a mid-ground we could either:
- Just document/make people aware of
maxForks - Have a dedicated parameter than controls maxForks for this one process? (this would also arguably be more visible than just document)
What do you think?
There was a problem hiding this comment.
I understand your point. Since this is a major change in the workflow, I'd like to leave it to @jfy133, as from my understanding, he wrote the original part.
| ch_binning_results_gzipped_final = ch_binning_results_gzipped_final.mix(ch_semibin_bins) | ||
| } | ||
|
|
||
| // group bins into per-sample process and not flood clusters with thousands of seqkit jobs |
There was a problem hiding this comment.
I understand your point. Since this is a major change in the workflow, I'd like to leave it to @jfy133, as from my understanding, he wrote the original part.
| "-profile assembly_input": { | ||
| "content": [ | ||
| 285, | ||
| 291, |
There was a problem hiding this comment.
Are the additional files variable?
There was a problem hiding this comment.
Yes, these are the additonal processes from "flattening" seqkit.
| ch_binning_results_gzipped_final = ch_binning_results_gzipped_final.mix(ch_semibin_bins) | ||
| } | ||
|
|
||
| // group bins into per-sample process and not flood clusters with thousands of seqkit jobs |
There was a problem hiding this comment.
So @piplus2 is sort of fair in his worry of flooding a HPC with lots of jobs - while you are right @dialvarezs that it is sort of the same for all the others, the problem is the seqkitstats is a very short/fast job which is something HPC admins do not like (batch them! Use an array!) as it adds a bunch of unnecessary strain on the scheduler database etc (apparently anyway).
Even if I think that would probably not toooo terrible if Diego's estimate is correct (samples * 30), as a mid-ground we could either:
- Just document/make people aware of
maxForks - Have a dedicated parameter than controls maxForks for this one process? (this would also arguably be more visible than just document)
What do you think?
|
@jfy133 maxForks makes sense to me. At least we have full control of what's gonna happen. |
|
maxForks makes sense to me. |
In principle yes. Or if they are are all running per bin, you could share across all because the number of bins won't change much at each stage |
…-from-binners # Conflicts: # tests/test_assembly_input.nf.test.snap # tests/test_hybrid.nf.test.snap # tests/test_longreadonly.nf.test.snap # tests/test_single_end.nf.test.snap
|
I added a new parameter to control seqkit concurrency, |
|
|
||
| withName: SEQKIT_STATS { | ||
| ext.args = "" | ||
| maxForks = params.bin_seqkit_stats_max_forks ?: null |
There was a problem hiding this comment.
The default should be set in nextflow.config, no?
There was a problem hiding this comment.
Yep, we can remove the ?: null
Performance / QoL improvement
In the current implementation, bins are grouped by sample before being fed to SEQKIT_STATS, which computes their sizes for downstream filtering. Because groupTuple has no size, it can't emit until every binner has finished, blocking bin QC for bins that already finished.
This PR removes that grouping and feeds each binner's bins to SEQKIT_STATS as they complete. Functionality is preserved: downstream re-keys the stats by bin filename (splitCsv -> join), so the result is identical regardless of how the seqkit jobs are batched.
The only drawback is more seqkit jobs (from 1 per sample to 1 per sample+assembler+binner), but that's negligible compared to removing the global pipeline lock.
PR checklist
nf-core pipelines lint).nextflow run . -profile test,docker --outdir <OUTDIR>).nextflow run . -profile debug,test,docker --outdir <OUTDIR>).docs/usage.mdis updated.docs/output.mdis updated.CHANGELOG.mdis updated.README.mdis updated (including new tool citations and authors/contributors).