ci: CUDA build improvements - #17
Merged
Merged
Conversation
Two changes, both measured against run 30332616539. Cache the windows-latest-cmake build. Its cuda12 leg compiles nine CUDA architectures in a single job and had no ccache step at all, making it the second-longest job in the run at 90.6 min of Build. windows-latest-cuda runs the same MSVC/Ninja/Release setup with ccache and averages 1.6 min per leg at 97.96% hit rate. No cmake flags are needed: ggml's src/CMakeLists.txt sets RULE_LAUNCH_COMPILE when it finds ccache on PATH (GGML_CCACHE defaults to ON), which is how windows-latest-cuda already gets its hits. The cuda12 leg gets max-size 2G because nine architectures plus GGML_CPU_ALL_VARIANTS will not fit the 500M default that suits the cpu and vulkan legs. Parallelize xz when packing the Linux CUDA artifacts. tar -cJf drives a single-threaded xz and was the largest cost in those jobs, 50.9 min across the eight x64 legs plus 5.9 min on arm64, against only 12.8 min of Build. Measured on a 2.9 GB binary payload: 12m41s single-threaded versus 47.7s on 32 cores, with the archive 1.8% larger because threaded mode compresses independent blocks. The runners are 4-core, so expect roughly 3-4x there. ccache itself was already healthy everywhere it was configured, at 98-100% hit rates, and the release job's artifact download is fine on a full ubuntu-latest VM. Neither needed changing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both Linux CUDA jobs installed the cuda-toolkit-12-9 metapackage, which apt reports as 3935 MB of archives per job. That is 16 jobs per nightly run on the x64 matrix plus the arm64 leg, and it made "Install CUDA Toolkit" the second-largest cost in those jobs at 36.5 min across the x64 legs. Most of it is never used. The metapackage pulls Nsight Systems and Compute, cuFFT, cuSPARSE, cuSOLVER and NPP; ggml-cuda links only CUDA::cudart, CUDA::cublas and CUDA::cuda_driver plus CCCL for thrust/cub, and stable-diffusion.cpp links only ggml. Install that subset instead, ~595 MB of direct archives, and add a guard that names the missing file if NVIDIA ever repackages one of them rather than failing partway through the compile. Package names verified present in both developer.download.nvidia.com repos the jobs use, ubuntu2204/x86_64 and ubuntu2404/sbsa. The Windows CUDA installs already select sub-packages through Jimver/cuda-toolkit and were left alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kenvandine
commented
Jul 29, 2026
kenvandine
left a comment
Member
Author
There was a problem hiding this comment.
Summary of Changes
-
ccache on
windows-latest-cmake- Added
ggml-org/ccache-action@v1.2.16to thewindows-latest-cmakejob prior to the build step. - Sets
max-size: 2Gfor thecuda12matrix leg (compiling 9 CUDA architectures + CPU variants) and500Mfor CPU/Vulkan legs.
- Added
-
Minimal CUDA Toolkit APT Dependencies (Linux x64 & ARM64)
- Replaced full
cuda-toolkit-12-9metapackage (~3.9 GB) with specific required components:cuda-nvcc-12-9,cuda-cudart-dev-12-9,cuda-cccl-12-9,libcublas-dev-12-9,libcurand-dev-12-9,libnvjitlink-dev-12-9(~1.2 GB). - Added an explicit post-install verification loop checking presence of required binaries (
nvcc,libcudart,libcublas,libcublasLt,libcurand,libnvJitLink) and headers (cublas_v2.h,cub.cuh).
- Replaced full
-
Parallelized
xzCompression- Replaced
tar -cJfwithtar -I 'xz -T0' -cfin bothubuntu-latest-cudaandubuntu-arm64-cudaartifact packaging steps to utilize all available CPU cores.
- Replaced
Key Findings & Verification
- Correctness & Safety: The minimal package selection matches all headers and shared libraries required by
ggml-cudaandstable-diffusion.cppCMake target linking and bundling scripts. The added assertion loop ensures early failure with explicit error messages should NVIDIA package structures change in future toolkit releases. - Performance: Substantially reduces APT archive download/install volume (~2.7 GB saved per Linux CUDA run) and drastically speeds up
tarpackaging via multi-threadedxz. - Workflow Syntax: All GitHub Actions expressions, shell constructs, and flag usages (
tar -I 'xz -T0') are valid and standard across Ubuntu GitHub runner environments.
superm1
approved these changes
Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three changes, all measured against run 30332616539.
1. Cache the
windows-latest-cmakebuildIts
cuda12leg compiles nine CUDA architectures (61;70;75;80;86;89;90;100;120) in a single job and had no ccache step at all. It was the second-longest job in the run, behind only ROCm:For comparison,
windows-latest-cudaruns the same MSVC / Ninja / Release setup with ccache and averages 1.6 min of build per leg at 97.96% hit rate.No cmake flags are needed. ggml's
src/CMakeLists.txt:68setsRULE_LAUNCH_COMPILEas soon as it finds ccache onPATH(GGML_CCACHEdefaults toON), which is exactly howwindows-latest-cudagets its hits today — it doesn't pass-DCMAKE_*_COMPILER_LAUNCHEReither:The
cuda12leg getsmax-size: 2G; the 500M default that suitscpuandvulkanwould thrash, since a single-architecture CUDA cache already measures 0.1 GB and this builds nine plusGGML_CPU_ALL_VARIANTS=ON.2. Install only the CUDA components the Linux builds use
Both Linux CUDA jobs installed the
cuda-toolkit-12-9metapackage — apt reports 3935 MB of archives per job, on 8 x64 legs plus arm64 every night. That madeInstall CUDA Toolkitthe second-largest cost in those jobs:Most of it is never used. The metapackage pulls Nsight Systems/Compute, cuFFT, cuSPARSE, cuSOLVER and NPP. Checked against source rather than assumed —
ggml-cuda/CMakeLists.txtlinks only:and stable-diffusion.cpp links only
ggml(target_link_libraries(${SD_LIB} PUBLIC ggml)). No reference to cuFFT/cuSPARSE/cuSOLVER/NPP/NVTX in either tree.So:
cuda-nvcc,cuda-cudart-dev(also supplies thelibcudastub behindCUDA::cuda_driver),cuda-ccclfor thrust/cub,libcublas-dev, and thelibcurand-dev/libnvjitlink-devlibs the Bundle step copies — ~595 MB of direct archives. All six verified present in both repos the jobs use:Added a guard that names the missing file if NVIDIA ever repackages one of these, rather than failing partway through the compile.
The Windows CUDA installs already select sub-packages via
Jimver/cuda-toolkitand were left alone.3. Parallelize xz when packing the Linux CUDA artifacts
tar -cJfdrives a single-threaded xz — 50.9 min across the eight x64 legs plus 5.9 min on arm64, four times the compile time. Measured on a 2.9 GB binary payload:xz -T1(runner default)xz -T0(32 cores)Round-trip verified —
xz -tpasses,diff -ron the extracted tree is clean. Two caveats: the archive is 1.8% larger, because threaded mode compresses independent blocks; and the runners are 4-core, not 32, so expect roughly 3–4× rather than 16×.Checked and deliberately not changed
ubuntu-latestVM it completes in 1.4 min. (The equivalent step in ci: unblock the release job's artifact download, parallelize xz, and slim the CUDA install llama.cpp#25 was stalling only because that job ran onubuntu-slim, a container runner without the disk for it.)evict-old-files: 1d. Looks risky against a nightly cron, but isn't. Scheduled runs actually start between 05:42 and 06:30 despite0 3 * * *, so gaps regularly exceed 24h — 24h23m and 24h24m in the last week — and hit rates on those runs were 99.48% and 100%.ccache --evict-older-thanruns in the post step and ccache refreshes mtimes on hits, so the saved snapshot always contains what the run just used.Free disk space(8.6 min/run) is now probably unnecessary given the much smaller toolkit, but removing a safety step without measuring the new headroom seemed like the wrong trade. Worth revisiting once this has run a few nights.Known follow-ups, not in this PR
cuda12cache increases that pressure. It's survivable — the measured 98–100% hit rates happened at the limit, because LRU keeps the nightly-refreshed entries hot — but worth watching. The cheapest headroom is elsewhere: a 1.34 GiBrocm-wheelsblob and five retained generations ofccache-ubuntu-rocm-cmake-7.14.0-x64(317 + 242 + 241 + 209 + 203 MiB ≈ 1.2 GiB of near-duplicates). I did not add a cache-pruning step — deleting caches out from under concurrent jobs is easy to get wrong and there's no evidence yet that it's needed.ubuntu-latest-rocmis the real long pole at 167.8 min, and nothing here touches it.ubuntu-latest-cmake,ubuntu-latest-cmake-vulkanand thecpu/vulkanlegs also lack ccache, but at 5–17 min each they weren't worth the extra cache entries right now. (Thecpuandvulkanlegs do pick it up here, since the step is on the sharedwindows-latest-cmakejob.)Testing
Workflow YAML parses; both rewritten
Install CUDA Toolkitscripts passbash -n; thewindows-latest-cmakestep order is correct (ccache lands beforeBuild, so it's onPATHat configure time);max-sizeverified as a real input onggml-org/ccache-action@v1.2.16. All three changes exercise on PR runs, though the first run will be a cold cache — the ccache win shows on the second.🤖 Generated with Claude Code