Skip to content

Commit 2936d8e

Browse files
authored
try out codspeed runners and add more realistic tests (#4395)
1 parent facc035 commit 2936d8e

2 files changed

Lines changed: 75 additions & 13 deletions

File tree

.github/workflows/codspeed.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ permissions:
2222
jobs:
2323
startup-benchmarks:
2424
name: Run start-up benchmarks
25-
runs-on: ubuntu-latest
25+
runs-on: codspeed-macro
2626
steps:
2727
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
2828
name: Check out source-code repository
@@ -32,6 +32,11 @@ jobs:
3232
with:
3333
python-version: "3.10"
3434

35+
- name: Install Nextflow
36+
uses: nf-core/setup-nextflow@b4ec1bc7c16a94435159de94a05253542fddf6ef # v3
37+
with:
38+
version: ${{ github.event.inputs.nextflow-version || matrix.nextflow-version }}
39+
3540
- name: Install uv
3641
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
3742
with:
@@ -40,6 +45,13 @@ jobs:
4045
- name: Install dependencies
4146
run: uv sync --all-extras
4247

48+
# Clone nf-core/modules into the local cache here, while network is
49+
# available. The modules-install benchmark then runs entirely offline
50+
# (no per-round fetch), so it stays deterministic even if the CodSpeed
51+
# step sandboxes network during measurement.
52+
- name: Warm nf-core/modules cache
53+
run: uv run python -c "from nf_core.modules.modules_repo import ModulesRepo; ModulesRepo()"
54+
4355
# These benchmarks spawn a fresh interpreter per round to measure
4456
# import/start-up cost, which CodSpeed's instruction-counting
4557
# "simulation" instrument can't see. Use "walltime" mode instead.

benchmarks/test_startup_benchmarks.py

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"""
1111

1212
import os
13+
import shutil
1314
import subprocess
1415
import sys
1516

@@ -34,6 +35,39 @@ def _cli_script(setup, args):
3435
return f"{setup}\nimport sys\nsys.argv = {args!r}\nfrom nf_core.__main__ import run_nf_core\nrun_nf_core()"
3536

3637

38+
@pytest.fixture(scope="session")
39+
def generated_pipeline(tmp_path_factory):
40+
"""Render a real pipeline scaffold once, reused by the realistic benchmarks.
41+
42+
Generation is expensive and identical for every consumer, so it happens a
43+
single time per session and outside any measured rounds. Consumers that
44+
mutate the pipeline (e.g. installing a module) must work on a copy so the
45+
shared scaffold stays pristine and the suite is order-independent.
46+
"""
47+
from nf_core.pipelines.create.create import PipelineCreate
48+
49+
pipeline_dir = tmp_path_factory.mktemp("pipeline_fixture") / "pipeline"
50+
PipelineCreate("benchmark", "benchmark pipeline", "nf-core", outdir=pipeline_dir, no_git=True).init_pipeline()
51+
return pipeline_dir
52+
53+
54+
@pytest.fixture(scope="session")
55+
def modules_cache():
56+
"""Ensure a local nf-core/modules clone exists, without ever fetching.
57+
58+
Constructing ``ModulesRepo`` clones the remote into ``NFCORE_DIR`` only if it
59+
is not already present; ``no_pull_global`` suppresses the ``git fetch`` on an
60+
existing clone. In CI the clone is warmed by an earlier, network-enabled
61+
workflow step, so this fixture (and every measured round) runs offline. When
62+
run locally against a cold cache it performs the one-off clone here, outside
63+
the measured rounds.
64+
"""
65+
from nf_core.modules.modules_repo import ModulesRepo
66+
67+
ModulesRepo.no_pull_global = True
68+
ModulesRepo()
69+
70+
3771
def test_import_main_startup(benchmark):
3872
"""Wall-time cost of importing the CLI entry point in a fresh interpreter."""
3973
_benchmark_command(benchmark, IMPORT_CMD)
@@ -71,16 +105,32 @@ def test_pipelines_create_startup(benchmark, tmp_path):
71105
_benchmark_command(benchmark, [sys.executable, "-c", _cli_script(setup, args)])
72106

73107

74-
def test_modules_install_startup(benchmark, tmp_path):
75-
"""Start module installation, stubbing only the remote clone and resulting writes."""
76-
args = ["nf-core", "modules", "install", "fastp", "-d", str(tmp_path)]
77-
setup = """import nf_core.modules.install as install_module
78-
install_module.ModuleInstall = type(
79-
"ModuleInstall",
80-
(),
81-
{
82-
"__init__": lambda self, *args, **kwargs: None,
83-
"install": lambda self, *args, **kwargs: True,
84-
},
85-
)"""
108+
def test_modules_install_startup(benchmark, generated_pipeline, modules_cache, tmp_path):
109+
"""Install a real module into a real pipeline, offline.
110+
111+
The pipeline scaffold (``generated_pipeline``) and the nf-core/modules clone
112+
(``modules_cache``) are both prepared once, outside the measured rounds. The
113+
rounds then run the real install (git checkout + file copy + modules.json
114+
write) against a private copy of the scaffold. ``no_pull_global`` suppresses
115+
the per-round ``git fetch`` and ``--force`` avoids the re-install prompt, so
116+
every round is deterministic and network-free.
117+
"""
118+
target = tmp_path / "pipeline"
119+
shutil.copytree(generated_pipeline, target)
120+
args = ["nf-core", "modules", "install", "fastp", "-d", str(target), "--force"]
121+
setup = "from nf_core.modules.modules_repo import ModulesRepo\nModulesRepo.no_pull_global = True"
122+
_benchmark_command(benchmark, [sys.executable, "-c", _cli_script(setup, args)])
123+
124+
125+
def test_pipelines_schema_lint_startup(benchmark, generated_pipeline):
126+
"""Lint a real, freshly generated pipeline schema.
127+
128+
The schema fixture is rendered once by ``generated_pipeline`` (outside the
129+
measured rounds); the benchmark then runs the real lint (JSON parse +
130+
JSON-Schema validation + default-param checks). Only ``fetch_wf_config`` is
131+
stubbed to skip the ``nextflow config`` JVM subprocess, which would
132+
otherwise dominate wall time and require Nextflow on the runner.
133+
"""
134+
args = ["nf-core", "pipelines", "schema", "lint", str(generated_pipeline)]
135+
setup = "import nf_core.utils\nnf_core.utils.fetch_wf_config = lambda *args, **kwargs: {}"
86136
_benchmark_command(benchmark, [sys.executable, "-c", _cli_script(setup, args)])

0 commit comments

Comments
 (0)