Skip to content

Speed up CI JIT compiles: Triton cache and arch-limited CUDA wheels - #2905

Draft
shengliangxu wants to merge 4 commits into
mainfrom
shengliangx/ci-shared-cache
Draft

shengliangxu wants to merge 4 commits into
mainfrom
shengliangx/ci-shared-cache

Conversation

@shengliangxu

@shengliangxu shengliangxu commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

Type of change: CI

Three small changes that cut compile time in the GPU and example jobs. Opened as a draft to measure them in CI. The first set of CI numbers below was measured on an earlier head of this branch (c038edc, cold run then a re-run of the same sha); the current head (7403c55) adds the week-key fix and the autotune cache, validated in the last bullet.

Cache Triton kernels across runs. Every GPU and example container starts with an empty Triton cache, so each job recompiles the same kernels: the GDN fixture (about 30 compiles), the Mamba first calls, the FLA fixture, the NVFP4 FP8-sweep kernel and the first vLLM engine. #2836 added the extension cache but nothing for Triton; this is the Triton half of 7f8854a on top of its cache-extensions / save-extensions actions.

  • cache-extensions exports TRITON_CACHE_DIR=/root/.cache/triton and restores a Triton cache keyed triton-<job key>-<ISO week>-<hash of modelopt/torch/kernels/**/*.py> with restore-keys on the job key, so a run starts from the newest cache of its lane. Triton entries are content-addressed and only ever added, so an older cache is a safe base.
  • save-extensions saves it only when the exact key was missing and the directory is not empty, like the extension cache: once a week and when the kernels change, not on every run. An earlier version saved whenever a run compiled 20 or more new entries; the warm run showed that the two lanes using torch.compile (diffusers, speculative_decoding) compile about 25 run-specific entries every time and re-saved a 24-58 MB copy on every run. The extension cache keeps its exact key; its hit check moves from the workflows' if into the action.
  • TRITON_CACHE_AUTOTUNING=1 makes Triton store the chosen autotune config next to the compiled kernel, in the same directory, so a warm job skips the benchmark too (the GDN fixtures still paid for it).
  • Cost: 6-52 MB per job. After merging, dispatch GPU tests and Example tests on main once so the entries land in main's cache scope. PR and merge-queue runs read it from there (a PR scope cannot be read by other PRs, and a merge group only sees main's).

Build the source-built CUDA wheels for the CI GPU's architecture only. mamba_ssm, causal-conv1d and fast-hadamard-transform hardcode one -gencode flag per architecture of the CUDA toolkit (nine with CUDA 13: sm_75/80/87/90/100/103/110/120/121) and ignore TORCH_CUDA_ARCH_LIST. A wheelhouse miss in the gpu job (the first run of every PR whose noxfile.py hash is not in main's cache scope, and the nightly after a noxfile.py change) therefore compiles eight architectures the RTX PRO 6000 runner never uses.

The step that already patches their setup.py to C++20 now also drops the -gencode entries that are not in TORCH_CUDA_ARCH_LIST (the GPU jobs set it to the runner's GPU) and raises if none matches. Without the variable nothing changes. fast-hadamard-transform is built from a git URL with no patch step and still builds every architecture. The wheels are now per architecture: the wheelhouse key (image + noxfile.py hash) must carry the GPU type before another GPU type runs the gpu job.

Testing

  • actionlint 1.7.12 on all workflows (a misspelled input name is reported, so the wiring of the action inputs and outputs is checked); the new shell steps run under bash -e -o pipefail with a missing, an empty and a populated cache directory; _only_runner_archs on the real setup.py of the three pinned packages for TORCH_CUDA_ARCH_LIST = 12.0, 9.0, "8.0;9.0+PTX" and 12.0a (only the requested -gencode pairs remain and the result compiles; unset leaves the file untouched; an architecture without an entry raises).
  • CI on c038edc (all 17 GPU and example lanes green in both rounds):
    • Arch-limited build, gpu job vs the wheelhouse-miss run of PR 2610 on the same image: the three wheel builds took 7.7 min instead of 15.4 (mamba_ssm 283 -> 74 s, causal-conv1d 436 -> 179 s, fast-hadamard unchanged at 138 s); all 1827 gpu tests pass; the wheelhouse cache entry went from 615 MB to 121 MB.
    • Triton cache, cold -> warm per test: megatron_bridge example lane 13.3 -> 7.6 min (test_distill_vlm 79 -> 20 s, test_prune_minitron_vlm 74 -> 33 s, test_qad 59 -> 29 s); speculative_decoding 10.7 -> 7.8 min (the torch.compile smoke test 188 -> 96 s); gpu: the NVFP4 sweep export test 60 -> 22 s and the GDN fixture setup 65 -> 38 s; gpu_megatron about -1 min (GDN setup 174 -> 130 s, the 75 s worker-pool starts are unchanged); gpu_vllm about -1 min (the first engine setup, 93 -> 102 s, is not helped: vLLM's own compile cache is not cached); diffusers -0.8 min. Lane totals are noisy (llm_qat moved -11.5 min with no cache change), so judge the per-test numbers.
  • CI on the current head 7403c55 (GPU, Example and Regression green): the first run restores the old caches through restore-keys and saves one new exact-key Triton cache per lane (211 MB in total; 25-70 MB for the lanes that use Triton, 0.1-0.6 MB for the lanes that barely do). A re-run of speculative_decoding and of gpu_megatron restores the exact key (hit for) and saves nothing, where the previous rule re-saved 24-58 MB on every run in the torch.compile lanes. The autotune cache: test_gdn_qat_and_sharded_restore setup in gpu_megatron went from 76 s to 54 s on the re-run, with the compiled kernels already restored in both runs (the torch.compile smoke test in speculative_decoding is not helped, 89 -> 92 s).

Before your PR is "Ready for review"

Make sure you read and follow Contributor guidelines and your commits are signed (git commit -s -S).

Make sure you read and follow the Security Best Practices (e.g. avoiding hardcoded trust_remote_code=True, torch.load(..., weights_only=False), pickle, etc.).

  • Is this change backward compatible?: N/A (CI only)
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: N/A
  • Did you write any new necessary tests?: N/A (workflow change and a CI build helper, checked on the real setup.py files above)
  • Did you update Changelog?: N/A
  • Did you get Claude approval on this PR?: N/A (optional, not run)

Additional Information

Related: #2836 (wheelhouse and extension cache), #2730 (reference measurements). Merge-queue runs only read main's cache scope; they should skip cache saves. For scale: in a survey of the GPU lanes after #2846, first-use JIT was about 160 s of a PR gpu job (GDN Triton compile 70 s, NVFP4 sweep kernels about 47 s), the first vLLM engine boot 104 s against 24-39 s steady state, and the 4 torch.compile tests of diffusers 153 of its 209 s of pytest.

@copy-pr-bot

copy-pr-bot Bot commented Oct 21, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@shengliangxu

Copy link
Copy Markdown
Collaborator Author

/ok to test c038edc

@codecov

codecov Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.91%. Comparing base (5fdd067) to head (7403c55).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2905      +/-   ##
==========================================
+ Coverage   70.16%   78.91%   +8.75%     
==========================================
  Files         659      659              
  Lines       74077    74077              
==========================================
+ Hits        51974    58458    +6484     
+ Misses      22103    15619    -6484     
Flag Coverage Δ
examples-diffusers 20.06% <ø> (-0.02%) ⬇️
examples-gpt-oss 13.53% <ø> (-0.01%) ⬇️
examples-hf_ptq 23.43% <ø> (+0.18%) ⬆️
examples-llm_distill 13.61% <ø> (-0.01%) ⬇️
examples-llm_eval 17.26% <ø> (-0.01%) ⬇️
examples-llm_qat 17.50% <ø> (-0.02%) ⬇️
examples-llm_sparsity 16.86% <ø> (+1.07%) ⬆️
examples-megatron_bridge 26.45% <ø> (-0.15%) ⬇️
examples-specdec_bench 13.31% <ø> (-0.01%) ⬇️
examples-speculative_decoding 17.49% <ø> (-0.01%) ⬇️
examples-torch_onnx 21.40% <ø> (-0.04%) ⬇️
examples-torch_trt 15.26% <ø> (-0.01%) ⬇️
examples-vllm_serve 17.49% <ø> (+2.37%) ⬆️
gpu 59.11% <ø> (+36.94%) ⬆️
regression 15.12% <ø> (-0.01%) ⬇️
unit 60.59% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Every GPU and example container starts with an empty Triton cache, so each
job recompiles the same kernels (GDN, Mamba, FLA, the NVFP4 sweep, vLLM engine
start-up).

cache-extensions now also restores the newest Triton cache of the lane. Triton
entries are content-addressed and only ever added, so the restore uses
restore-keys on a run-unique key rather than an exact key. save-extensions saves
a new Triton cache only when a run compiled at least 20 entries more than it
restored, so a warm run saves nothing and a cache cannot be frozen incomplete by
a failed run. The extension cache keeps its exact key; its hit check moves from
the workflows' `if` into the action.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
mamba_ssm and causal-conv1d hardcode a -gencode flag for every architecture of
the CUDA toolkit (nine with CUDA 13) and ignore TORCH_CUDA_ARCH_LIST, so a
wheelhouse miss compiled eight architectures the CI runner never runs. The step
that already patches their setup.py to C++20 now also drops the -gencode entries
that are not in TORCH_CUDA_ARCH_LIST (the GPU jobs set it to the runner's GPU)
and fails if none matches. Without the variable nothing changes.

fast-hadamard-transform is built from a git URL, with no patch step, and still
builds every architecture.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Saving a Triton cache whenever a run compiled 20 more entries than it restored
saved a new copy on every run in the two lanes that use torch.compile
(diffusers, speculative_decoding): each run compiles about 25 new entries that
later runs do not reuse, so a warm run saved a 24-58 MB cache again.

The key now carries the ISO week and the hash of modelopt/torch/kernels, and
the save runs only when that exact key was missing and the cache is not empty,
like the extension cache. restore-keys still restores the newest cache of the
lane, so a run starts from the last one. The cache is refreshed once a week and
when the kernels change.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
TRITON_CACHE_DIR caches compiled kernels, but an autotuned kernel still
benchmarks its configs on first use in every process, which the warm CI runs
still paid for (the GDN fixtures). TRITON_CACHE_AUTOTUNING=1 writes the chosen
config next to the compiled kernel, inside the same directory, so a warm job
skips the benchmark as well.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@shengliangxu
shengliangxu force-pushed the shengliangx/ci-shared-cache branch from c038edc to 7403c55 Compare October 21, 2026 22:36
@shengliangxu

Copy link
Copy Markdown
Collaborator Author

/ok to test 7403c55

This branch has not been deployed

No deployments
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