Skip to content

vllm: Add version 0.29.0+cpu - #2093

Merged
luhenry merged 6 commits into
mainfrom
vllm
Sep 21, 2026
Merged

luhenry merged 6 commits into
mainfrom
vllm

Conversation

@luhenry

@luhenry luhenry commented Sep 19, 2026 •

Copy link
Copy Markdown
Member

Compiles vLLM's CPU inference backend (vllm._C and the PyO3 frontend) against the torch 2.13.0+cpu wheels this registry already publishes. Upstream publishes no riscv64 wheel.

Mirrors upstream's docker/Dockerfile.cpu, vLLM's only CPU wheel build.

Differs from upstream

  • -DVLLM_RVV_VLEN=0 - auto-detection would bake the runner's own VLEN into every wheel.
  • Rust toolchain installed in-container - upstream's CPU image ships one, the manylinux image does not.
  • --py-limited-api=cp312 where upstream passes cp38. torch==2.13.0 has no riscv64 wheel below cp312, so cp312 is the oldest interpreter this wheel can be built on and a cp38 tag would claim interpreters it was never compiled against.
  • numactl-devel installed in-container - upstream's Debian-based image's libnuma-dev under its Rocky name.

Matrix: one cp312 build producing one cp312-abi3 wheel, which serves cp312, cp313 and cp314 - the interpreters this registry's torch 2.13.0+cpu covers. The C extensions are built USE_SABI 3 and the PyO3 extension declares features=["pyo3/abi3-py38"], so the wheel is genuinely stable-ABI; upstream gets the tag by passing --py-limited-api to bdist_wheel, which cibuildwheel does not do on its own. No free-threaded leg, matching upstream, which publishes no cp314t wheel (and py_limited_api is off under Py_GIL_DISABLED by construction).

Testing: imports vllm and vllm._C from the installed wheel and reports the detected CPU architecture; upstream's own suites need model downloads. The import is not a formality here - it is what caught the sbgemm_ gap below, which the build itself cannot see.

License: OK

Patches

  • 0001-requirements-do-not-require-numba-on-riscv64.patch - Upstream-Status: To upstream. Without it the wheel is buildable but not installable: requirements/cpu.txt requires numba everywhere but s390x, and numba has no riscv64 wheel and cannot be built from sdist (llvmlite needs a conda-only patched LLVM with no linux-riscv64 subdir). numba is optional to vLLM by construction (is_numba_available()), and this is the configuration upstream already ships for s390x. riscv64-only.
  • 0002-cpu-probe-the-torch-openblas-for-sbgemm.patch - Upstream-Status: To upstream. cpu_extension.cmake defines VLLM_HAS_OPENBLAS whenever the installed torch wheel contains a libopenblas*.so*, and blas_gemm() then calls sbgemm_. Our riscv64 torch 2.13.0+cpu does ship torch/lib/libopenblas.so.0, but built without BUILD_BFLOAT16: it exports sgemm_ and no sbgemm_ at all, because OpenBLAS only has bf16 kernels for x86_64 Cooper Lake and later, ARM64 Neoverse, POWER10 and z14 and later. Since _C deliberately does not link OpenBLAS and expects libtorch.so to have loaded it RTLD_GLOBAL, the extension links cleanly and fails only on import vllm._C with undefined symbol: sbgemm_. The patch probes the library for the symbol and falls back to the PyTorch reference path blas_gemm.h already carries for this case (at::native::cpublas::gemm_no_downcast_stub, which libtorch_cpu.so does export). Architecture-agnostic, and it keeps current behaviour wherever the probe cannot run, so no working platform changes.

luhenry pushed a commit that referenced this pull request Sep 19, 2026
@luhenry luhenry changed the title vllm: build riscv64 CPU-backend wheels vllm: Add version 0.29.0+cpu Sep 19, 2026
@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-21 07:28 UTC

Add build-vllm.yml and docs/packages/vllm.yaml for vLLM 0.29.0+cpu, the
VLLM_TARGET_DEVICE=cpu build of vllm-project/vllm.

vLLM's CPU backend already has first-class riscv64 support upstream: a
riscv64 branch in cmake/cpu_extension.cmake, RVV kernels under csrc/cpu
(cpu_types_riscv*.hpp, sgl-kernels/gemm_int4.cpp, cpu_wna16.cpp), a
CpuArchEnum.RISCV platform, and platform_machine == "riscv64" markers in
requirements/cpu.txt that already pin torch==2.13.0 - exactly the version
this registry publishes as 2.13.0+cpu.

The wheel is built at the rv64gc baseline (-DVLLM_RVV_VLEN=0, upstream's
documented scalar RISC-V mode) rather than letting cmake auto-detect VLEN
from the runner's /proc/cpuinfo, which would bake that runner's vector
extensions into a wheel shipped to every riscv64 user.
cibuildwheel's before-build cwd is not guaranteed to be the package
directory when package-dir points at a subdirectory, so anchor the
requirements path explicitly.
csrc/cpu/utils.cpp includes <numa.h> and cmake/cpu_extension.cmake links
libnuma unconditionally (ENABLE_NUMA defaults to TRUE and is only turned
off for Apple), so the CPU extension does not compile without the NUMA
development headers. Upstream's docker/Dockerfile.cpu installs
libnuma-dev for exactly this; the manylinux image is Rocky 10, where the
same headers come from numactl-devel in appstream.
The wheel built, but importing it failed with

  ImportError: vllm/_C.abi3.so: undefined symbol: sbgemm_

cpu_extension.cmake turns on the OpenBLAS bf16 GEMM path whenever the
installed torch wheel contains a libopenblas*.so*, and _C resolves the
symbol through libtorch.so rather than linking OpenBLAS, so an OpenBLAS
without sbgemm_ links cleanly and only fails at import. The riscv64
torch 2.13.0+cpu wheel ships torch/lib/libopenblas.so.0 built without
BUILD_BFLOAT16: it exports sgemm_ but no sbgemm_ and no bf16 symbol at
all. Patch cmake to probe the library for the symbol and fall back to
blas_gemm.h's existing PyTorch reference path, whose
at::native::cpublas::gemm_no_downcast_stub libtorch_cpu.so does export.
Upstream's docker/Dockerfile.cpu builds its CPU wheel with
"setup.py bdist_wheel --py-limited-api=cp38"; cibuildwheel passes no such
flag, so our first green run produced
vllm-0.29.0+cpu-cp312-cp312-manylinux_2_39_riscv64.whl where upstream
publishes cp38-abi3 - one interpreter served instead of every one from
cp312 up, while this registry's torch 2.13.0+cpu covers cp312, cp313 and
cp314.

The wheel is genuinely stable-ABI already: CMakeExtension passes
py_limited_api (cmake builds the C extensions USE_SABI 3, hence
_C.abi3.so) and the PyO3 extension is declared with
features=["pyo3/abi3-py38"] and py_limited_api=True. Only the wheel tag
was missing, which is gotcha 11's setuptools-rust case.

Tagged cp312 rather than upstream's cp38 because torch 2.13.0 has no
riscv64 wheel below cp312, so cp312 is the oldest interpreter this can
ever be built on and a cp38 tag would claim interpreters the wheel was
not compiled against (gotcha 96).
@luhenry
luhenry marked this pull request as ready for review September 21, 2026 07:25
@luhenry
luhenry merged commit dc06417 into main Sep 21, 2026
9 checks passed
@luhenry
luhenry deleted the vllm branch September 21, 2026 07:25
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