Skip to content

One thread per MPI rank by default (OMP/OpenBLAS/MKL/Accelerate), threads untouched without MPI - #102

Open
cekees wants to merge 3 commits into
mainfrom
mpi-single-thread-default
Open

cekees wants to merge 3 commits into
mainfrom
mpi-single-thread-default

Conversation

@cekees

@cekees cekees commented Oct 24, 2026

Copy link
Copy Markdown
Owner

What

Under MPI with more than one rank, import proteus sets OMP_NUM_THREADS, OPENBLAS_NUM_THREADS, MKL_NUM_THREADS and VECLIB_MAXIMUM_THREADS to 1 unless the user already set them. A serial run, or one rank, is untouched, so the no-MPI mode keeps whatever threads the libraries choose (as decided: threads stay supported there).

proteus/_threads.py holds the rules; proteus/__init__.py applies them from the launcher's announced size (Open MPI, MPICH/Hydra, Slurm, PMIx), and proteus/Comm.py applies them again from the real communicator size.

Why

C30 baseline, section 6.2 of ON_NODE_PERFORMANCE_BASELINE.md: the legacy kernels are limited by instruction issue on one core, and OpenBLAS starts a thread per core in every rank (16 per rank on the M4 Max), which oversubscribes a node run one rank per core.

Two things the measurement showed

  • OPENBLAS_NUM_THREADS=1 alone does nothing on conda-forge's OpenBLAS (built on OpenMP): 13.8 cores busy in one process with only it set, 1.0 with OMP_NUM_THREADS=1. All four variables are set. The baseline doc's wording named only OpenBLAS; corrected there.
  • The variables are read when numpy loads its BLAS. If numpy was imported before proteus, setting them is too late (8.2 cores busy per rank in the test). Comm then limits the loaded libraries through threadpoolctl if installed (added to the three dev environments) and logs a warning if not.

Tests (test/test_thread_defaults.py, 14)

Rules on a fake environment; import proteus under each launcher variable, one rank, and a user value; and real mpiexec -n 2 ranks measuring BLAS CPU time over wall time (proteus first, numpy first, one rank, user value kept). Negative control: with the runtime limit disabled the numpy-first test fails at 8.2.

Not in this PR

The generated Kokkos kernels will read OMP_NUM_THREADS too, so rank-per-core stays the default there and hybrid runs set it explicitly.

🤖 Generated with Claude Code

cekees and others added 3 commits October 24, 2026 08:19
… thread counts are 1 under MPI

Under MPI with more than one rank, import proteus (and proteus.Comm, which
knows the real rank count) sets OMP_NUM_THREADS, OPENBLAS_NUM_THREADS,
MKL_NUM_THREADS and VECLIB_MAXIMUM_THREADS to 1 unless the user set them.
A serial run, or a single rank, is untouched and may use threads.

OPENBLAS_NUM_THREADS alone is not enough: conda-forge's OpenBLAS is built on
OpenMP and ignores it (13.8 cores busy in one process with only that set; one
with OMP_NUM_THREADS=1). The variables are read at library load, so when numpy
was imported before proteus, Comm limits the loaded libraries through
threadpoolctl if it is installed (added to the dev environments) and logs a
warning if not.

Tests cover the rules on a fake environment, import proteus under each
launcher variable, and real mpiexec ranks measuring BLAS CPU time per wall
time with proteus first and with numpy first.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… the ranks' environment

clean_environment dropped every OMPI_*, PMIX_* and PMI_* variable, including
CI's OMPI_MCA_* settings that keep Open MPI off UCX; two-rank launches then
failed to create a UCP worker and hung until the timeout on linux-64 and
linux-aarch64. Only the launcher's size announcements are removed now.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…r until it has enough points

It used the module's shared generator and 40 draws. Where that generator
stood depended on which tests the xdist worker had run first, and for n=3
the peak is narrow enough that one linux-aarch64 run got 4 points above
the cutoff and failed 'assert checked >= 5'. A generator of its own, and
draws until 8 points are above the cutoff, make it independent of order.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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.

1 participant