Skip to content

CI: Test on Windows on ARM - #1003

Merged
bashtage merged 11 commits into
mainfrom
a90-windows-arm
Nov 5, 2026
Merged

bashtage merged 11 commits into
mainfrom
a90-windows-arm

Conversation

@bashtage

@bashtage bashtage commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Adds a GitHub Actions job that builds arch and runs the whole test suite on Windows on ARM (ARM64), on GitHub's windows-11-arm runner. arch builds, and every test passes on Python 3.13, 3.14 and 3.15, with no BLAS thread limit.

Results

Run 41928225500, numpy 2.5.4, SciPy 1.18.1, pandas 3.1.1, statsmodels 0.15.0, matplotlib 3.11.2:

Python numba Tests Slow tests
3.13.17 0.68.1 31,579 passed, 457 skipped 784 passed, 1 skipped
3.14.9 0.68.1 31,579 passed, 457 skipped 784 passed, 1 skipped
3.15.1 none (no win_arm64 wheels) 30,480 passed, 462 skipped 784 passed, 1 skipped

numpy's OpenBLAS reported 4 threads in every job, one for each of the runner's cores.

The job

.github/workflows/windows-arm.yml runs on Python 3.13 and 3.14 with numba, and on 3.15 without it. Python 3.12 can't be tested, because statsmodels only has win_arm64 wheels from 3.13. For each Python, the job:

  • checks that Python is ARM64 (win-arm64), so that it can't test an x64 Python running under emulation;
  • installs the dependencies from wheels only (--only-binary=:all:), so arch is the only package that is compiled;
  • builds and installs the wheel, and imports the compiled extensions directly. arch falls back to the pure Python code without warning when an extension can't be imported;
  • doesn't limit the BLAS threads, and fails if any thread limit is set, or if numpy's OpenBLAS reports fewer than 2 threads. threadpoolctl can't find that OpenBLAS, so the job asks it directly;
  • runs the tests that the other CI runs (-m "not slow", with pytest-xdist), then the slow tests, whether or not the first ones pass.

It runs on pushes to main and on pull requests, and a new push to a pull request cancels the run before it.

Why there's no thread limit

The other CI sets OPENBLAS_NUM_THREADS, OMP_NUM_THREADS and MKL_NUM_THREADS to 1. With that limit, the first run of this job passed 31,565 tests, and 14 tests crashed in numpy.linalg.inv and solve, all in the unit root code. The experiments in #1004 to #1007 traced the crashes to the OpenBLAS of numpy's win_arm64 wheels (numpy/numpy#29442):

  • inv and solve crash for 23×23 to 31×31 matrices, with an access violation in LAPACK's dgesv;
  • they crash only when OpenBLAS starts with 1 thread. With 2 or 3 threads they hang at some of those sizes, and with 4 (the runner's cores) or no limit they run fine;
  • every numpy with win_arm64 wheels has the bug, from 2.3.0 to the nightly.

Without the limit, the 14 tests pass.

Release notes

The 9.0 notes change in three places:

  • Highlights and Compatibility: wheels for Windows on ARM (ARM64), for Python 3.13 to 3.15. Users shouldn't limit OpenBLAS to one thread on this platform, with either OPENBLAS_NUM_THREADS=1 or OMP_NUM_THREADS=1.
  • Documentation and maintenance: the new job, and why it has no thread limit.

Wheels

The release wheels are built in arch-wheels, and bashtage/arch-wheels#31 adds win_arm64 wheels for Python 3.13 to 3.15. All 27 of its jobs pass (run 41930418282). Each of the three Windows on ARM jobs builds and repairs a win_arm64 wheel and tests it: 30,180 passed and 1,546 skipped, without the slow tests and with no BLAS thread limit. The notes assume that #31 is merged before 9.0 is released.

Not included

The branch's history includes the experiments: commits that removed the Azure pipeline and the wheel builds and added scripts that isolated the crashes. Later commits restore and remove them, so the change against main is only the workflow and the release notes.

🤖 Generated with Claude Code

Build the wheel on the windows-11-arm runner with Python 3.14 and test the
installed package. The dependencies, numba included, are installed from
win_arm64 wheels only. The compiled extensions are imported directly, since
arch falls back to the pure Python code when they cannot be imported.
@codecov

codecov Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.44%. Comparing base (88d011a) to head (5c4f825).
⚠️ Report is 12 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1003   +/-   ##
=======================================
  Coverage   99.44%   99.44%           
=======================================
  Files         152      152           
  Lines       34110    34110           
  Branches     2586     2586           
=======================================
  Hits        33920    33920           
  Misses        114      114           
  Partials       76       76           
Flag Coverage Δ
adder 99.38% <ø> (ø)
subtractor 99.38% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The first run crashed with access violations in numpy.linalg.inv and solve,
in the OpenBLAS of numpy's win_arm64 wheel, in 14 unit root tests. Run only
the univariate tests, and then the multiple comparison tests if they pass, in
a single process with the name of each test. Show the BLAS that numpy loads.

ci/github-actions/windows_arm_linalg.py runs each test that crashed in its own
process and saves the arguments of every inv and solve before the call, so
the matrix of a crash is kept. It passes each of these matrices, and random
matrices of sizes 1 to 160, to inv or solve in processes that only import
numpy, with other OpenBLAS kernels and with 1 and 4 threads. The results are
in the job summary and the matrices are uploaded.
Remove the Azure pipeline and the wheel builds, which run the tests, so that
only the Windows on ARM job runs the tests on this branch. Not for merging.
@bashtage bashtage changed the title CI: Build and test on Windows on ARM CI: Experiment with Windows on ARM (not for merging) Nov 4, 2026
Load numpy with importlib, since it is only imported so that threadpoolctl
finds its BLAS. Mark the subprocess import and call, which only run this
script with its own arguments and without a shell. Replace the asserts with
an error.
The crashes reproduce with numpy alone on the saved matrices, with 1 OpenBLAS
thread and not with 4. Add a step that calls inv of the identity, and inv and
solve of a random positive definite matrix, at sizes 1 to 64 and every 8th to
160 with 1 thread, each in its own process, and then with other numbers of
threads at the sizes that crash. Show the processor, Windows version and
runner image. Record the last call of a sweep rather than the end of the
traceback.
Add det (the LU factorization alone) and cholesky to the map of the sizes
that crash, and SciPy's inv, lu_factor and lu_solve, which use SciPy's own
OpenBLAS, so that a crash can be placed in getrf or the solve, and in numpy or
OpenBLAS. Rerun the calls that crash in a thread with a 64 MB stack to rule out
a stack overflow. Show SciPy's build configuration.
The map ran a process for every call and size and took more than an hour,
so the job would time out. #1004 to #1007 map the crashes in minutes,
each in its own pull request. Not for merging.
numpy.linalg.inv and solve crash in the OpenBLAS of numpy's win_arm64
wheels only when OpenBLAS starts with 1 thread (see #1004 to #1007), so
do not set OPENBLAS_NUM_THREADS, OMP_NUM_THREADS or MKL_NUM_THREADS, and
fail if any thread limit is set or OpenBLAS reports fewer than 2 threads.
Run the tests that the other CI runs with pytest-xdist, then the slow
tests, on Python 3.13 and 3.14 with numba and 3.15 without it (no
win_arm64 wheels). statsmodels has no win_arm64 wheels for 3.12. Remove
the crash isolation, which moved to #1004 to #1007.
@bashtage bashtage changed the title CI: Experiment with Windows on ARM (not for merging) CI: Test on Windows on ARM (not for merging yet) Nov 4, 2026
They were removed while the Windows on ARM job was an experiment. They
are the same as on main.
Note the Windows on ARM job, which does not limit the BLAS threads, and
that arch builds and passes its tests on Windows on ARM with Python 3.13
and later, but crashes in NumPy's OpenBLAS with OPENBLAS_NUM_THREADS=1.
@bashtage bashtage changed the title CI: Test on Windows on ARM (not for merging yet) CI: Test on Windows on ARM Nov 4, 2026
arch-wheels builds and tests win_arm64 wheels for Python 3.13 to 3.15. Also
note that OMP_NUM_THREADS=1 limits OpenBLAS to one thread, too.
@bashtage
bashtage marked this pull request as ready for review November 5, 2026 11:11
@bashtage
bashtage merged commit 2c5e47d into main Nov 5, 2026
28 checks passed
@bashtage
bashtage deleted the a90-windows-arm branch November 5, 2026 11:11
bashtage added a commit that referenced this pull request Nov 11, 2026
This reverts commit f024069. The Windows on ARM test job, its 9.0 note and the
#1003 cite stay as they are on main.
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