Skip to content

gh-1088: Add realistic benchmark tests - #1098

Open
connoraird wants to merge 66 commits into
mainfrom
connor/1088-realistic-benchmarks
Open

gh-1088: Add realistic benchmark tests#1098
connoraird wants to merge 66 commits into
mainfrom
connor/1088-realistic-benchmarks

Conversation

@connoraird

@connoraird connoraird commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Description

  • Adds a more realistic benchmark to be used for understanding likely performance of GLASS on various machines and configurations

Closes: #1088 #1084

Changelog entry

Added: Realistic benchmark test for benchmarking glass on various machines

Checks

  • Is your code passing linting?
  • Is your code passing tests?
  • Have you added additional tests (if required)?
  • Have you modified/extended the documentation (if required)?
  • Have you added a one-liner changelog entry above (if required)?

@connoraird connoraird changed the title Add docs and submission script for archer2 gh-1088: Add realistic benchmark tests Jun 2, 2026
@connoraird
connoraird changed the base branch from main to connor/1087-rename-regressions June 2, 2026 14:19
@connoraird
connoraird force-pushed the connor/1088-realistic-benchmarks branch from c2cd7e6 to 19ec5c0 Compare June 2, 2026 14:22
@connoraird
connoraird force-pushed the connor/1087-rename-regressions branch 2 times, most recently from cd255a4 to 90d3f20 Compare June 4, 2026 08:53
@connoraird
connoraird force-pushed the connor/1088-realistic-benchmarks branch from 1c6f375 to a77d3e0 Compare June 4, 2026 09:02
@connoraird
connoraird force-pushed the connor/1087-rename-regressions branch from 568254e to bcb579a Compare June 5, 2026 08:56
Base automatically changed from connor/1087-rename-regressions to main June 5, 2026 10:59
@connoraird
connoraird force-pushed the connor/1088-realistic-benchmarks branch from a77d3e0 to 6b294a1 Compare June 15, 2026 09:22
@connoraird connoraird added the benchmarks Benchmarking work label Jun 15, 2026
@connoraird
connoraird force-pushed the connor/1088-realistic-benchmarks branch from 6b294a1 to 696463b Compare June 23, 2026 10:07
@connoraird
connoraird requested a review from paddyroddy July 30, 2026 14:04
@connoraird
connoraird marked this pull request as ready for review July 30, 2026 14:04
@connoraird
connoraird force-pushed the connor/1088-realistic-benchmarks branch from f474cc5 to 9329122 Compare July 30, 2026 14:06
@connoraird

Copy link
Copy Markdown
Contributor Author

@paddyroddy I'm not sure why readthedocs is failing. Any ideas?

@paddyroddy

Copy link
Copy Markdown
Member

@paddyroddy I'm not sure why readthedocs is failing. Any ideas?

I've been trying to fix it, no luck yet

@paddyroddy paddyroddy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have gone through. Fixed typos. And tried to fix readthedocs.

Comment thread .gitignore Outdated
Comment thread benchmarks/__init__.py Outdated
Comment thread benchmarks/cosma8/README.md Outdated
Comment thread benchmarks/archer2/submit-gpu.sh
Comment thread benchmarks/cosma8/submit-cpu.sh
Comment thread benchmarks/benchmark_utils.py Outdated
Comment on lines +48 to +49
array_api_strict.set_array_api_strict_flags(api_version="2025.12")
jax.config.update("jax_enable_x64", val=True)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole file seems very similar to tests/fixtures/array_backends.py. Is there a way we can reduce the duplication?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only similarity is the if blocks, i.e.:

if not ARRAY_BACKEND or ARRAY_BACKEND == "numpy":
    xp_available_backends["numpy"] = np
	...

Everything else is new, no?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess all of this?

# environment variable to specify array backends for testing
# can be:
# - a particular array library (numpy, jax, array_api_strict, ...)
# - all (try finding every supported array library available in the environment)
ARRAY_BACKEND: str = os.environ.get("ARRAY_BACKEND", "")
def _check_version(lib: str, array_api_compliant_version: str) -> None:
"""
Check if installed library's version is compliant with the array API standard.
Parameters
----------
lib
name of the library.
array_api_compliant_version
version of the library compliant with the array API standard.
Raises
------
ImportError
If the installed version is not compliant with the array API standard.
"""
lib_version = packaging.version.Version(importlib.metadata.version(lib))
if lib_version < packaging.version.Version(array_api_compliant_version):
msg = f"{lib} must be >= {array_api_compliant_version}; found {lib_version}"
raise ImportError(msg)
def _import_and_add_numpy(xp_available_backends: dict[str, ModuleType]) -> None:
"""Add numpy to the backends dictionary."""
_check_version("numpy", "2.2.6")
xp_available_backends["numpy"] = np
def _import_and_add_array_api_strict(
xp_available_backends: dict[str, ModuleType],
) -> None:
"""Add array_api_strict to the backends dictionary."""
import array_api_strict # noqa: PLC0415
_check_version("array_api_strict", "2.3.1")
xp_available_backends["array_api_strict"] = array_api_strict
array_api_strict.set_array_api_strict_flags(api_version="2025.12")
def _import_and_add_jax(xp_available_backends: dict[str, ModuleType]) -> None:
"""Add jax to the backends dictionary."""
import jax # noqa: PLC0415
_check_version("jax", "0.6.2")
xp_available_backends["jax.numpy"] = jax.numpy
# enable 64 bit numbers
jax.config.update("jax_enable_x64", val=True)
# a dictionary with all array backends to test
xp_available_backends: dict[str, ModuleType] = {}
# if no backend passed, use numpy by default
if not ARRAY_BACKEND or ARRAY_BACKEND == "numpy":
_import_and_add_numpy(xp_available_backends)
elif ARRAY_BACKEND == "array_api_strict":
_import_and_add_array_api_strict(xp_available_backends)
elif ARRAY_BACKEND == "jax":
_import_and_add_jax(xp_available_backends)
# if all, try importing every backend
elif ARRAY_BACKEND == "all":
_import_and_add_numpy(xp_available_backends)
_import_and_add_array_api_strict(xp_available_backends)
_import_and_add_jax(xp_available_backends)
else:
msg = f"unsupported array backend: {ARRAY_BACKEND}"
raise ValueError(msg)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided not to do that here and just always import numpy, jax, array_api_strict, etc. In fact I can probably remove array_api_strict as I doubt we care about benchmarking that.

Comment on lines +54 to +56
*args: tuple[Any, ...],
xp: ModuleType,
**kwargs: dict[str, Any],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems very unusual. Surely we go function_to_benchmark, xp, args, kwargs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know what you mean. The reason for this is I want to ensure xp is always provided as a named variable. If I have xp, *args, **kwargs then the first positional arg will be assumed to be xp but it doesn't have to be.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does xp have to be named?

How about:

def run_benchmark(
    function_to_benchmark: FunctionType,
    xp: ModuleType,
    *,
    args: tuple[Any, ...] = (),
    kwargs: dict[str, Any] | None = None,
):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't have to be. However, if I then call run_benchmark like this:

x: FloatArray

# some logic to set `x` to and define `some_benchmark`

run_benchmark(
        some_benchmark,
		x,
)

Then xp in run_benchmark will be x.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clearly. Is that not a fault with run_benchmark? Not entirely sure what you're trying to do here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about

def run_benchmark(
      function_to_benchmark: Callable[..., Any],
      *,
      xp: ModuleType,
      args: tuple[Any, ...] = (),
      kwargs: dict[str, Any] | None = None,
  ) -> None:

Comment thread benchmarks/benchmark_utils.py Outdated
Comment thread benchmarks/benchmark_utils.py
Comment thread benchmarks/benchmark_utils.py Outdated
@paddyroddy

Copy link
Copy Markdown
Member

I've been trying to fix it, no luck yet

I've pushed another attempted fix. Will request a Copilot review also.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new “realistic” benchmarking workflow (scripts + docs) to measure GLASS performance across local machines and HPC clusters (e.g., Archer2/Cosma8/AAC6), and makes a few array-backend compatibility tweaks in core lensing/shell code to support those benchmarks.

Changes:

  • Introduces a new benchmarks/ suite with a lensing benchmark, shared utilities, and cluster submission scripts/docs.
  • Updates dependency groups for benchmark + GPU environments and adjusts example execution to install glass-ext-camb.
  • Improves array-backend compatibility in lensing/shear code and shells tests.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
tests/regression/archer2/README.md Repoints setup instructions to shared benchmark docs (link needs correction).
tests/core/test_shells.py Adjusts assertion construction for backend compatibility.
pyproject.toml Adds benchmark/GPU dependency groups and lint ignores; modifies examples extra.
noxfile.py Ensures glass-ext-camb is installed for running example notebooks.
glass/shells.py Forces RadialWindow.zeff to be a Python float.
glass/lensing.py Moves shear computation utilities toward backend-agnostic array ops.
benchmarks/README.md Adds benchmark runner documentation (contains a small typo).
benchmarks/lensing.py Adds a realistic lensing benchmark script.
benchmarks/cosma8/submit-gpu.sh Adds Cosma8 GPU Slurm submission script (path handling needs tightening).
benchmarks/cosma8/submit-cpu.sh Adds Cosma8 CPU Slurm submission script (path handling needs tightening).
benchmarks/cosma8/README.md Adds Cosma8 benchmark instructions (contains a broken relative link).
benchmarks/benchmark_utils.py Adds backend selection + timing utilities and a Cosmology wrapper (contains a bug).
benchmarks/archer2/submit-gpu.sh Adds Archer2 GPU Slurm submission script (path handling needs tightening).
benchmarks/archer2/submit-cpu.sh Adds Archer2 CPU submission script (path handling needs tightening).
benchmarks/archer2/setup-gpu-env.sh Adds Archer2 GPU module/environment setup script (minor typo).
benchmarks/archer2/README.md Adds Archer2 benchmark instructions (contains a broken relative link).
benchmarks/aac6/submit-gpu.sh Adds AAC6 GPU Slurm submission script (path handling needs tightening).
benchmarks/aac6/README.md Adds AAC6 benchmark instructions (contains a broken relative link).
benchmarks/init.py Adds benchmarks package marker.
.typos.toml Adds a shell-word exception for “HSA”.
.gitignore Ignores healpy-data directory used for offline runs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread glass/lensing.py Outdated
Comment thread benchmarks/benchmark_utils.py Outdated
Comment thread benchmarks/benchmark_utils.py
Comment thread benchmarks/lensing.py
Comment thread benchmarks/lensing.py Outdated
Comment thread benchmarks/cosma8/submit-cpu.sh Outdated
Comment thread benchmarks/cosma8/submit-gpu.sh Outdated
Comment thread benchmarks/aac6/submit-gpu.sh Outdated
Comment thread benchmarks/archer2/submit-cpu.sh Outdated
Comment thread pyproject.toml Outdated

@paddyroddy paddyroddy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had another pass

Comment thread benchmarks/aac6/README.md

### GPU prerequisites

For the gpu benchmark, there is an additional dependency group `aac6-gpu` which

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For the gpu benchmark, there is an additional dependency group `aac6-gpu` which
For the GPU benchmark, there is an additional dependency group `aac6-gpu` which


### GPU prerequisites

For the gpu benchmark, there is an additional dependency group `archer2-gpu`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For the gpu benchmark, there is an additional dependency group `archer2-gpu`
For the GPU benchmark, there is an additional dependency group `archer2-gpu`

the exact "ideal" benchmark.

[benchmarks/archer2/submit-gpu.sh](./submit-gpu.sh) specifically will submit a
job to the archer2 amd gpu testbed queue.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
job to the archer2 amd gpu testbed queue.
job to the ARCHER2 AMD GPU testbed queue.

Comment thread benchmarks/archer2/submit-gpu.sh Outdated
# Setup environment
source "$GLASS_DIR/benchmarks/archer2/setup-gpu-env.sh"

# Flags to maximise jax gpu performance

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Flags to maximise jax gpu performance
# Flags to maximise JAX GPU performance


### GPU prerequisites

For the gpu benchmark, there is an additional dependency group `cosma8-gpu`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For the gpu benchmark, there is an additional dependency group `cosma8-gpu`
For the GPU benchmark, there is an additional dependency group `cosma8-gpu`

module load craype-accel-amd-gfx90a
module load craype-x86-milan

# Ensure the rocm library and build is known to jax

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Ensure the rocm library and build is known to jax
# Ensure the ROCm library and build is known to JAX

## Running the benchmarks

Benchmarks can be submitted as a batch job to slurm via the provided script. For
example to benchmark using jax with amd/rocm, run the following from the root of

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
example to benchmark using jax with amd/rocm, run the following from the root of
example to benchmark using JAX with AMD/ROCm, run the following from the root of

will need to install you python virtual environment. Finally, you will need to
load specific modules for the GPU benchmark.

Note that for the GPU benchmarks ARCHER2 only support rocm up to v0.6.x.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Note that for the GPU benchmarks ARCHER2 only support rocm up to v0.6.x.
Note that for the GPU benchmarks ARCHER2 only support ROCm up to v0.6.x.

Comment thread benchmarks/lensing.py
from benchmark_utils import CosmologyWrapper, run_benchmark, xp_available_backends

# use the CAMB cosmology that generated the matter power spectra
import camb # ty: ignore[unresolved-import]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea why we need the ty: ignore[unresolved-import] statements in this file?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've realised that benchmarks aren't currently being checked by ty. I'm going to push adding this config and fixing the few warnings.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we installing these dependencies?

Comment thread pyproject.toml
"camb",
"cosmology-api",
"cosmology-compat-camb",
"glass-ext-camb",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the issues we were having with glass-ext-camb was because previously they were only included as optional-dependencies, whereas now we have them as dependency-group.

@connoraird

Copy link
Copy Markdown
Contributor Author

Making a note that there seems to be an issue with OOM error on Archer2 exclusive nodes with JAX. This page contains potential solutions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

benchmarks Benchmarking work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add realistic benchmarks

3 participants