Repository navigation
ENH: restrict CPU-specific code paths via REPROSEED_CPU #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yarikoptic-gitmate
wants to merge
5
commits into
master
Choose a base branch
from
claude/ecstatic-brahmagupta-jsqg3p
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
7f09bdf
BF: print messages to stderr, not stdout
claude 5f82297
ENH: restrict CPU-specific code paths via REPROSEED_CPU
claude dcbd85b
Merge remote-tracking branch 'origin/master' into claude/ecstatic-bra…
claude 216b22d
ENH: keep CPU variables already set by the caller
claude da85ebd
DOC: MKL_CBWR=AVX2 does not align MKL results on AMD CPUs
claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # Restricting CPU features | ||
|
|
||
| Numerical libraries choose CPU-specific (SIMD) code paths at run time, | ||
| e.g. AVX512 `exp` in [NumPy][numpy] or matrix multiplication kernels in | ||
| [OpenBLAS][openblas], which can produce results differing in the last | ||
| bits on different CPUs. `reproseed.sh` therefore restricts them to the | ||
| [x86-64 microarchitecture level][x86-64-levels] given in | ||
| `REPROSEED_CPU`. | ||
|
|
||
| On x86-64 it defaults to `x86-64-v3` (AVX2 and FMA, supported by most | ||
| CPUs since Intel Haswell and AMD Excavator/Zen), for which the | ||
| following variables are exported: | ||
|
|
||
| | Library | Variable | Value | | ||
| |--------------------------|-------------------------------------------|-------------| | ||
| | [NumPy][numpy] | [`NPY_DISABLE_CPU_FEATURES`][npy-disable] | see below | | ||
| | [OpenBLAS][openblas] | [`OPENBLAS_CORETYPE`][openblas-vars] | `Haswell` | | ||
| | [MKL][mkl] | [`MKL_CBWR`][mkl-cbwr] | `AVX2` | | ||
| | [PyTorch][pytorch] | [`ATEN_CPU_CAPABILITY`][aten-cpu] | `avx2` | | ||
| | [oneDNN][onednn] | [`ONEDNN_MAX_CPU_ISA`][onednn-isa] | `AVX2` | | ||
| | [oneDNN][onednn] < 2.5 | [`DNNL_MAX_CPU_ISA`][dnnl-isa] | `AVX2` | | ||
|
|
||
| - `NPY_DISABLE_CPU_FEATURES` lists the AVX512 targets of NumPy < 2.4 | ||
| (`AVX512F AVX512_SKX AVX512_ICL AVX512_SPR`) and >= 2.4 | ||
| (`X86_V4 AVX512_ICL AVX512_SPR`), since disabling one target does not | ||
| disable the others. It is not set if `/proc/cpuinfo` shows no | ||
| AVX512, or if [`NPY_ENABLE_CPU_FEATURES`][npy-enable] is set (NumPy | ||
| refuses both). | ||
| - `OPENBLAS_CORETYPE` and `ATEN_CPU_CAPABILITY` select code for the | ||
| level, which would crash on a CPU without AVX2 or FMA, so they are set | ||
| only if `/proc/cpuinfo` confirms those. That excludes some | ||
| Pentium/Celeron/Atom CPUs, VMs with a generic CPU model (e.g., | ||
| `qemu64`), and non-Linux systems; results there might differ. | ||
| - `OPENBLAS_CORETYPE` affects only OpenBLAS builds with | ||
| [`DYNAMIC_ARCH`][openblas-dynamic-arch], e.g. in NumPy wheels, Debian, | ||
| conda-forge. | ||
| - `MKL_CBWR` branches other than `AUTO` and `COMPATIBLE` are available | ||
| only on Intel CPUs, others silently use `AUTO`, so MKL results can | ||
| still differ between Intel and AMD CPUs. Set `MKL_CBWR=COMPATIBLE` | ||
| (slower) to avoid that. | ||
| - oneDNN is used by PyTorch and [TensorFlow][tensorflow]. | ||
|
|
||
| Variables already set by the caller, even to an empty value, are left | ||
| as they are. With `REPROSEED_CPU=native` none of them is set. It is the default on other | ||
| architectures (e.g., aarch64), for which no restrictions are implemented | ||
| (yet). | ||
|
|
||
| ## Checking code paths in use | ||
|
|
||
| - NumPy and its OpenBLAS: [`numpy.show_runtime()`][numpy-show-runtime] | ||
| (OpenBLAS `architecture` requires [threadpoolctl][threadpoolctl]) | ||
| - PyTorch: [`torch.backends.cpu.get_cpu_capability()`][torch-cpu-capability] | ||
| - oneDNN: [`ONEDNN_VERBOSE=1`][onednn-verbose] | ||
|
|
||
| ## Caveats | ||
|
|
||
| - NumPy warns on import about listed features it does not know (or, in | ||
| NumPy 1.20-1.24, the CPU lacks): with an `ImportWarning`, hidden by | ||
| default, since NumPy 1.25, but with a visible `RuntimeWarning` before. | ||
| If warnings are turned into errors (e.g., `filterwarnings = error` in | ||
| pytest), ignore those, or set `REPROSEED_CPU=native`. | ||
| - If `import numpy` fails with "You cannot disable CPU feature ..., | ||
| since it is part of the baseline optimizations", that NumPy was built | ||
| to require AVX512. Set `REPROSEED_CPU=native`. | ||
| - Results of multi-threaded computation (e.g., OpenBLAS, MKL, OpenMP | ||
| reductions, [ANTs][ants]) can also depend on the number of threads, so | ||
| use the same [`OMP_NUM_THREADS`][omp-num-threads], | ||
| [`OPENBLAS_NUM_THREADS`][openblas-vars], | ||
| [`MKL_NUM_THREADS`][mkl-threads], | ||
| [`ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS`][itk-threads] etc. when | ||
| comparing results. | ||
|
|
||
| [ants]: https://github.com/ANTsX/ANTs | ||
| [aten-cpu]: https://github.com/pytorch/pytorch/blob/v2.14.1/aten/src/ATen/native/DispatchStub.cpp#L30 | ||
| [dnnl-isa]: https://github.com/uxlfoundation/oneDNN/blob/v2.4/src/cpu/x64/cpu_isa_traits.cpp#L34 | ||
| [itk-threads]: https://github.com/InsightSoftwareConsortium/ITK/blob/v5.4.7/Modules/Core/Common/src/itkMultiThreaderBase.cxx#L296 | ||
| [mkl]: https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl.html | ||
| [mkl-cbwr]: https://www.intel.com/content/www/us/en/docs/onemkl/developer-guide-linux/2025-2/specifying-code-branches.html | ||
| [mkl-threads]: https://www.intel.com/content/www/us/en/docs/onemkl/developer-guide-linux/2025-2/techniques-to-set-the-number-of-threads.html | ||
| [npy-disable]: https://numpy.org/doc/stable/reference/simd/build-options.html#runtime-dispatch | ||
| [npy-enable]: https://github.com/numpy/numpy/blob/v2.5.3/numpy/_core/src/common/npy_cpu_features.c#L53 | ||
| [numpy]: https://numpy.org | ||
| [numpy-show-runtime]: https://numpy.org/doc/stable/reference/generated/numpy.show_runtime.html | ||
| [omp-num-threads]: https://www.openmp.org/spec-html/5.0/openmpse50.html | ||
| [onednn]: https://github.com/uxlfoundation/oneDNN | ||
| [onednn-isa]: https://uxlfoundation.github.io/oneDNN/dev_guide_cpu_dispatcher_control.html | ||
| [onednn-verbose]: https://uxlfoundation.github.io/oneDNN/dev_guide_verbose.html | ||
| [openblas]: https://www.openmathlib.org/OpenBLAS/ | ||
| [openblas-dynamic-arch]: https://www.openmathlib.org/OpenBLAS/docs/faq/#how-to-choose-target-manually-at-runtime-when-compiled-with-dynamic_arch | ||
| [openblas-vars]: https://www.openmathlib.org/OpenBLAS/docs/runtime_variables/ | ||
| [pytorch]: https://pytorch.org | ||
| [tensorflow]: https://www.tensorflow.org | ||
| [threadpoolctl]: https://github.com/joblib/threadpoolctl | ||
| [torch-cpu-capability]: https://docs.pytorch.org/docs/2.14/backends.html#torch.backends.cpu.get_cpu_capability | ||
| [x86-64-levels]: https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AI draft - not reviewed by John
PR 9's default x86-64-v3 profile sets
MKL_CBWR=AVX2, including replacing an explicitCOMPATIBLEsetting. With MKL 2025.2.0, theprbenchmark using PR 9's default reports AVX2 (10) on Typhon and Smaug (Intel), but AUTO (2) on Unity cpu053 (AMD EPYC 7763). At each fixed thread count (1, 2, or 4), all ten tested arrays agree between the two Intel hosts. Comparing either Intel host with Unity at the same thread count, all four MKL GEMMs differ while the six NumPy/OpenBLAS arrays agree.PR 11 preserves an explicitly supplied
MKL_CBWR=COMPATIBLE. In thecompatible_fixedbenchmark, that configuration makes all ten tested arrays agree across the three hosts at each fixed thread count. The benchmark candidate shell script matches PR 11 commit51d4c3578fe3bb43aeae87a29c5768a2cc53c4fdbyte for byte. PR 11 still defaults to AVX2 when MKL_CBWR is unset; it does not switch the default to COMPATIBLE.Recorded metadata and comparisons; AMD diagnostic for PR 9's default; Intel branch semantics.
These comparisons support preserving the explicit COMPATIBLE choice for the tested workload and library version. They do not establish agreement across different thread counts or untested workflows. A default profile for mixed Intel/AMD use is a separate question; no performance tradeoff was measured.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please chime in back when it is ready/reviewed by John?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two commits address this:
216b22d: a value the caller has already set, such asMKL_CBWR=COMPATIBLEorAVX2,STRICT, is now kept rather than overridden. This applies to every CPU variable, even when set to an empty value.da85ebd: the docs now say that oneMKL branches other thanAUTO/COMPATIBLEare Intel-only, so the defaultMKL_CBWR=AVX2does not align MKL results on AMD CPUs, and they point toMKL_CBWR=COMPATIBLEfor mixed Intel/AMD use.Whether the default itself should become
COMPATIBLE(consistent across vendors, but slower) is left open for @yarikoptic.Generated by Claude Code