[ROCm] Add ROCm/HIP wheel build, CI, and release parity with CUDA (#3171) - #3184
[ROCm] Add ROCm/HIP wheel build, CI, and release parity with CUDA (#3171)#3184andyluo7 wants to merge 8 commits into
Conversation
Closes the ROCm CI parity gap (kvcache-ai#3171). Mooncake already supports AMD GPUs in the transfer engine (USE_HIP, hip_transport, dmabuf MR registration from kvcache-ai#2225), but there was no CI to build/test the wheel and no published mooncake-transfer-engine-rocm package. The transfer engine is torch-independent and has no device kernels, so the ROCm wheel is GPU-arch-agnostic (one wheel covers gfx942 and gfx950) and the ROCm runtime (libamdhip64/libhsa-runtime64) is auditwheel-excluded and bound at load time -- so the same wheel works in both the upstream vllm/vllm-openai-rocm and lmsysorg/sglang ROCm images. - scripts/build_wheel.sh: add HIP_BUILD variant -> mooncake-transfer-engine-rocm (ROCm runtime libs were already in the auditwheel exclude list). - .github/workflows/ci_rocm.yml: PR build check in the ROCm dev image (py3.10 + py3.12), mirroring ci_cu13.yml. No GPU needed to compile. - .github/workflows/release-rocm.yaml: tag-triggered build+publish, mirroring release-musa.yaml (self-contained container build), py3.10-3.13. - ci.yml: wire build-wheel-rocm into the CI graph and CI Gate. Validated on real hardware (build in rocm/dev-ubuntu-22.04:7.2.3-complete, installed into the target images): - MI300X gfx942 (Hotaisle): SGLang v0.5.16-rocm720-mi30x (cp310) and vLLM v0.26.0 (cp312) -- mooncake_master --version + tests/test_transfer_on_hip.py (2/2 sync read+write loopback transfers) pass on-device. - MI355X gfx950 (Crusoe): same images (mi35x variants), same tests. Signed-off-by: andyluo7 <andy.luo@amd.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The rocm/dev-ubuntu image ships without git, so actions/checkout fell back to a REST-API source tarball (no .git), and dependencies.sh's `git submodule sync` failed with "not a git repository". Install git before the checkout step and fetch submodules in checkout, matching how the git-provisioned CUDA/MUSA containers behave. Signed-off-by: andyluo7 <andy.luo@amd.com>
Setting PATH through GITHUB_ENV is not honored reliably across steps, so cmake's USE_HIP configure could not find hipify-perl. Use GITHUB_PATH and also export PATH=/opt/rocm/bin in the configure/build/wheel steps. Signed-off-by: andyluo7 <andy.luo@amd.com>
The image's system python3.10 exists but ships without the venv module, so
the wheel smoke test's `python3.10 -m venv` failed ("ensurepip is not
available"). Install python{ver}-venv/-dev unconditionally instead of only
when the interpreter is missing (3.12 came from deadsnakes with venv, which
is why only 3.10 failed).
Signed-off-by: andyluo7 <andy.luo@amd.com>
The etcd-wrapper build fetches Go modules from proxy.golang.org, which can fail with transient stream errors and stop the build. Retry cmake --build up to 3 times; Go caches already-fetched modules so retries resume. Signed-off-by: andyluo7 <andy.luo@amd.com>
Aionw
left a comment
There was a problem hiding this comment.
Found one release-blocking issue in the historical-tag backfill path.
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag) || github.ref }} |
There was a problem hiding this comment.
[P1] Preserve the ROCm package rename when backfilling old tags
For workflow_dispatch, this checks out the historical tag before running scripts/build_wheel.sh. Tags predating this PR—including the documented example v0.3.12—do not recognize HIP_BUILD and have no ROCm package-name branch, so HIP_BUILD=1 is ignored and the script produces mooncake-transfer-engine instead of mooncake-transfer-engine-rocm. That mislabeled ROCm build then proceeds to the existing GitHub Release and PyPI upload steps. Please apply the ROCm metadata rename independently of the tagged script, or reject tags that do not contain HIP_BUILD support.
There was a problem hiding this comment.
Good catch — fixed in e1d9bcd. Two guards, so a mislabeled ROCm build can never reach the GitHub Release / PyPI upload:
- Reject unsupported tags — after checkout, the job fails if the checked-out
scripts/build_wheel.shhas noHIP_BUILDbranch, with a clear message to use a tag at/after ROCm support landed. - Assert the package name (defense in depth) — after
build_wheel.sh, the job fails unless the produced wheel ismooncake_transfer_engine_rocm-*.whl, so an ignoredHIP_BUILD(from any cause) can't proceed to publish.
Note: the same backfill-with-variant pattern exists in release-musa.yaml and release-npu.yaml (a pre-ROCm tag would similarly mislabel there) — happy to file a follow-up to add the same guard to those, keeping this PR scoped to ROCm.
A workflow_dispatch backfill checks out a historical tag and runs its scripts/build_wheel.sh. Tags predating ROCm support have no HIP_BUILD branch, so HIP_BUILD=1 is ignored and the script emits the default "mooncake-transfer-engine" package, which the publish steps would then upload over the CUDA package on PyPI. - Reject any checked-out tree whose scripts/build_wheel.sh lacks HIP_BUILD. - Defense in depth: assert the built wheel is mooncake-transfer-engine-rocm before the upload/publish steps run. Signed-off-by: andyluo7 <andy.luo@amd.com>
Aionw
left a comment
There was a problem hiding this comment.
One follow-up issue remains in the manual historical-tag backfill path.
| # scripts/build_wheel.sh would ignore HIP_BUILD and emit the default | ||
| # "mooncake-transfer-engine" package, which the publish steps would | ||
| # then upload over the CUDA package on PyPI. Refuse such tags. | ||
| if ! grep -q 'HIP_BUILD' scripts/build_wheel.sh; then |
There was a problem hiding this comment.
[P2] Keep the advertised v0.3.12 backfill usable
This guard safely prevents a mislabeled wheel from being published, but it also makes the documented workflow_dispatch example impossible: HIP_BUILD is introduced by this PR, so v0.3.12—and every tag that exists when this PR merges—will fail here. As a result, manual dispatch cannot backfill the initial ROCm package; it can only rerun future tags that already contain this change. If historical backfill is intended, please apply the ROCm package metadata rename in the workflow for legacy tags and keep the post-build filename assertion. Otherwise, update the input description to remove the v0.3.12 example and state explicitly that only tags containing HIP_BUILD are supported.
There was a problem hiding this comment.
Went with option (b) in 2dfa5a1 — the guard stays (it correctly prevents a mislabeled publish), and I've updated the workflow_dispatch input so it no longer advertises the pre-ROCm v0.3.12 example. It now reads "Release tag to (re)build the ROCm wheel for (must contain HIP_BUILD support)", with a comment noting older tags are rejected by design.
Backfilling the initial ROCm package onto a tag whose source predates ROCm support isn't meaningful (it'd build ROCm from a tree that never had it), so manual dispatch is intentionally limited to tags at/after this change. The first ROCm release will come from the normal v* tag push once this merges.
|
This workflow publishes Before merging/enabling the workflow, please confirm that PyPI has a pending Trusted Publisher configured with the exact identity |
Per review follow-ups (Aionw): - workflow_dispatch input no longer advertises the pre-ROCm v0.3.12 example; it states only tags containing HIP_BUILD support can be built (older tags are rejected by the guard, so backfilling the initial ROCm package is not possible by design). - Publish to PyPI before attaching wheels to the GitHub Release so a PyPI auth/config failure fails cleanly without leaving a dangling release. - Document the one-time PyPI Trusted Publisher prerequisite for the new mooncake-transfer-engine-rocm project (OIDC, no API token). Signed-off-by: andyluo7 <andy.luo@amd.com>
|
Re: PyPI Trusted Publisher — good catch, addressed in 2dfa5a1:
That configuration is a one-time maintainer/PyPI-owner action I can't perform or verify from the PR side — @maintainers, please set up the pending Trusted Publisher for |
| # the NEW project `mooncake-transfer-engine-rocm` with identity | ||
| # owner/repo `kvcache-ai/Mooncake`, workflow `release-rocm.yaml`, and | ||
| # environment `pypi`. Without it the publish step fails. | ||
| - name: Publish package to PyPI |
There was a problem hiding this comment.
[P2] Keep the release recoverable after PyPI succeeds
Moving the PyPI upload first avoids leaving GitHub Release assets behind when authentication fails, but both uploads are still in the same job. If PyPI succeeds and softprops/action-gh-release then fails transiently, rerunning the failed job starts from the PyPI step, which rejects the already-uploaded filenames, so the workflow can no longer reach the GitHub Release upload.
Please split PyPI publishing and GitHub Release attachment into separate jobs, with the latter depending on the former. Then “re-run failed jobs” can retry only the GitHub Release job without republishing immutable PyPI files.
There was a problem hiding this comment.
Done in e164c96 — split into two jobs: publish-pypi and publish-github-release (the latter needs: publish-pypi). Now a transient action-gh-release failure can be re-run on its own via "re-run failed jobs" without restarting the immutable PyPI upload.
| exit 1 | ||
| fi | ||
|
|
||
| - name: Upload Python wheel artifact |
There was a problem hiding this comment.
[P2] Smoke-test the exact release wheels before publishing
The ROCm CI smoke test covers only Python 3.10 and 3.12 at the PR head, while this workflow builds and immediately publishes Python 3.10–3.13 wheels from the release tag. The release job currently checks only the filename, so packaging or ELF-layout failures in the exact release artifacts—especially cp311/cp313—can reach PyPI undetected.
Please install each matrix wheel into a fresh venv and run at least mooncake_master --version before uploading the artifact, matching the smoke test already used in ci_rocm.yml.
There was a problem hiding this comment.
Done in e164c96 — added a "Smoke test the exact release wheel" step in the build matrix that installs each per-version artifact (cp310–cp313) into a fresh venv and runs mooncake_master --version before the wheel is uploaded, matching ci_rocm.yml. This runs on the release tag's actual artifacts, so cp311/cp313 packaging/ELF issues are caught before PyPI. (mooncake_master --version needs no GPU.)
Per review follow-ups (Aionw): - Smoke-test the exact per-matrix release artifact (cp310-cp313): install each built wheel into a fresh venv and run `mooncake_master --version` before upload, so packaging/ELF failures (esp. cp311/cp313, not covered by the PR-head ci_rocm smoke test) can't reach PyPI. No GPU needed. - Split publishing into `publish-pypi` and `publish-github-release` (the latter needs the former). PyPI uploads are immutable, so a combined job that failed on the GitHub Release step after a successful PyPI upload could never be re-run; separate jobs let the release upload be retried on its own. Signed-off-by: andyluo7 <andy.luo@amd.com>
Summary
Closes #3171 (ROCm CI parity with CUDA CI).
Mooncake already supports AMD GPUs in the transfer engine (
USE_HIP,hip_transport/, dmabuf MR registration from #2225), but there was no CI to build/test the ROCm wheel and no publishedmooncake-transfer-engine-rocmpackage. This adds that, mirroring the existing CUDA/MUSA workflows.The transfer engine is torch-independent and has no device kernels, so the ROCm wheel is GPU-arch-agnostic (one wheel covers gfx942 and gfx950), and the ROCm runtime (
libamdhip64/libhsa-runtime64) is already inbuild_wheel.sh's auditwheel exclude list and bound at load time. As a result the same wheel works in both the upstreamvllm/vllm-openai-rocmandlmsysorg/sglangROCm images.Changes
scripts/build_wheel.sh— addHIP_BUILDvariant → packagemooncake-transfer-engine-rocm(ROCm runtime libs were already excluded by auditwheel)..github/workflows/ci_rocm.yml— PR build check in the ROCm dev image (rocm/dev-ubuntu-22.04:7.2.3-complete), py3.10 + py3.12, mirroringci_cu13.yml. No GPU needed to compile HIP..github/workflows/release-rocm.yaml— tag-triggered build + publish, mirroring the self-contained container build inrelease-musa.yaml; py3.10–3.13; publishes to PyPI (needs aPYPI_*token /pypienvironment, same as other variants).ci.yml— wirebuild-wheel-rocminto the CI graph and the CI Gate.Validation (on real AMD hardware)
Built the wheel in
rocm/dev-ubuntu-22.04:7.2.3-complete, installed it into the latest upstream images, and ranmooncake_master --version+mooncake-wheel/tests/test_transfer_on_hip.py(registers HIP buffers, loopback sync read+write with data verification):vllm/vllm-openai-rocm:v0.26.0lmsysorg/sglang:v0.5.16-rocm720-mi30xvllm/vllm-openai-rocm:v0.26.0lmsysorg/sglang:v0.5.16-rocm720-mi35xlddconfirmsengine.sobindslibamdhip64.so.7/libhsa-runtime64.so.1to the host image's/opt/rocmat runtime (nothing vendored, no torch dependency).Environment note (not a wheel/CI issue)
On the Crusoe MI355X node, when the container is given the ionic RoCE NICs,
test_transfer_on_hip.pyexercises RDMA GPU-VRAM registration. The dmabuf path is taken correctly (hsa_amd_portable_export_dmabufsucceeds), butibv_reg_dmabuf_mron the ionic NIC returnsEFAULT— a node-level ionic kernel-driver limitation registering GPU dmabuf MRs (the same NIC family works on other clusters). This is independent of this PR (same wheel, same test); the transfer engine functions correctly via the local transport, and RDMA works wherever the NIC/driver supports GPU dmabuf MR registration.🤖 Generated with Claude Code