Skip to content

Commit 3606851

Browse files
committed
submit benchmask
1 parent 68d20cd commit 3606851

59 files changed

Lines changed: 71334 additions & 10 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/benchmark.yml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
name: Cross-OS Performance Benchmark
2+
3+
# Runs scripts/perf_report.py on Linux, Windows, and macOS GitHub-hosted
4+
# runners, building the native C extension from source on each. This produces
5+
# the multi-platform, OpenMP-enabled numbers the laptop benchmark could not,
6+
# and uploads them as downloadable artifacts.
7+
#
8+
# Trigger manually: Actions tab -> "Cross-OS Performance Benchmark" -> Run workflow.
9+
# Note: GitHub-hosted runners have NO GPU, so the CUDA path is still not exercised.
10+
11+
on:
12+
workflow_dispatch:
13+
inputs:
14+
quick:
15+
description: "Quick mode (small datasets, faster)"
16+
type: boolean
17+
default: false
18+
reps:
19+
description: "Repetitions per benchmark (0 = perf_report default of 5)"
20+
type: string
21+
default: "0"
22+
push:
23+
tags:
24+
- "bench-*" # push a tag like bench-v9.0.0 to run automatically
25+
26+
concurrency:
27+
group: benchmark-${{ github.ref }}
28+
cancel-in-progress: true
29+
30+
jobs:
31+
benchmark:
32+
name: benchmark (${{ matrix.os }})
33+
runs-on: ${{ matrix.os }}
34+
timeout-minutes: 45
35+
strategy:
36+
fail-fast: false
37+
matrix:
38+
os: [ubuntu-latest, windows-latest, macos-latest]
39+
40+
steps:
41+
- name: Checkout
42+
uses: actions/checkout@v4
43+
44+
- name: Set up Python
45+
uses: actions/setup-python@v5
46+
with:
47+
python-version: "3.11"
48+
49+
- name: Enable OpenMP toolchain (macOS)
50+
if: runner.os == 'macOS'
51+
run: |
52+
brew install libomp
53+
PREFIX="$(brew --prefix libomp)"
54+
# setup.py's OpenMP probe and the linker need these paths
55+
echo "CPATH=$PREFIX/include" >> "$GITHUB_ENV"
56+
echo "LIBRARY_PATH=$PREFIX/lib" >> "$GITHUB_ENV"
57+
echo "DYLD_LIBRARY_PATH=$PREFIX/lib" >> "$GITHUB_ENV"
58+
59+
- name: Install dependencies
60+
shell: bash
61+
run: |
62+
python -m pip install --upgrade pip setuptools wheel
63+
python -m pip install numpy scipy scikit-learn pandas plotly
64+
65+
- name: Build metbit from source (compiles native + OpenMP extension)
66+
shell: bash
67+
run: |
68+
python -m pip install -e . --no-build-isolation --force-reinstall
69+
70+
- name: Report active backend
71+
shell: bash
72+
run: |
73+
python - <<'PY'
74+
from metbit._native import backend_info
75+
info = backend_info()
76+
for k, v in info.items():
77+
print(f"{k} = {v}")
78+
if not info["native_c"]:
79+
print("::warning::native C extension NOT active on this runner - "
80+
"benchmark measures only the NumPy fallback.")
81+
PY
82+
83+
- name: Run benchmark (OpenMP thread sweep)
84+
shell: bash
85+
run: |
86+
set -e
87+
OS="${{ matrix.os }}"
88+
EXTRA=""
89+
[ "${{ inputs.quick }}" = "true" ] && EXTRA="$EXTRA --quick"
90+
[ "${{ inputs.reps }}" != "0" ] && [ -n "${{ inputs.reps }}" ] && EXTRA="$EXTRA --reps ${{ inputs.reps }}"
91+
CORES="$(python -c 'import os;print(os.cpu_count() or 1)')"
92+
THREADS="$(printf '1\n%s\n' "$CORES" | sort -un)"
93+
echo "Runner cores: $CORES ; sweeping OMP threads: $(echo $THREADS)"
94+
for T in $THREADS; do
95+
echo "===== OMP_NUM_THREADS=$T ====="
96+
OMP_NUM_THREADS="$T" METBIT_N_JOBS="$T" \
97+
python scripts/perf_report.py --output "reports/ci/${OS}/omp_${T}" $EXTRA
98+
done
99+
100+
- name: Upload results
101+
uses: actions/upload-artifact@v4
102+
with:
103+
name: benchmark-${{ matrix.os }}
104+
path: reports/ci/**
105+
if-no-files-found: error
106+
retention-days: 90

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,9 @@ ENV/
5656
manuscript/incob2026/~$cob2026_manuscript.docx
5757
/manuscript
5858
/manuscript
59+
60+
# Benchmark large intermediate output (regenerate via benchmark_mtbls1.py)
61+
/Benchmark/MTBLS1/result/spectral_matrix_normalised.csv
62+
63+
# Benchmark raw datasets (large; fetch per each dataset's PROVENANCE/README)
64+
/Benchmark/data/

Benchmark/MTBLS1/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# metbit MTBLS1 Full-Pipeline Benchmark
2+
3+
Reproduces the full metbit workflow on the public MTBLS1 human urine <sup>1</sup>H NMR
4+
study (Salek et al., 2007; via MetaboLights), from raw Bruker FID archives
5+
through preprocessing, alignment, normalisation, PCA, OPLS-DA, VIP scoring,
6+
and STOCSY. Referenced from the manuscript's Data Availability statement.
7+
8+
## Scope
9+
10+
- **Performance** — wall-clock time and peak memory (via `tracemalloc`) for
11+
each pipeline stage.
12+
- **Reproducibility** — same-seed determinism check: the stochastic stages
13+
(OPLS-DA cross-validation) are re-fit `n=3` times against the same
14+
preprocessed matrix with a fixed `random_state`, comparing R2Y, Q2, and the
15+
top-10 VIP-ranked ppm positions across runs.
16+
- **Usability** — the pipeline is a single Python function; the report
17+
records its length and the number of distinct metbit API calls needed to
18+
go from raw FID directories to a STOCSY connectivity plot, with no manual
19+
intervention between stages.
20+
21+
## Running
22+
23+
```bash
24+
python Benchmark/MTBLS1/benchmark_mtbls1.py
25+
```
26+
27+
Requires the raw MTBLS1 FID archive (132 Bruker directories) at
28+
`manuscript/benchmark/data/MTBLS1/FILES` and sample metadata at
29+
`manuscript/benchmark/data/MTBLS1/samples_parsed.csv`. These are fetched from
30+
MetaboLights separately (not part of this script, to avoid re-downloading
31+
~132 FID directories on every run) and are gitignored due to size.
32+
33+
## Output (`result/`)
34+
35+
| File | Contents |
36+
|---|---|
37+
| `REPORT.md` | Combined human-readable summary of all three scopes |
38+
| `performance_report.json` | Per-stage timing, peak memory, environment info |
39+
| `reproducibility_report.json` | Same-seed determinism metrics |
40+
| `usability_report.json` | API-call / lines-of-code usability metrics |
41+
| `opls_metrics.json` | R2Y, Q2, groups, top-VIP STOCSY anchor |
42+
| `spectral_matrix_normalised.csv` | PQN-normalised, aligned spectral matrix (float32) |
43+
| `pca_scores.csv` | PCA scores (PC1-PC5) per sample |
44+
| `vip_scores.csv` | VIP score per spectral feature |
45+
46+
## Caveats
47+
48+
Disease status in MTBLS1 is completely confounded with acquisition batch
49+
(see manuscript Section 2.13), and cross-validation here is performed at the
50+
spectrum level, not the participant level. R2Y/Q2/VIP values in this
51+
benchmark are workflow-execution outputs, not evidence of disease
52+
discrimination — consistent with how they are reported in the manuscript.

0 commit comments

Comments
 (0)