Skip to content

Commit 61fb86c

Browse files
[ci]: prebuild L40S kernel artifact (#1562)
1 parent 6e4bfbf commit 61fb86c

4 files changed

Lines changed: 103 additions & 31 deletions

File tree

docker/Dockerfile

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -186,22 +186,40 @@ RUN --mount=type=cache,target=/opt/uv/cache \
186186

187187
COPY . .
188188

189-
# Install FastVideo Unified Kernel exactly once. build.sh initializes only its
190-
# CUTLASS/ThunderKittens submodules, then compiles for TORCH_CUDA_ARCH_LIST
191-
# (default Hopper sm_90a) without requiring a live GPU.
192-
ARG FASTVIDEO_KERNEL_WHEEL_DIR=/opt/fastvideo-kernel-wheels
193-
ARG FASTVIDEO_KERNEL_BUILD_INFO=/opt/fastvideo-kernel-build-info.json
189+
# Build immutable FastVideo kernel wheels for the published image. The requested
190+
# architecture remains installed for normal image users; amd64 images also carry
191+
# an SM89 artifact so the predominant L40S Modal lanes can reuse it exactly.
192+
ARG FASTVIDEO_KERNEL_PREBUILT_DIR=/opt/fastvideo-kernel-prebuilt
194193
RUN --mount=type=cache,target=/opt/uv/cache \
195194
source $HOME/.local/bin/env && \
196195
source /opt/venv/bin/activate && \
197-
export TORCH_CUDA_ARCH_LIST=${TORCH_CUDA_ARCH_LIST} && \
196+
default_arch="${TORCH_CUDA_ARCH_LIST}" && \
197+
default_wheel_dir="${FASTVIDEO_KERNEL_PREBUILT_DIR}/${default_arch}" && \
198+
export TORCH_CUDA_ARCH_LIST="${default_arch}" && \
198199
cd fastvideo-kernel && \
199-
CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL} \
200-
./build.sh --wheel-dir ${FASTVIDEO_KERNEL_WHEEL_DIR} && \
200+
CMAKE_ARGS= CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL} \
201+
./build.sh --wheel-dir "${default_wheel_dir}" && \
201202
cd /FastVideo && \
202-
python fastvideo/tests/modal/kernel_build_cache.py write-build-info \
203-
--wheel-dir ${FASTVIDEO_KERNEL_WHEEL_DIR} \
204-
--output ${FASTVIDEO_KERNEL_BUILD_INFO}
203+
CMAKE_ARGS= python fastvideo/tests/modal/kernel_build_cache.py write-build-info \
204+
--wheel-dir "${default_wheel_dir}" \
205+
--output "${default_wheel_dir}/metadata.json" && \
206+
if [[ "${TARGETARCH:-amd64}" == "amd64" && "${default_arch}" != "8.9" ]]; then \
207+
export TORCH_CUDA_ARCH_LIST=8.9 && \
208+
l40s_wheel_dir="${FASTVIDEO_KERNEL_PREBUILT_DIR}/8.9" && \
209+
cd /FastVideo/fastvideo-kernel && \
210+
CMAKE_ARGS= CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL} \
211+
./build.sh --wheel-dir "${l40s_wheel_dir}" && \
212+
cd /FastVideo && \
213+
CMAKE_ARGS= python fastvideo/tests/modal/kernel_build_cache.py write-build-info \
214+
--wheel-dir "${l40s_wheel_dir}" \
215+
--output "${l40s_wheel_dir}/metadata.json"; \
216+
fi && \
217+
export TORCH_CUDA_ARCH_LIST="${default_arch}" && \
218+
default_wheel="$(find "${default_wheel_dir}" -maxdepth 1 -type f \
219+
\( -name 'fastvideo_kernel-*.whl' -o -name 'fastvideo-kernel-*.whl' \) \
220+
| sort | tail -n 1)" && \
221+
uv pip install "${default_wheel}" \
222+
--reinstall-package fastvideo-kernel --no-deps
205223

206224
# Install FastVideo itself (editable) now that the source is present, and set up
207225
# shell configuration. Dependencies and the local kernel are already installed,

docs/contributing/ci_architecture.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ then publishes one multi-platform manifest per CUDA version. CUDA 12.6 owns the
281281
unparameterized `docker/Dockerfile` build defaults, which remain CUDA 13 and
282282
`cu130`.
283283

284+
Published amd64 development images keep their configured Hopper kernel wheel
285+
installed and also carry an immutable SM89 wheel under
286+
`/opt/fastvideo-kernel-prebuilt`. Modal PR and SSIM jobs select the exact
287+
source, ABI, and GPU-architecture match from that directory, so L40S jobs reuse
288+
the trusted image artifact while kernel-changing PRs still build locally.
289+
284290
The optional Dreamverse matrix builds backend and UI images for CUDA 12.6 and
285291
CUDA 13 on `amd64`. Dreamverse remains `amd64`-only because its FA4 dependency
286292
stack is not yet validated on ARM64.

fastvideo/tests/modal/kernel_build_cache.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424

2525

2626
CACHE_SCHEMA_VERSION = 3
27-
DEFAULT_PREBUILT_INFO_PATH = "/opt/fastvideo-kernel-build-info.json"
27+
DEFAULT_PREBUILT_INFO_PATH = "/opt/fastvideo-kernel-prebuilt"
2828
KERNEL_RELATIVE_DIR = "fastvideo-kernel"
29+
DEFAULT_BUILD_INFO_OUTPUT = "/opt/fastvideo-kernel-prebuilt/default/metadata.json"
2930
METADATA_FILE = "metadata.json"
3031
EXPECTED_DISTRIBUTION = "fastvideo-kernel"
3132

@@ -372,26 +373,35 @@ def _cache_entry_wheel(cache_entry: Path, cache_key: str) -> tuple[Path | None,
372373
return _validated_payload_wheel(metadata, wheel, cache_key)
373374

374375

376+
def _prebuilt_metadata_paths(prebuilt_info_path: Path) -> list[Path]:
377+
if prebuilt_info_path.is_dir() and not prebuilt_info_path.is_symlink():
378+
return sorted(prebuilt_info_path.glob(f"*/{METADATA_FILE}"))
379+
if prebuilt_info_path.exists() or prebuilt_info_path.is_symlink():
380+
return [prebuilt_info_path]
381+
return []
382+
383+
375384
def _try_install_prebuilt(metadata: dict[str, object], prebuilt_info_path: Path) -> bool:
376385
cache_key = str(metadata["cache_key"])
377-
if not prebuilt_info_path.exists():
378-
return False
379-
prebuilt, reason = _load_metadata(prebuilt_info_path)
380-
if prebuilt is None:
381-
_log(f"Docker-prebuilt kernel metadata is invalid at {prebuilt_info_path}: {reason}")
382-
return False
383-
wheel_path = Path(str(prebuilt.get("wheel_path", "")))
384-
wheel, reason = _validated_payload_wheel(prebuilt, wheel_path, cache_key)
385-
if wheel is None:
386-
_log(f"Docker-prebuilt kernel artifact rejected at {prebuilt_info_path}: {reason}")
387-
return False
388-
try:
389-
_log(f"using Docker-prebuilt kernel wheel for cache key {cache_key}")
390-
_install_wheel(wheel)
391-
except (FileNotFoundError, OSError, subprocess.CalledProcessError) as error:
392-
_log(f"Docker-prebuilt kernel installation failed: {error}; falling back")
393-
return False
394-
return True
386+
for metadata_path in _prebuilt_metadata_paths(prebuilt_info_path):
387+
prebuilt, reason = _load_metadata(metadata_path)
388+
if prebuilt is None:
389+
_log(f"Docker-prebuilt kernel metadata is invalid at {metadata_path}: {reason}")
390+
continue
391+
wheel_path = Path(str(prebuilt.get("wheel_path", "")))
392+
wheel, reason = _validated_payload_wheel(prebuilt, wheel_path, cache_key)
393+
if wheel is None:
394+
_log(f"Docker-prebuilt kernel artifact rejected at {metadata_path}: {reason}")
395+
continue
396+
try:
397+
_log(f"Docker-prebuilt cache hit for key {cache_key}: {wheel}")
398+
_install_wheel(wheel)
399+
except (FileNotFoundError, OSError, subprocess.CalledProcessError) as error:
400+
_log(f"Docker-prebuilt kernel installation failed: {error}; falling back")
401+
return False
402+
return True
403+
_log(f"Docker-prebuilt cache miss for key {cache_key}")
404+
return False
395405

396406

397407
def _store_cache_entry(cache_root: Path, metadata: dict[str, object], wheel: Path) -> Path:
@@ -535,7 +545,7 @@ def parse_args() -> argparse.Namespace:
535545
parser.add_argument("--prebuilt-info-path",
536546
default=os.environ.get("FASTVIDEO_KERNEL_PREBUILT_INFO", DEFAULT_PREBUILT_INFO_PATH))
537547
parser.add_argument("--wheel-dir", default="")
538-
parser.add_argument("--output", default=DEFAULT_PREBUILT_INFO_PATH)
548+
parser.add_argument("--output", default=DEFAULT_BUILD_INFO_OUTPUT)
539549
return parser.parse_args()
540550

541551

fastvideo/tests/modal/test_kernel_build_cache.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ def _write_cache_entry(cache_root: Path, cache_key: str, *, payload: str = "payl
6060
return cache_entry
6161

6262

63+
def _write_prebuilt_artifact(prebuilt_root: Path, name: str, cache_key: str) -> Path:
64+
artifact_dir = prebuilt_root / name
65+
artifact_dir.mkdir(parents=True)
66+
wheel = _write_wheel(artifact_dir / WHEEL_NAME)
67+
payload = {
68+
**_metadata(cache_key),
69+
"artifact": kernel_build_cache._wheel_artifact(wheel),
70+
"wheel_path": str(wheel),
71+
}
72+
(artifact_dir / kernel_build_cache.METADATA_FILE).write_text(json.dumps(payload), encoding="utf-8")
73+
return wheel
74+
75+
6376
def _patch_stable_metadata(monkeypatch) -> None:
6477
for name in (
6578
"GPU_BACKEND",
@@ -318,6 +331,31 @@ def test_consumer_falls_back_when_cached_install_fails(monkeypatch, tmp_path) ->
318331
assert built == [True]
319332

320333

334+
def test_normal_l40s_pr_ssim_install_uses_docker_prebuilt_without_build(monkeypatch, tmp_path, capsys) -> None:
335+
prebuilt_root = tmp_path / "prebuilt"
336+
_write_prebuilt_artifact(prebuilt_root, "9.0a", "hopper-key")
337+
l40s_wheel = _write_prebuilt_artifact(prebuilt_root, "8.9", "l40s-key")
338+
installed = []
339+
monkeypatch.setattr(kernel_build_cache, "_build_metadata", lambda repo_root: _metadata("l40s-key"))
340+
monkeypatch.setattr(kernel_build_cache, "_install_wheel", installed.append)
341+
monkeypatch.setattr(
342+
kernel_build_cache,
343+
"_build_and_install_local",
344+
lambda repo_root, metadata: pytest.fail("L40S prebuilt hit unexpectedly called build.sh"),
345+
)
346+
347+
kernel_build_cache.install_cached_or_build(
348+
tmp_path,
349+
cache_root=None,
350+
prebuilt_info_path=prebuilt_root,
351+
)
352+
353+
output = capsys.readouterr().out
354+
assert installed == [l40s_wheel]
355+
assert "Docker-prebuilt cache hit for key l40s-key" in output
356+
assert "build.sh" not in output
357+
358+
321359
def test_prebuilt_requires_validated_artifact(monkeypatch, tmp_path) -> None:
322360
wheel = _write_wheel(tmp_path / WHEEL_NAME)
323361
prebuilt_info = tmp_path / "prebuilt.json"

0 commit comments

Comments
 (0)