Skip to content

Add exact threaded Hobohm I#196

Merged
diegozea merged 12 commits into
masterfrom
feat/threaded-hobohmI
Mar 29, 2026
Merged

Add exact threaded Hobohm I#196
diegozea merged 12 commits into
masterfrom
feat/threaded-hobohmI

Conversation

@diegozea

@diegozea diegozea commented Mar 28, 2026

Copy link
Copy Markdown
Owner

Summary

This PR adds an exact threaded implementation of Hobohm I in src/MSA/Hobohm.jl while keeping the outer representative loop serial and threading only the inner scan over candidate sequences for a fixed representative.

The threaded implementation is available through the existing public API via the threads::Bool keyword. Threading remains explicit opt-in (threads=false by default), so existing callers do not pay thread startup overhead unless they ask for it and custom predicates are not run concurrently unless the caller opts in.

Why The Implementation Is Exact

The algorithm still processes representatives in the same serial order as the original implementation. For a fixed representative, the threaded path only parallelizes the candidate scan over j; each iteration evaluates the predicate for one candidate and, if it matches, assigns that candidate to the current representative's cluster.

A follow-up refactor moved clustersize construction out of the representative loop entirely: the core pass now computes only the final cluster assignment vector, and clustersize is derived afterward with a single linear pass over those assignments. That keeps the results identical while simplifying the shared serial/threaded core.

Why It Is Thread-Safe

The outer representative loop is still serial, so at any moment there is only one active representative and one active cluster_id. Inside the threaded candidate scan, each iteration touches a distinct j, so direct cluster[j] = cluster_id writes do not contend with each other.

There are no shared counter updates inside the threaded region, no threadid()-indexed mutable state, and no :static scheduling. clustersize is built afterward in a separate serial pass.

API

The public API adds only a threads::Bool keyword. It does not expose integer tuning parameters such as min_parallel, ntasks, or chunk sizes. Threading is enabled only when the caller passes threads=true and worker threads are available.

CI Coverage

The existing Hobohm threaded-equivalence tests are now exercised in GitHub Actions as well. The main CI workflow sets JULIA_NUM_THREADS: 4,1 for the julia-actions/julia-runtest@v1 step, so hobohmI(...; threads=true) no longer silently falls back to the serial scan during coverage runs.

Validation

Commands run locally in a multithreaded Julia session:

julia --project --threads=auto -e 'push!(LOAD_PATH, "test"); using MIToSTests; MIToSTests.retest("MSA"); MIToSTests.retest("Hobohm I"); MIToSTests.retest("Hobohm I")'
julia --project --threads=auto -e 'push!(LOAD_PATH, "test"); using MIToSTests; MIToSTests.retest("Gaps"); MIToSTests.retest("Gaps")'
julia --project --threads=auto -e 'push!(LOAD_PATH, "test"); using MIToSTests; MIToSTests.retest("Information"); MIToSTests.retest("CorrectedMutualInformation"); MIToSTests.retest("CorrectedMutualInformation")'

Results:

  • MIToSTests: Hobohm I | 28 on both targeted runs.
  • MIToSTests | 249 on both Gaps runs.
  • MIToSTests: Information | 3, then MIToSTests: CorrectedMutualInformation | 55 on both targeted runs.

@codecov

codecov Bot commented Mar 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.29730% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.06%. Comparing base (2cc2780) to head (6072f3f).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/MSA/Hobohm.jl 97.29% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #196      +/-   ##
==========================================
- Coverage   97.07%   97.06%   -0.01%     
==========================================
  Files          65       65              
  Lines        4987     5007      +20     
==========================================
+ Hits         4841     4860      +19     
- Misses        146      147       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Mar 28, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1)

Time benchmarks
master 6072f3f... master / 6072f3f...
Information/CorrectedMutualInformation/buslje09/msa 0.885 ± 0.0072 s 0.884 ± 0.0085 s 1 ± 0.013
Information/CorrectedMutualInformation/buslje09/msa_large 0.0364 ± 0.0012 s 0.0363 ± 0.0013 s 1 ± 0.049
Information/CorrectedMutualInformation/buslje09/msa_wide 0.789 ± 0.0097 s 0.785 ± 0.0098 s 1 ± 0.018
Information/MIp/PF09645 9.52 ± 0.29 ms 9.47 ± 0.3 ms 1.01 ± 0.044
Information/frequencies!/1 0.33 ± 0.03 μs 0.271 ± 0.02 μs 1.22 ± 0.14
Information/frequencies!/2 1.51 ± 0.04 μs 1.45 ± 0.02 μs 1.04 ± 0.031
Information/highlevel/BLMI 0.0631 ± 0.00017 s 0.063 ± 0.00022 s 1 ± 0.0045
Information/highlevel/buslje09 11.4 ± 0.1 ms 11.3 ± 0.13 ms 1.01 ± 0.015
Information/shannon_entropy/PF09645 20 ± 0.65 μs 20.2 ± 0.68 μs 0.991 ± 0.046
MSA/Annotations/filtercolumns/boolean mask 10.4 ± 0.23 μs 10.5 ± 0.25 μs 0.991 ± 0.032
MSA/Annotations/filtercolumns/index array 3.54 ± 0.13 μs 3.55 ± 0.13 μs 0.997 ± 0.052
MSA/Base.vcat/annotated 4.46 ± 0.32 μs 4.44 ± 0.29 μs 1 ± 0.098
MSA/Base.vcat/unannotated 1.48 ± 0.13 μs 1.5 ± 0.13 μs 0.987 ± 0.12
MSA/Residue conversions/char2res 0.354 ± 0.85 ms 0.351 ± 0.017 ms 1.01 ± 2.4
MSA/Residue conversions/int2res 0.205 ± 0.048 ms 0.21 ± 0.048 ms 0.976 ± 0.32
MSA/Residue conversions/res2char 0.261 ± 0.013 ms 0.261 ± 0.012 ms 1 ± 0.069
MSA/Residue conversions/res2int 0.222 ± 0.084 ms 0.21 ± 0.1 ms 1.06 ± 0.66
MSA/hobohmI/pid20 0.461 ± 0.12 μs 0.481 ± 0.12 μs 0.958 ± 0.35
MSA/hobohmI/pid62 0.511 ± 0.13 μs 0.551 ± 0.11 μs 0.927 ± 0.3
MSA/hobohmI/pid80 0.521 ± 0.12 μs 0.561 ± 0.13 μs 0.929 ± 0.3
MSA/hobohmI/pid99 0.591 ± 0.13 μs 0.702 ± 0.12 μs 0.842 ± 0.23
MSA/identity/matrix_Float64 17.8 ± 0.5 μs 17.5 ± 0.47 μs 1.01 ± 0.04
MSA/identity/mean 0.0894 ± 0.022 ms 0.0904 ± 0.022 ms 0.989 ± 0.34
MSA/read/Clustal 29.8 ± 3.6 μs 30.3 ± 2.3 μs 0.982 ± 0.14
MSA/read/Clustal_num 30.1 ± 5.2 μs 30.3 ± 2.2 μs 0.993 ± 0.19
MSA/read/FASTA 0.0482 ± 0.008 ms 0.0472 ± 0.0035 ms 1.02 ± 0.19
MSA/read/FASTA.gz 0.0443 ± 0.0053 ms 0.0448 ± 0.0053 ms 0.989 ± 0.17
MSA/read/FASTA.gz_annotated 0.0507 ± 0.007 ms 0.0509 ± 0.0056 ms 0.995 ± 0.18
MSA/read/FASTA_deletefullgaps 6.13 ± 1 ms 6.14 ± 1.1 ms 0.998 ± 0.24
MSA/read/FASTA_deletefullgaps_mapping 0.0969 ± 0.022 s 0.0941 ± 0.021 s 1.03 ± 0.33
MSA/read/Stockholm 0.0345 ± 0.0083 ms 0.035 ± 0.0079 ms 0.985 ± 0.32
MSA/read/Stockholm.gz 0.0616 ± 0.0037 ms 0.0616 ± 0.004 ms 1 ± 0.088
MSA/read/Stockholm_annotated 0.0459 ± 0.011 ms 0.0465 ± 0.011 ms 0.987 ± 0.34
MSA/read/Stockholm_mapping 0.2 ± 0.039 ms 0.196 ± 0.04 ms 1.02 ± 0.29
MSA/read/Stockholm_mapping_coords 0.125 ± 0.031 ms 0.125 ± 0.031 ms 0.997 ± 0.35
MSA/write/FASTA 0.256 ± 0.081 ms 0.226 ± 0.059 ms 1.13 ± 0.46
PDB/_generate_interaction_keys/defaults 0.0443 ± 0.016 ms 0.0445 ± 0.017 ms 0.995 ± 0.53
PDB/_get_matched_Cαs/hemoglobin 0.0388 ± 0.0074 ms 0.038 ± 0.0071 ms 1.02 ± 0.27
PDB/_pdbresidues_to_mmcifdict/2vqc 0.596 ± 0.08 ms 0.59 ± 0.083 ms 1.01 ± 0.2
PDB/contact/1CBN_20_30_CB 0.191 ± 0.01 μs 0.21 ± 0.01 μs 0.91 ± 0.064
PDB/contact/1CBN_20_30_heavy 0.25 ± 0.001 μs 0.25 ± 0.001 μs 1 ± 0.0057
PDB/count_alanine/1CBN 0.331 ± 0.001 μs 0.321 ± 0.01 μs 1.03 ± 0.032
PDB/distance/1CBN_20_30 0.14 ± 0.001 μs 0.13 ± 0.01 μs 1.08 ± 0.083
PDB/read/MMCIFFile 2.96 ± 0.058 ms 2.97 ± 0.057 ms 0.997 ± 0.027
PDB/squared_distance/1CBN_20_30_CB 0.19 ± 0.01 μs 0.191 ± 0.011 μs 0.995 ± 0.078
PDB/squared_distance/1CBN_20_30_heavy 0.261 ± 0.011 μs 0.261 ± 0.001 μs 1 ± 0.042
Pfam/accession mapping/acc2seqnames 0.19 ± 0.012 ms 0.191 ± 0.012 ms 0.991 ± 0.087
SIFTS/ResidueDetails/_get_details 2.22 ± 0.54 μs 2 ± 0.7 μs 1.11 ± 0.47
SIFTS/ResidueDetails/_is_missing 2.21 ± 0.52 μs 2.45 ± 0.91 μs 0.904 ± 0.4
SIFTS/SIFTSResidue/18gs 0.1 ± 0.009 μs 0.1 ± 0.001 μs 1 ± 0.091
SIFTS/siftsmapping/2vqc 2.38 ± 0.046 ms 2.36 ± 0.046 ms 1.01 ± 0.028
Utils/get_n_words/ascii 0.13 ± 0.02 μs 0.12 ± 0.01 μs 1.08 ± 0.19
Utils/get_n_words/utf8 0.111 ± 0.01 μs 0.11 ± 0.01 μs 1.01 ± 0.13
Utils/hascoordinates/invalid 0.09 ± 0.01 μs 0.09 ± 0.01 μs 1 ± 0.16
Utils/hascoordinates/valid 0.131 ± 0.01 μs 0.131 ± 0.01 μs 1 ± 0.11
Utils/list2matrix/upper 0.226 ± 0.046 ms 0.231 ± 0.04 ms 0.978 ± 0.26
Utils/list2matrix/upper_diagonal 0.336 ± 0.054 ms 0.329 ± 0.058 ms 1.02 ± 0.24
Utils/matrix2list/upper 0.0808 ± 0.016 ms 0.0798 ± 0.016 ms 1.01 ± 0.29
Utils/matrix2list/upper_diagonal 0.0809 ± 0.016 ms 0.0781 ± 0.014 ms 1.04 ± 0.28
time_to_load 0.79 ± 0.012 s 0.787 ± 0.0055 s 1 ± 0.017
Memory benchmarks
master 6072f3f... master / 6072f3f...
Information/CorrectedMutualInformation/buslje09/msa 0.766 M allocs: 0.032 GB 0.766 M allocs: 0.032 GB 1
Information/CorrectedMutualInformation/buslje09/msa_large 0.0901 M allocs: 5.03 MB 0.0901 M allocs: 5.03 MB 1
Information/CorrectedMutualInformation/buslje09/msa_wide 0.742 M allocs: 30.3 MB 0.742 M allocs: 30.3 MB 1
Information/MIp/PF09645 20.3 k allocs: 0.819 MB 20.3 k allocs: 0.819 MB 1
Information/frequencies!/1 0 allocs: 0 B 0 allocs: 0 B
Information/frequencies!/2 0 allocs: 0 B 0 allocs: 0 B
Information/highlevel/BLMI 19.9 k allocs: 1.19 MB 19.9 k allocs: 1.19 MB 1
Information/highlevel/buslje09 0.0377 M allocs: 2.3 MB 0.0377 M allocs: 2.3 MB 1
Information/shannon_entropy/PF09645 0.047 k allocs: 12.2 kB 0.047 k allocs: 12.2 kB 1
MSA/Annotations/filtercolumns/boolean mask 18 allocs: 5.22 kB 18 allocs: 5.22 kB 1
MSA/Annotations/filtercolumns/index array 16 allocs: 1.62 kB 16 allocs: 1.62 kB 1
MSA/Base.vcat/annotated 0.143 k allocs: 6.58 kB 0.143 k allocs: 6.58 kB 1
MSA/Base.vcat/unannotated 0.064 k allocs: 2.7 kB 0.064 k allocs: 2.7 kB 1
MSA/Residue conversions/char2res 3 allocs: 4.1 MB 3 allocs: 4.1 MB 1
MSA/Residue conversions/int2res 3 allocs: 4.1 MB 3 allocs: 4.1 MB 1
MSA/Residue conversions/res2char 3 allocs: 2.05 MB 3 allocs: 2.05 MB 1
MSA/Residue conversions/res2int 3 allocs: 4.1 MB 3 allocs: 4.1 MB 1
MSA/hobohmI/pid20 31 allocs: 1.77 kB 31 allocs: 1.77 kB 1
MSA/hobohmI/pid62 31 allocs: 1.77 kB 31 allocs: 1.77 kB 1
MSA/hobohmI/pid80 31 allocs: 1.77 kB 31 allocs: 1.77 kB 1
MSA/hobohmI/pid99 31 allocs: 1.77 kB 31 allocs: 1.77 kB 1
MSA/identity/matrix_Float64 0.249 k allocs: 11.8 kB 0.249 k allocs: 11.8 kB 1
MSA/identity/mean 1.23 k allocs: 0.0517 MB 1.23 k allocs: 0.0517 MB 1
MSA/read/Clustal 0.394 k allocs: 24.3 kB 0.394 k allocs: 24.3 kB 1
MSA/read/Clustal_num 0.394 k allocs: 24.3 kB 0.394 k allocs: 24.3 kB 1
MSA/read/FASTA 0.406 k allocs: 0.044 MB 0.406 k allocs: 0.044 MB 1
MSA/read/FASTA.gz 0.443 k allocs: 0.0752 MB 0.443 k allocs: 0.0752 MB 1
MSA/read/FASTA.gz_annotated 0.533 k allocs: 0.0794 MB 0.533 k allocs: 0.0794 MB 1
MSA/read/FASTA_deletefullgaps 13.6 k allocs: 17.4 MB 13.6 k allocs: 17.4 MB 1
MSA/read/FASTA_deletefullgaps_mapping 1.64 M allocs: 0.0795 GB 1.64 M allocs: 0.0795 GB 1
MSA/read/Stockholm 0.402 k allocs: 0.033 MB 0.402 k allocs: 0.033 MB 1
MSA/read/Stockholm.gz 0.479 k allocs: 0.0754 MB 0.479 k allocs: 0.0754 MB 1
MSA/read/Stockholm_annotated 0.559 k allocs: 0.0412 MB 0.559 k allocs: 0.0412 MB 1
MSA/read/Stockholm_mapping 2.08 k allocs: 0.104 MB 2.08 k allocs: 0.104 MB 1
MSA/read/Stockholm_mapping_coords 1.64 k allocs: 0.0812 MB 1.64 k allocs: 0.0812 MB 1
MSA/write/FASTA 0.303 k allocs: 14.1 kB 0.303 k allocs: 14.1 kB 1
PDB/_generate_interaction_keys/defaults 0.497 k allocs: 0.0581 MB 0.497 k allocs: 0.0581 MB 1
PDB/_get_matched_Cαs/hemoglobin 0.584 k allocs: 0.0438 MB 0.584 k allocs: 0.0438 MB 1
PDB/_pdbresidues_to_mmcifdict/2vqc 8.56 k allocs: 1.12 MB 8.56 k allocs: 1.12 MB 1
PDB/contact/1CBN_20_30_CB 4 allocs: 0.281 kB 4 allocs: 0.281 kB 1
PDB/contact/1CBN_20_30_heavy 4 allocs: 0.281 kB 4 allocs: 0.281 kB 1
PDB/count_alanine/1CBN 0 allocs: 0 B 0 allocs: 0 B
PDB/distance/1CBN_20_30 0 allocs: 0 B 0 allocs: 0 B
PDB/read/MMCIFFile 0.039 M allocs: 2.9 MB 0.039 M allocs: 2.9 MB 1
PDB/squared_distance/1CBN_20_30_CB 4 allocs: 0.281 kB 4 allocs: 0.281 kB 1
PDB/squared_distance/1CBN_20_30_heavy 4 allocs: 0.281 kB 4 allocs: 0.281 kB 1
Pfam/accession mapping/acc2seqnames 4.32 k allocs: 0.319 MB 4.32 k allocs: 0.319 MB 1
SIFTS/ResidueDetails/_get_details 25 allocs: 1.45 kB 25 allocs: 1.45 kB 1
SIFTS/ResidueDetails/_is_missing 25 allocs: 1.45 kB 25 allocs: 1.45 kB 1
SIFTS/SIFTSResidue/18gs 4 allocs: 0.125 kB 4 allocs: 0.125 kB 1
SIFTS/siftsmapping/2vqc 5.94 k allocs: 0.88 MB 5.94 k allocs: 0.88 MB 1
Utils/get_n_words/ascii 5 allocs: 0.203 kB 5 allocs: 0.203 kB 1
Utils/get_n_words/utf8 5 allocs: 0.219 kB 5 allocs: 0.219 kB 1
Utils/hascoordinates/invalid 0 allocs: 0 B 0 allocs: 0 B
Utils/hascoordinates/valid 0 allocs: 0 B 0 allocs: 0 B
Utils/list2matrix/upper 3 allocs: 1.91 MB 3 allocs: 1.91 MB 1
Utils/list2matrix/upper_diagonal 6 allocs: 2.86 MB 6 allocs: 2.86 MB 1
Utils/matrix2list/upper 3 allocs: 0.952 MB 3 allocs: 0.952 MB 1
Utils/matrix2list/upper_diagonal 3 allocs: 0.956 MB 3 allocs: 0.956 MB 1
time_to_load 0.149 k allocs: 11.1 kB 0.149 k allocs: 11.1 kB 1

@coveralls

coveralls commented Mar 28, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 97.258% (+0.01%) from 97.248%
when pulling 6072f3f on feat/threaded-hobohmI
into 2cc2780 on master.

@diegozea diegozea merged commit 29c7fbb into master Mar 29, 2026
14 of 18 checks passed
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.

2 participants