Skip to content

Commit d300645

Browse files
authored
feat(python): package CUDA as an optional extension (#8510)
Keeps the base vortex-data wheel CPU-only and model CUDA support as a separate `vortex-data-cuda` extension package imported as `vortex_cuda`. The base package exposes `vortex.cuda_extension_installed()` to check whether the extension is importable, while `vortex_cuda.cuda_available()` performs the runtime CUDA probe. Wires `vortex-data[cuda]` to depend on the exact matching `vortex-data-cuda` version, and have the CUDA package depend on the exact matching `vortex-data` version. Release automation updates those pins with the workspace version, but this change intentionally does not add CUDA wheel publishing jobs yet. Keeps `vortex-python-cuda` out of the uv workspace so `uv --all-packages` on non-GPU CI does not build the CUDA crate chain. Add explicit CUDA Python checks to the GPU workflow instead, including the uv setup needed to run them. Embeds generated PTX into `vortex-cuda` when available so installed wheels do not depend on build-machine kernel file paths at runtime. --------- Signed-off-by: Alexander Droste <alexander.droste@protonmail.com>
1 parent d0d400c commit d300645

24 files changed

Lines changed: 345 additions & 63 deletions

File tree

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ jobs:
154154
run: |
155155
# Build docs for the whole workspace (aside from vortex-python that is having hard time with the python-docs syntax),
156156
# including all private docs.
157-
cargo doc --profile ci --no-deps --document-private-items --workspace --exclude vortex-python
157+
cargo doc --profile ci --no-deps --document-private-items --workspace --exclude vortex-python --exclude vortex-python-cuda
158158
# nextest doesn't support doc tests, so we run it here
159-
cargo test --profile ci --doc --workspace --all-features --exclude vortex-cxx --exclude vortex-jni --exclude vortex-ffi --exclude xtask --no-fail-fast
159+
cargo test --profile ci --doc --workspace --all-features --exclude vortex-cxx --exclude vortex-jni --exclude vortex-ffi --exclude xtask --exclude vortex-python-cuda --no-fail-fast
160160
161161
build-rust:
162162
name: "Rust build (${{matrix.config.name}})"
@@ -187,7 +187,7 @@ jobs:
187187
target: wasm32-unknown-unknown
188188
env:
189189
rustflags: "RUSTFLAGS='-A warnings --cfg getrandom_backend=\"unsupported\"'"
190-
args: "--target wasm32-unknown-unknown --exclude vortex --exclude vortex-cuda --exclude vortex-cub --exclude vortex-nvcomp --exclude vortex-datafusion --exclude vortex-duckdb --exclude vortex-tui --exclude vortex-zstd --exclude vortex-test-e2e-cuda --exclude vortex-sqllogictest --exclude vortex-parquet-variant"
190+
args: "--target wasm32-unknown-unknown --exclude vortex --exclude vortex-cuda --exclude vortex-cub --exclude vortex-nvcomp --exclude vortex-datafusion --exclude vortex-duckdb --exclude vortex-tui --exclude vortex-zstd --exclude vortex-test-e2e-cuda --exclude vortex-python-cuda --exclude vortex-sqllogictest --exclude vortex-parquet-variant"
191191
steps:
192192
- uses: runs-on/action@v2
193193
if: github.repository == 'vortex-data/vortex'
@@ -355,7 +355,7 @@ jobs:
355355
--exclude vortex-bench `
356356
--exclude vortex-python --exclude vortex-duckdb `
357357
--exclude vortex-fuzz --exclude vortex-cuda --exclude vortex-cuda-ffi `
358-
--exclude vortex-nvcomp --exclude vortex-cub --exclude vortex-test-e2e-cuda `
358+
--exclude vortex-nvcomp --exclude vortex-cub --exclude vortex-test-e2e-cuda --exclude vortex-python-cuda `
359359
--exclude duckdb-bench `
360360
--exclude lance-bench --exclude datafusion-bench --exclude random-access-bench `
361361
--exclude compress-bench --exclude xtask --exclude vortex-datafusion `

.github/workflows/codspeed.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,6 @@ jobs:
100100
--bench fsst_cuda \
101101
--bench runend_cuda \
102102
--profile bench
103-
- name: Verify CUDA kernels were built
104-
run: test -f vortex-cuda/kernels/gen/release/dynamic_dispatch.ptx
105-
- name: Stage CUDA kernels with Codspeed artifacts
106-
run: cp -R vortex-cuda/kernels/gen/release target/codspeed/cuda-kernels
107103
- name: Upload benchmark executables
108104
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
109105
with:
@@ -156,7 +152,6 @@ jobs:
156152
uses: CodSpeedHQ/action@d872884a306dd4853acf0f584f4b706cf0cc72a2
157153
env:
158154
CARGO_MANIFEST_DIR: ${{ github.workspace }}/vortex-cuda
159-
VORTEX_CUDA_KERNELS_DIR: ${{ github.workspace }}/target/codspeed/cuda-kernels
160155
with:
161156
run: cargo codspeed run $(printf -- '--bench %s ' ${{ matrix.benches }})
162157
token: ${{ secrets.CODSPEED_TOKEN }}

.github/workflows/cuda.yaml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@ jobs:
3838
filters: |
3939
cuda:
4040
- "vortex-cuda/**"
41+
- "vortex-python/python/vortex/__init__.py"
42+
- "vortex-python/pyproject.toml"
43+
- "vortex-python/test/test_cuda.py"
44+
- "vortex-python-cuda/**"
4145
- "vortex-test/**"
46+
- "pyproject.toml"
47+
- "uv.lock"
4248
- ".github/workflows/**"
4349
4450
cuda-build-lint:
@@ -62,7 +68,7 @@ jobs:
6268
command: >-
6369
cargo build --profile ci --locked --all-features --all-targets
6470
-p vortex-cuda -p vortex-cuda-ffi -p vortex-cub -p vortex-nvcomp
65-
-p gpu-scan-cli -p vortex-test-e2e-cuda
71+
-p gpu-scan-cli -p vortex-test-e2e-cuda -p vortex-python-cuda
6672
- name: Clippy CUDA crates
6773
run: |
6874
cargo clippy --profile ci --locked --all-features --all-targets \
@@ -72,6 +78,7 @@ jobs:
7278
-p vortex-nvcomp \
7379
-p gpu-scan-cli \
7480
-p vortex-test-e2e-cuda \
81+
-p vortex-python-cuda \
7582
-- -D warnings
7683
7784
cuda-test:
@@ -95,6 +102,10 @@ jobs:
95102
- uses: ./.github/actions/setup-rust
96103
with:
97104
repo-token: ${{ secrets.GITHUB_TOKEN }}
105+
- name: Install uv
106+
uses: spiraldb/actions/.github/actions/setup-uv@a746510eafaa926484c354541cfc49b2ec06cc63 # 0.18.6
107+
with:
108+
sync: false
98109
- name: Install nextest
99110
uses: taiki-e/install-action@9e1e5806d4a4822de933115878265be9aaa786d9 # v2
100111
with:
@@ -112,10 +123,19 @@ jobs:
112123
-p vortex-cub \
113124
-p vortex-nvcomp \
114125
-p vortex-test-e2e-cuda \
126+
-p vortex-python-cuda \
115127
--all-features \
116128
--no-fail-fast \
117129
--target x86_64-unknown-linux-gnu \
118130
--verbose
131+
- name: Python CUDA extension checks
132+
env:
133+
MATURIN_PEP517_ARGS: "--profile ci"
134+
run: |
135+
# --all-packages installs the shared dev tooling (basedpyright) from the
136+
# root `dev` group; --extra cuda adds the vortex-data-cuda extension.
137+
uv run --all-packages --extra cuda basedpyright vortex-python vortex-python-cuda
138+
uv run --all-packages --extra cuda pytest --benchmark-disable vortex-python/test/test_cuda.py vortex-python-cuda/test
119139
120140
cuda-test-sanitizer:
121141
needs: [changes]

.github/workflows/musl.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666
run: |
6767
cargo nextest run --cargo-profile ci --locked --workspace --no-fail-fast \
6868
--exclude vortex-cuda --exclude vortex-cub --exclude vortex-nvcomp \
69-
--exclude gpu-scan-cli --exclude vortex-test-e2e-cuda \
69+
--exclude gpu-scan-cli --exclude vortex-test-e2e-cuda --exclude vortex-python-cuda \
7070
--exclude vortex-duckdb --exclude duckdb-bench --exclude vortex-sqllogictest \
7171
--exclude vortex-bench --exclude lance-bench --exclude datafusion-bench --exclude vortex-datafusion \
7272
--exclude compress-bench --exclude random-access-bench --exclude vortex-bench-server

.github/workflows/package.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,34 @@ jobs:
5050
run: |
5151
cargo set-version --workspace ${{ inputs.version }}
5252
53+
# cargo set-version bumps Cargo.toml crate versions but not the ==0.1.0 pins
54+
# between these two wheels in their pyproject.toml. The base and CUDA extension
55+
# wheels must require each other at the exact released version (they exchange
56+
# Vortex IPC buffers), so rewrite those pins here.
57+
- name: Set Python extension version pins
58+
env:
59+
VERSION: ${{ inputs.version }}
60+
run: |
61+
python3 - <<'PY'
62+
import os
63+
import re
64+
from pathlib import Path
65+
66+
version = os.environ["VERSION"]
67+
replacements = {
68+
Path("vortex-python/pyproject.toml"): "vortex-data-cuda",
69+
Path("vortex-python-cuda/pyproject.toml"): "vortex-data",
70+
}
71+
for path, package in replacements.items():
72+
text = path.read_text()
73+
text, count = re.subn(
74+
f"{re.escape(package)}==[^\"\\]]+", f"{package}=={version}", text
75+
)
76+
if count != 1:
77+
raise RuntimeError(f"Expected one {package} version pin in {path}, found {count}")
78+
path.write_text(text)
79+
PY
80+
5381
- name: Set up Python
5482
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
5583
# Latest macOS doesn't allow maturin to install stuff into the global Python interpreter

.github/workflows/rust-instrumented.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ jobs:
8888
- name: Rust Tests
8989
if: ${{ matrix.suite == 'tests' }}
9090
run: |
91-
cargo nextest run --locked --workspace --all-features --no-fail-fast
91+
cargo nextest run --locked --workspace --all-features --no-fail-fast --exclude vortex-python-cuda
9292
- name: Generate coverage report
9393
run: |
9494
RUST_SYSROOT="$(rustc --print sysroot)"

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ members = [
3737
"fuzz",
3838
"vortex-jni",
3939
"vortex-python",
40+
"vortex-python-cuda",
4041
"vortex-tui",
4142
"vortex-web/crate",
4243
"vortex-sqllogictest",

docs/api/python/runtime.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ otherwise to the number of available CPU cores minus one. Use
99
.. autosummary::
1010
:nosignatures:
1111

12+
~vortex.cuda_extension_installed
1213
~vortex.set_worker_threads
1314
~vortex.worker_threads
1415

1516
.. raw:: html
1617

1718
<hr>
1819

20+
.. autofunction:: vortex.cuda_extension_installed
21+
1922
.. autofunction:: vortex.set_worker_threads
2023

2124
.. autofunction:: vortex.worker_threads

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ members = ["bench-orchestrator", "vortex-python", "docs"]
3737
[tool.uv.sources]
3838
bench-orchestrator = { workspace = true }
3939
vortex-data = { workspace = true }
40+
# Resolve vortex-data-cuda from its local path so the optional `cuda` extra is
41+
# lockable before the package is published to PyPI. A path source (not
42+
# `workspace = true`) keeps the CUDA extension out of the default `--all-packages`
43+
# set, so it is only built when the `cuda` extra is requested.
44+
vortex-data-cuda = { path = "vortex-python-cuda" }
4045
docs = { workspace = true }
4146

4247
[tool.ruff]

0 commit comments

Comments
 (0)