Repository navigation
ci: fuse duplicate PR gates and reduce repeated setup #975
Description
Summary
Ready-for-review pull requests currently fan out into 15 GitHub Actions jobs. Most compilation jobs start on fresh hosted runners, repeat checkout and dependency setup, and discard their local build outputs when the job ends.
Recent ordinary PR CI runs usually complete in about 6–7 minutes of wall time, with an observed 11-minute outlier. sccache is already enabled and working, so this issue is about reducing duplicated job work and runner isolation.
The release workflow is explicitly out of scope.
Measured baseline
Recent non-release PR runs:
Representative steps from the typical run:
| Job | Total | Checkout | CUDA setup | Cargo gate |
|---|---|---|---|---|
| Qwen3 CUDA compile | 5m20s | 33s | 45s | 3m42s |
| Gemma 4 CUDA Clippy | 6m17s | 65s | 88s | 3m20s |
| Qwen3.5 CUDA compile | 4m42s | 31s | 38s | 3m12s |
sccache was active in that run:
- Qwen3 CUDA compile: 79.7% hit rate
- Qwen3 CUDA Clippy: 84.8%
- Gemma 4 CUDA Clippy: 92.6%
- Qwen3.5 CUDA Clippy: 98.8%
The slow run also showed that cache hits alone do not bound wall time:
- Qwen3.5 CUDA compile: 98.7% hits, but the Cargo step took 8m21s
- Simulated frontend E2E: 100% hits (511/511), but the job took 9m44s
The typical run also reported cache write errors in several parallel jobs, including 94 in Qwen3 compile and 76 in Qwen3.5 compile. The logs do not expose the backend error detail, so the cause should be verified before changing cache behavior.
Current duplication
- The Gemma 4/Qwen3.5 matrix runs both
cargo checkandcargo clippywith the same package, feature, release profile, and--all-targetssurface. - Qwen3 has separate CUDA compile and Clippy jobs with largely overlapping package and target coverage.
- CPU Clippy, four CPU unit-test jobs, and simulated frontend E2E each repeat Rust/sccache setup; most also repeat protobuf installation and dependency materialization.
- Every CUDA job repeats recursive submodule checkout and CUDA toolkit installation.
pegainfer-kernels/build.rslaunches nvcc directly, so those CUDA translation units are outside the RustRUSTC_WRAPPER=sccachepath and are rebuilt on every fresh runner.
Production invariant
A ready PR must continue to fail when any currently covered surface fails:
- DCO, attribution scripts, Rust formatting, and locked Cargo metadata
- CPU Clippy
- CPU unit tests for
pegainfer-build,pegainfer-frontend,pegainfer-sim, andkvbm-logical - simulated frontend E2E
- Qwen3, Qwen3.5, and Gemma 4 release-mode CUDA all-target compilation and lint coverage for sm_80
Before deleting a job, document which retained command covers every package, feature, and target previously checked by that job.
Proposed work
- Remove the feature-model
compilematrix entries where the retained Clippy invocation demonstrably covers the identical package/feature/--all-targetssurface. - Map the Qwen3 compile and Clippy target surfaces. Remove the compile job only if the retained Clippy command fully subsumes it; otherwise consolidate both commands into one runner.
- Evaluate one fused CPU gate that installs dependencies once and runs CPU Clippy, unit tests, and simulated frontend E2E against the same workspace.
- Keep model lines in separate CUDA jobs so feature-specific build requirements remain isolated.
- Avoid adding composite-action wrappers solely to reduce YAML duplication; they do not share runner state.
Acceptance criteria
- Every current gate is mapped to a retained command, with no package/feature/target coverage loss.
- Report before/after:
- workflow wall time
- summed job runtime
- per-job setup and Cargo-step duration
- sccache hit/miss/write-error statistics
- Use multiple completed ordinary PR runs and retain links to the raw Actions results.
- Do not claim a CI performance improvement until the before/after data is measured.
- If CPU fusion reduces summed runner time but increases wall time, report both results and make the tradeoff explicit.
- Release CI remains unchanged.
Follow-ups outside this issue
- Route direct nvcc compilation through a supported cache launcher.
- Evaluate a prebuilt CI image with CUDA, protobuf, libibverbs, and Triton.
- Investigate the GitHub Actions cache write errors.
- Evaluate persistent or self-hosted runners separately.
Activity
luicarus commentedon Sep 15, 2026
I'd like to take this one.
I read through
ci.ymland mapped the current gates before making changes. The proposed scope in the issue looks good to me, and I'll keep the first PR within it.A couple of implementation details I noticed while mapping the workflow:
feature-model-cudaruns thecompileandclippylegs over the same package / feature / release /--all-targetssurface. I'll verify that the retained Clippy command gives equivalent compile coverage before removing the twocompilelegs.qwen3-cuda-compile. One difference is that the compile job setsCUDA_PATH,PEGAINFER_CUDA_SM, andPEGAINFER_NVCC_JOBSat job scope, while the Clippy job sets them only on the Cargo step; I'll preserve the relevant environment if the jobs are consolidated.Before deleting any job, I'll include a package / feature / target coverage map showing which retained command replaces it. I'll also report the before/after measurements required by the acceptance criteria across multiple completed PR runs, rather than assuming consolidation improves wall time.
One thing I want to reconcile first: the issue mentions 15 ready-for-review jobs, while my initial count from the current workflow is 16 executions (8 single-instance + 4
cpu-unit-tests+ 4feature-model-cuda). I'll verify whether one is conditional / outside the ordinary PR path or whether the workflow has changed since the baseline was recorded.I'll leave the cache-write-error investigation and release workflow untouched, as they are explicitly out of scope here.
If this scope sounds right, I'll proceed.
chore(ci): let rust-toolchain.toml drive the toolchain install
chore(ci): one CUDA lane row per model line
chore(ci): drop compile gates already covered by clippy (#1048)
feat(build): route nvcc through an optional launcher
ci: put sccache in front of nvcc on the CUDA lanes
feat(build): route nvcc through an optional launcher (#1066)
github-actions commentedon Sep 29, 2026
This issue has been inactive for 14 days. It will be closed after another 30 days unless there is new activity.
FeathBow commentedon Oct 11, 2026
Hi @luicarus, #1048 and #1066 covered the compile gates and the nvcc launcher, thanks. Unless you are already working on it, we will take the fused CPU gate next, with the before and after measurements the issue asks for.