Commit f4b272f
committed
Build and publish CUDA wheels
## The problem
The wheel can carry the CUDA delegate, but no published wheel contains one: there is no CUDA
row in any workflow, so a GPU user has to build from source.
## The change
Add the workflows that build and publish CUDA wheels for Linux x86_64 and aarch64, and a smoke test
that checks each wheel from the artifact itself. The build machines for these rows have no GPU, so
the smoke test does not execute a model; it verifies the CUDA libraries are present, that the
declared runtime matches the wheel's CUDA version, that nothing resolves through the build
machine's toolkit, and that the shipped device code covers every GPU architecture the row claims.
```
executorch-1.5.0-cp312-cp312-manylinux_2_28_x86_64.whl +cu130
```
A release publishes CUDA 12.6, 13.0 and 13.2, for Python 3.10 through 3.13. A pull request builds a
single row instead of all twelve, because a full matrix costs hours for little extra signal.
Which GPU architectures each row compiles for is chosen per row rather than detected on the builder.
Detecting it would produce a wheel carrying device code for whatever machine happened to build it,
which installs fine and then fails at the first GPU call.
The aarch64 CUDA 12.6 row also compiles for compute capability 8.7, which is an embedded module.
Every other row lists only the architectures the published PyTorch build for that train covers, and
by that rule 8.7 would be left out, because the generic aarch64 build of this train carries 8.0 and
9.0 only. It is included because this is the only row whose CUDA major version matches what that
module's software release ships, and because this wheel declares no PyTorch dependency: a user there
supplies the build that carries their architecture. Leaving 8.7 out does not protect them from a bad
pairing, it only removes the device code they need. Without it, a model reaching one of the shipped
optional operators, quantized matrix multiply, sort or random number generation, fails at the first
launch on that device.
Two guards keep a release honest:
- if the shared matrix generator stops offering a combination this policy advertises, the step fails
instead of quietly publishing fewer wheels. A missing job is otherwise a green check for a wheel
that was never built.
- if a row reaches the architecture list with no CUDA version, the build refuses rather than falling
back to the builder's GPU. A TORCH_CUDA_ARCH_LIST that holds only named GPU families PyTorch
accepts, such as "Hopper", now fails to configure instead of quietly leaving
CMAKE_CUDA_ARCHITECTURES unset and taking the compiler default. The three named forms CMake
itself understands, "native", "all" and "all-major", are rejected before this logic runs:
torch resolves the list with its own bundled CUDA architecture module, which does not know
those names and stops the configure. That is upstream behaviour, not something this change
introduces or can work around, so a caller has to name architectures explicitly.
Windows CUDA is deliberately absent. The separate shared libraries this wheel exists to ship are
Linux only today, so a Windows CUDA wheel would carry a delegate a C++ application still could not
link.
## Test plan
- built the full release matrix, twelve wheels, and confirmed each one's contents match the row it
claims: the CUDA libraries present, the CUDA runtime declared, and device code for every GPU
architecture the row advertises.
- ran a GPU model end to end from a CI-built wheel on three NVIDIA GPUs covering three device
architectures, with output identical to eager PyTorch on each (largest absolute difference 0),
and inspected the wheel for a fourth device it cannot execute on.
- ran the matrix filter over generated inputs, including incomplete and malformed ones, and
confirmed it refuses rather than publishing a partial release: a missing CUDA version, a missing
python, or a python present on rows this policy does not build are each reported by name.
- confirmed a CPU row still produces a CPU wheel on a builder that happens to have a CUDA toolkit
installed.
- the newest architecture also ships in its portable form, so a GPU newer than any in the row can
still run by having the driver compile it at load time. Checked with `cuobjdump --list-ptx`, since
`--list-elf` prints identical output whether or not the portable form is present.
- every library that carries GPU device code covers the whole row on its own.
- the declared CUDA packages are compared against the expected set in BOTH directions. A one-way
comparison accepted a wheel that omitted required packages, and a name-suffix comparison accepted
cross-train names because for CUDA 13 the suffix is empty.
- the python axis is an allowlist, matching the CUDA axis. Testing only the disabled list let any
python not on it through: a 3.9 row was emitted successfully.
- `install_utils.py` is in both CUDA workflows' path filters. It owns the supported CUDA train list
and the toolkit detection, so a change there previously ran no CUDA wheel job.
- requesting the JetPack rows fails with its own reason instead of the generic empty-matrix message,
since both of its lists are deliberately empty and no workflow asks for them.
- torchao keeps its CUDA channel where that channel exists. Falling back to the plain nightly index
was needed only on aarch64, where the CUDA channel publishes nothing, and doing it everywhere
changed which torchao an x86_64 install resolves.
- the CUDA smoke test now asserts the QnnBackend and OpenvinoBackend registrations that a CPU Linux
row asserts. The CUDA build enables OpenVINO on every Linux architecture and downloads the QNN
SDK on x86_64, so a CUDA wheel carries both backends; a previous premise that "a CUDA row is not
built with them" was false, and dropping the checks meant those two backends were unverified on
every CUDA wheel.
Known gap: no automated job runs a CUDA model on real hardware before publication. Running a model
on real hardware is a separate release-time step that a person owns today, not an automated job
wired into these workflows.
ghstack-source-id: 95d67c7
ghstack-comment-id: 5220374521
Pull-Request: #216681 parent d863e9c commit f4b272f
9 files changed
Lines changed: 1110 additions & 4 deletions
File tree
- .ci/scripts/wheel
- .github
- scripts
- workflows
- backends/cuda
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
0 commit comments