Skip to content

build: add sccache-backed jit-cache builds and AOT diagnostics - #3205

Merged
dierksen merged 25 commits into
mainfrom
jdierksen/sccache-nvcc-threads
May 8, 2026
Merged

dierksen merged 25 commits into
mainfrom
jdierksen/sccache-nvcc-threads

Conversation

@dierksen

@dierksen dierksen commented Apr 29, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR adds sccache-backed compiler caching for the FlashInfer jit-cache/AOT build paths and folds in the AOT memory diagnostics work from #3204.

The main pieces are:

  • Add compiler launcher support in the JIT build generation so nvcc and host compiler invocations can be wrapped by sccache or another launcher.
  • Install and configure sccache in the release/nightly jit-cache wheel builds and the PR AOT build/import jobs.
  • Use the shared S3 cache in read-write mode when AWS credentials are available, and fall back to anonymous read-only mode for PR jobs without credentials.
  • Print sccache --show-stats at the end of the relevant jobs so cache hit rates and compile behavior are visible in CI logs.
  • Keep FLASHINFER_NVCC_THREADS supported, but default it back to 1 after CI showed higher nvcc internal threading was slower for this workload.
  • Increase build-level parallelism through MAX_JOBS, bounded by CPU count and an AOT memory budget so the larger CUDA builds do not overrun the runner.
  • Add AOT memory monitoring/report files around install, wheel build, import/config, and module verification steps to diagnose OOMs and runner shutdowns.
  • Clean stale jit-cache build artifacts before building and pick the newest wheel from dist/.
  • Add tests for the nvcc flag generation, launcher-compatible depfile/debug flags, and build regeneration behavior.

Context

This PR supersedes #3204. The memory monitor and AOT MAX_JOBS safety work from #3204 are included here, but the final script shape is different because it is integrated with the sccache setup and the later threading/concurrency experiments. Merging #3204 separately would still create a conflict in scripts/task_test_jit_cache_package_build_import.sh.

The current direction is to use sccache for the big rebuild/retry win while keeping nvcc internal threading conservative. The PR still allows FLASHINFER_NVCC_THREADS to be overridden for experiments, but the default path lets ninja drive parallelism through MAX_JOBS.

CI Notes

The latest PR Test run completed successfully after the infrastructure-triggered AOT rerun path. The Release workflow's jit-cache jobs completed and showed useful sccache hit rates; the remaining Release failure was in build-flashinfer-cubin while downloading cubins, which appears separate from the sccache/AOT build changes.

Related

Supersedes #3204.

Performance comparison

This looks at the mean and median time to complete each AOT build type over the last 48 hours compared with the most recent 2 builds on this branch.

job 48h mean 48h median #4214 #4214 vs median #4284 #4284 vs median
x64 cu126 1:09:34 51:55 42:28 18.2% faster 31:36 39.1% faster
x64 cu128 1:45:24 1:24:04 1:09:46 17.0% faster 45:04 46.4% faster
x64 cu129 2:36:17 2:25:29 2:12:39 8.8% faster 59:50 58.9% faster
x64 cu130 2:27:16 2:04:49 1:48:57 12.7% faster 59:34 52.3% faster
arm64 cu126 1:23:52 1:30:43 1:13:56 18.5% faster 50:43 44.1% faster
arm64 cu128 2:12:08 2:25:56 2:04:47 14.5% faster 1:12:37 50.2% faster
arm64 cu129 3:45:38 4:05:35 3:38:02 11.2% faster 1:31:15 62.8% faster
arm64 cu130 3:34:21 3:53:18 3:21:49 13.5% faster 1:34:48 59.4% faster

dierksen and others added 9 commits April 3, 2026 16:43
…builds

Add compiler launcher support (FLASHINFER_NVCC_LAUNCHER, FLASHINFER_CXX_LAUNCHER)
to ninja build rules, enabling sccache/ccache wrapping of nvcc and cxx invocations.
When unset, behavior is unchanged.

Coordinate FLASHINFER_NVCC_THREADS with MAX_JOBS so nvcc parallelizes across
gencode targets internally (--threads=N) instead of compiling them sequentially.
This better utilizes available cores, especially for builds targeting many SM
architectures (e.g., CUDA 12.9 with 7 gencode targets).

Pass sccache S3 credentials from GitHub secrets into the Docker build containers
for nightly and release workflows. sccache is conditionally installed at build
time only when SCCACHE_BUCKET is set; without it, builds behave identically
to before.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apply the same coordinated NVCC_THREADS/MAX_JOBS parallelism and optional
sccache integration to task_test_jit_cache_package_build_import.sh, which
also builds jit-cache wheels via python -m build --wheel.

Moved the parallelism calculation after CUDA arch detection so the thread
count is based on the actual number of gencode targets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass S3 sccache credentials through ci/bash.sh into the Docker container
for both the spot and on-demand rerun variants of the aot-build-import job.

Secrets are not exposed to fork PRs (GitHub Actions default behavior), so
this is safe for an open source project.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of requiring AWS credentials (which aren't available to fork PRs),
the AOT build import test now always sets up sccache with the public cache
bucket using anonymous read-only access (SCCACHE_S3_NO_CREDENTIALS=true).

When AWS credentials ARE available (nightly/release builds), sccache
operates in read-write mode to populate the cache.

This means:
- Fork PRs: read-only cache hits, no credentials needed
- Nightly/release: read-write, populates the cache for everyone
- No secrets passed to pr-test.yml at all

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EC2 runners are in us-west-2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SCCACHE_BUCKET and SCCACHE_REGION are not sensitive — use repository
variables (vars.*) instead of secrets so they propagate to fork PR
workflows. Only the AWS credentials remain as secrets.

Also rename default bucket to flashinfer-build-cache.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds jobserver-aware nvcc parallelism and sccache-backed remote compilation caching: exports/control of FLASHINFER_NVCC_THREADS, forwards SCCache/S3/AWS env into CI containers, installs/configures sccache when enabled, recomputes MAX_JOBS via memory budgeting, updates Ninja generation and tests accordingly.

Changes

Cohort / File(s) Summary
GitHub Workflows
.github/workflows/nightly-release.yml, .github/workflows/release.yml, .github/workflows/pr-test.yml
Pass FLASHINFER_NVCC_THREADS, SCCACHE_BUCKET, SCCACHE_REGION, and AWS credential envs into build/test Docker runs.
JIT C++/CUDA build integration
flashinfer/jit/cpp_ext.py
Add nvcc-thread parsing/validation, Make/Ninja jobserver detection, helpers for jobserver/launcher usage, emit optional cxx_launcher/nvcc_launcher in generated Ninja, and avoid -j when inheriting jobserver.
JIT core build orchestration
flashinfer/jit/core.py
Always regenerate per-op build.ninja under FileLock and switch CUDA flags to use get_nvcc_parallelism_flags() (including --device-debug when applicable).
Build & test scripts
scripts/build_flashinfer_jit_cache_whl.sh, scripts/task_test_jit_cache_package_build_import.sh
Introduce FLASHINFER_NVCC_THREADS, compute/cap it, recompute MAX_JOBS via a per-job memory budget (bounded by CPUs), install/configure/start sccache for S3 backend when set, export launchers and SCCACHE envs, and print sccache stats.
Tests
tests/test_jit_cpp_ext.py
New tests validating nvcc parallelism flags, jobserver conditions, Ninja generation details, run_ninja() -j behavior, and that JitSpec.build() writes the Ninja file before building.

Sequence Diagram(s)

sequenceDiagram
    actor GHA as GitHub Actions
    participant Docker as Docker Container
    participant Script as Build Script
    participant Sccache as sccache Server
    participant S3 as S3 Storage
    participant Compiler as nvcc/C++ Compiler

    GHA->>Docker: Run container, pass env (FLASHINFER_NVCC_THREADS, SCCACHE_BUCKET, SCCACHE_REGION, AWS_*, ...)
    Docker->>Script: Execute build_flashinfer_jit_cache_whl.sh
    alt SCCACHE_BUCKET set
        Script->>Sccache: Install & start sccache
        Script->>Sccache: Configure S3 backend (bucket, region, prefix)
        Script->>Script: Export FLASHINFER_NVCC_LAUNCHER / FLASHINFER_CXX_LAUNCHER = sccache
    end
    Script->>Compiler: Invoke compiler (via launcher if set / inherit jobserver if available)
    Compiler->>Sccache: Request compile (when launcher used)
    Sccache->>S3: Fetch/store cache objects
    S3-->>Sccache: Cache hit/miss
    Sccache-->>Compiler: Return cached artifact or compiled output
    Script->>Sccache: Print sccache stats
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

run-ci

Suggested reviewers

  • sricketts
  • aleozlx
  • yongwww
  • yzh119
  • bkryu

Poem

🐰 I count the threads and watch them play,
sccache hides the work and saves the day.
Ninja whispers tokens, jobservers hum,
compiles hop faster — caches make them run.
a rabbit cheers — your builds are done!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main changes: adding sccache-backed compiler caching for JIT builds and AOT memory diagnostics, matching the detailed changeset across workflows, build scripts, and the JIT compilation pipeline.
Description check ✅ Passed The PR description is comprehensive and detailed, covering objectives, context, implementation details, and performance comparisons. It explains the main pieces, provides related context, and includes CI notes and performance data.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jdierksen/sccache-nvcc-threads

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request integrates compiler launchers like sccache into the JIT build system and optimizes build parallelism by coordinating Ninja jobs with internal nvcc threads. Feedback highlights the need to explicitly pass the nvcc_threads variable to Ninja to enable parallel compilation across multiple architectures. Additionally, it is recommended to increase the memory budget per job to mitigate OOM risks and to unify the sccache configuration logic across scripts for better consistency.

Comment thread flashinfer/jit/cpp_ext.py
Comment thread flashinfer/jit/cpp_ext.py Outdated
Comment thread scripts/build_flashinfer_jit_cache_whl.sh Outdated
Comment thread scripts/build_flashinfer_jit_cache_whl.sh Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
scripts/build_flashinfer_jit_cache_whl.sh (1)

16-32: Clamp NVCC_THREADS to nproc before deriving MAX_JOBS.

If a runner has fewer cores than gencode targets, this still oversubscribes a single nvcc process. For example, 4 cores and 7 arch targets end up at MAX_JOBS=1 and NVCC_THREADS=7, so the “Cap total threads at available CPUs” comment is not actually enforced.

Suggested fix
 NUM_ARCHS=$(echo "${FLASHINFER_CUDA_ARCH_LIST}" | wc -w)
 NVCC_THREADS=${FLASHINFER_NVCC_THREADS:-${NUM_ARCHS}}
+if (( NVCC_THREADS > NPROC )); then NVCC_THREADS=${NPROC}; fi
 if (( NVCC_THREADS > 8 )); then NVCC_THREADS=8; fi
 if (( NVCC_THREADS < 1 )); then NVCC_THREADS=1; fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build_flashinfer_jit_cache_whl.sh` around lines 16 - 32, The
NVCC_THREADS value must be clamped to available CPU count before computing
MAX_JOBS so a single nvcc process can't be oversubscribed; after you compute and
bound NVCC_THREADS (the existing logic around FLASHINFER_NVCC_THREADS and the
1..8 clamp), add a clamp against NPROC (if NVCC_THREADS > NPROC then set
NVCC_THREADS=NPROC, ensuring it's still >=1) and only then compute MEM_PER_JOB,
MAX_JOBS and TOTAL_THREADS (references: NVCC_THREADS, NPROC, MEM_PER_JOB,
MAX_JOBS, TOTAL_THREADS, MEM_AVAILABLE_GB).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/build_flashinfer_jit_cache_whl.sh`:
- Around line 70-76: The current sccache installation pipes the remote tarball
directly into tar and installs to /usr/local/bin without verifying integrity;
change the flow in scripts/build_flashinfer_jit_cache_whl.sh (and replicate the
same change in scripts/task_test_jit_cache_package_build_import.sh) to download
the tarball to a temporary file (use SCCACHE_VERSION and SCCACHE_ARCH to
construct the URL), also download or embed a pinned checksum for that exact
release, verify the tarball with sha256sum (or another trusted verifier) before
extracting, only extract after the checksum matches, and then move the sccache
binary to /usr/local/bin and clean up; ensure you fail the script with a clear
error if the checksum verification fails.
- Around line 68-89: When SCCACHE_BUCKET is set but AWS credentials are not
available in forked PRs, export SCCACHE_S3_NO_CREDENTIALS=true before
configuring and starting sccache so the runner uses the public read-only S3
cache; update the SCCACHE block (the section that exports SCCACHE_S3_KEY_PREFIX,
SCCACHE_IDLE_TIMEOUT, FLASHINFER_NVCC_LAUNCHER, FLASHINFER_CXX_LAUNCHER and
calls sccache --start-server) to detect missing credentials (e.g.,
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN unset) and in that
case export SCCACHE_S3_NO_CREDENTIALS=true and echo that state so sccache will
operate credentialless for public cache access.

---

Nitpick comments:
In `@scripts/build_flashinfer_jit_cache_whl.sh`:
- Around line 16-32: The NVCC_THREADS value must be clamped to available CPU
count before computing MAX_JOBS so a single nvcc process can't be
oversubscribed; after you compute and bound NVCC_THREADS (the existing logic
around FLASHINFER_NVCC_THREADS and the 1..8 clamp), add a clamp against NPROC
(if NVCC_THREADS > NPROC then set NVCC_THREADS=NPROC, ensuring it's still >=1)
and only then compute MEM_PER_JOB, MAX_JOBS and TOTAL_THREADS (references:
NVCC_THREADS, NPROC, MEM_PER_JOB, MAX_JOBS, TOTAL_THREADS, MEM_AVAILABLE_GB).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7290836a-da32-42fe-aecf-231956edcaaf

📥 Commits

Reviewing files that changed from the base of the PR and between ed70283 and fb6d67a.

📒 Files selected for processing (5)
  • .github/workflows/nightly-release.yml
  • .github/workflows/release.yml
  • flashinfer/jit/cpp_ext.py
  • scripts/build_flashinfer_jit_cache_whl.sh
  • scripts/task_test_jit_cache_package_build_import.sh

Comment thread scripts/build_flashinfer_jit_cache_whl.sh
Comment thread scripts/build_flashinfer_jit_cache_whl.sh Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
scripts/build_flashinfer_jit_cache_whl.sh (1)

69-90: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing credentialless S3 cache access handling.

This block enables sccache unconditionally when SCCACHE_BUCKET is set, but fork PRs do not receive AWS secrets. Without setting SCCACHE_S3_NO_CREDENTIALS=true, those jobs will fail to use the public read-only cache path.

Compare with scripts/task_test_jit_cache_package_build_import.sh which correctly handles this case at lines 123-129.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build_flashinfer_jit_cache_whl.sh` around lines 69 - 90, The sccache
setup enables caching whenever SCCACHE_BUCKET is set but doesn't handle
credentialless public-read-only access for fork PRs; update the block that
configures sccache (the logic setting SCCACHE_S3_KEY_PREFIX,
FLASHINFER_NVCC_LAUNCHER, FLASHINFER_CXX_LAUNCHER and starting sccache) to
detect when AWS credentials are absent and set SCCACHE_S3_NO_CREDENTIALS=true
accordingly (mirror the check used in
scripts/task_test_jit_cache_package_build_import.sh), so that when
SCCACHE_BUCKET is present but no AWS secrets are available the script uses
credentialless S3 access instead of failing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@flashinfer/jit/cpp_ext.py`:
- Around line 183-200: The nvcc jobserver check in should_use_nvcc_jobserver
currently allows CUDA 13.0 but the --jobserver flag requires CUDA 13.1+, so
update the version comparison in should_use_nvcc_jobserver from Version("13.0")
to Version("13.1"); ensure get_nvcc_parallelism_flags continues to call
should_use_nvcc_jobserver(cuda_version) so the --jobserver flag is only appended
for CUDA >= 13.1 (functions: should_use_nvcc_jobserver and
get_nvcc_parallelism_flags).

---

Duplicate comments:
In `@scripts/build_flashinfer_jit_cache_whl.sh`:
- Around line 69-90: The sccache setup enables caching whenever SCCACHE_BUCKET
is set but doesn't handle credentialless public-read-only access for fork PRs;
update the block that configures sccache (the logic setting
SCCACHE_S3_KEY_PREFIX, FLASHINFER_NVCC_LAUNCHER, FLASHINFER_CXX_LAUNCHER and
starting sccache) to detect when AWS credentials are absent and set
SCCACHE_S3_NO_CREDENTIALS=true accordingly (mirror the check used in
scripts/task_test_jit_cache_package_build_import.sh), so that when
SCCACHE_BUCKET is present but no AWS secrets are available the script uses
credentialless S3 access instead of failing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac54837e-e8f4-4a0a-b22f-f2cef3c46579

📥 Commits

Reviewing files that changed from the base of the PR and between fb6d67a and 2169e1a.

📒 Files selected for processing (7)
  • .github/workflows/nightly-release.yml
  • .github/workflows/release.yml
  • flashinfer/jit/core.py
  • flashinfer/jit/cpp_ext.py
  • scripts/build_flashinfer_jit_cache_whl.sh
  • scripts/task_test_jit_cache_package_build_import.sh
  • tests/test_jit_cpp_ext.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/release.yml
  • .github/workflows/nightly-release.yml

Comment thread flashinfer/jit/cpp_ext.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/build_flashinfer_jit_cache_whl.sh (1)

99-101: ⚠️ Potential issue | 🟠 Major

Add --no-isolation to the wheel build command.

This production wheel builder must use the current environment's CUDA/NVSHMEM stack. An isolated build will install the latest nvidia-nvshmem-cu12 from PyPI, creating a version mismatch between the compiled module's device library and the runtime's host library (provided by torch). This causes runtime failures in NVSHMEM-dependent operations.

Suggested change
 # Build the wheel using the build module for better isolation
 echo "Building wheel..."
-python -m build --wheel
+python -m build --wheel --no-isolation
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build_flashinfer_jit_cache_whl.sh` around lines 99 - 101, The wheel
build invocation "python -m build --wheel" uses an isolated build environment
which pulls PyPI dependencies (e.g., nvidia-nvshmem-cu12); change the invocation
to disable isolation by adding the --no-isolation flag so the builder uses the
current environment's CUDA/NVSHMEM stack (update the "python -m build --wheel"
command to "python -m build --wheel --no-isolation").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/build_flashinfer_jit_cache_whl.sh`:
- Around line 17-21: The wheel build should pass --no-isolation to avoid
pip-resolving nvidia-nvshmem-cu12; change the python invocation to use "python
-m build --wheel --no-isolation". Also validate FLASHINFER_NVCC_THREADS before
doing arithmetic: parse/convert FLASHINFER_NVCC_THREADS to an integer (fall back
to NUM_ARCHS if empty or non-numeric) and assign that to NVCC_THREADS (or a temp
VAR) before the existing comparisons that use NUM_ARCHS, NVCC_THREADS and NPROC
so the (( ... )) arithmetic never sees non-numeric values.

In `@scripts/task_test_jit_cache_package_build_import.sh`:
- Around line 497-502: Before running the build in the flashinfer-jit-cache
step, remove any existing dist/ artifacts so WHEEL_FILE won't pick up an old
wheel; specifically, add a cleanup that deletes or empties the dist directory
before invoking run_with_aot_memory_monitor "build_flashinfer_jit_cache_wheel"
(python -m build --wheel), then keep the current WHEEL_FILE assignment
(WHEEL_FILE=$(ls dist/*.whl | head -n 1)) so it will select the newly produced
wheel from the fresh dist/ directory.

---

Outside diff comments:
In `@scripts/build_flashinfer_jit_cache_whl.sh`:
- Around line 99-101: The wheel build invocation "python -m build --wheel" uses
an isolated build environment which pulls PyPI dependencies (e.g.,
nvidia-nvshmem-cu12); change the invocation to disable isolation by adding the
--no-isolation flag so the builder uses the current environment's CUDA/NVSHMEM
stack (update the "python -m build --wheel" command to "python -m build --wheel
--no-isolation").
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e7783455-3b13-436e-a7f9-80e6cf97d0df

📥 Commits

Reviewing files that changed from the base of the PR and between 2169e1a and 6986b83.

📒 Files selected for processing (2)
  • scripts/build_flashinfer_jit_cache_whl.sh
  • scripts/task_test_jit_cache_package_build_import.sh

Comment thread scripts/build_flashinfer_jit_cache_whl.sh Outdated
Comment thread scripts/task_test_jit_cache_package_build_import.sh Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
flashinfer/jit/cpp_ext.py (1)

183-200: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update CUDA version check from 13.0 to 13.1 for --jobserver flag.

The --jobserver flag was introduced in CUDA 13.1, not 13.0. The current check at line 189 uses Version("13.0") but should use Version("13.1") to prevent attempting to use an unsupported flag.

🐛 Proposed fix
 def should_use_nvcc_jobserver(cuda_version: Optional[Version] = None) -> bool:
     """Return whether nvcc can safely acquire slots from the inherited jobserver."""
     if not sys.platform.startswith("linux"):
         return False
     if cuda_version is None:
         cuda_version = get_cuda_version()
-    return cuda_version >= Version("13.0") and (
+    return cuda_version >= Version("13.1") and (
         should_use_ninja_jobserver() or _launcher_uses_sccache()
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@flashinfer/jit/cpp_ext.py` around lines 183 - 200, The CUDA version check in
should_use_nvcc_jobserver is wrong: change the minimum required Version from
"13.0" to "13.1" so the function only enables the nvcc --jobserver flag for CUDA
>= 13.1; update the Version("13.0") literal used in should_use_nvcc_jobserver
(which get_nvcc_parallelism_flags relies on) to Version("13.1") so
flags.append("--jobserver") is only attempted for supported CUDA versions.
scripts/task_test_jit_cache_package_build_import.sh (1)

499-504: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clean dist/ directory before building wheel.

As noted in the past review, if the workspace contains a previous build artifact, line 504 (ls dist/*.whl | head -n 1) may select an older wheel. Add cleanup before the build step.

🔧 Suggested fix
 cd flashinfer-jit-cache
+rm -rf dist build *.egg-info
 run_with_aot_memory_monitor "build_flashinfer_jit_cache_wheel" \
     python -m build --wheel
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/task_test_jit_cache_package_build_import.sh` around lines 499 - 504,
Before running build_flashinfer_jit_cache_wheel, clear previous artifacts so
WHEEL_FILE (computed from ls dist/*.whl) won't pick an old wheel: in the cd
flashinfer-jit-cache block (where build_flashinfer_jit_cache_wheel and python -m
build --wheel are invoked) add a cleanup step to remove the dist directory or
delete dist/*.whl (e.g., rm -rf dist || true or rm -f dist/*.whl) immediately
before running the build so the subsequent WHEEL_FILE assignment always picks
the newly produced wheel.
🧹 Nitpick comments (3)
.github/workflows/pr-test.yml (2)

334-339: 💤 Low value

Same quoting issue for ${DOCKER_IMAGE}.

Apply the same fix as noted above for the aot-build-import job.

🔧 Suggested fix
-          bash ci/bash.sh ${DOCKER_IMAGE} \
+          bash ci/bash.sh "${DOCKER_IMAGE}" \
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-test.yml around lines 334 - 339, The workflow step
invokes bash ci/bash.sh ${DOCKER_IMAGE} ... without quoting the DOCKER_IMAGE
variable; update that invocation to quote the variable (use "${DOCKER_IMAGE}")
so docker image values with special chars or spaces are handled safely—modify
the bash call in the task_test_jit_cache_package_build_import.sh step (same
pattern as fixed in the aot-build-import job) to replace ${DOCKER_IMAGE} with
"${DOCKER_IMAGE}" while leaving the rest of the environment flags intact.

242-247: 💤 Low value

Quote ${DOCKER_IMAGE} to prevent word splitting.

Static analysis flags SC2086 on the unquoted ${DOCKER_IMAGE} variable. While Docker image names typically don't contain spaces, quoting prevents unexpected behavior if the tag contains special characters.

🔧 Suggested fix
-          bash ci/bash.sh ${DOCKER_IMAGE} \
+          bash ci/bash.sh "${DOCKER_IMAGE}" \
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-test.yml around lines 242 - 247, The Docker image
variable is unquoted causing shell word-splitting (SC2086); update the call to
pass the image as a single quoted argument by changing the invocation of
ci/bash.sh to use "${DOCKER_IMAGE}" instead of ${DOCKER_IMAGE} so the bash
wrapper receives the full image string intact.
scripts/task_test_jit_cache_package_build_import.sh (1)

462-467: Add SHA256 checksum verification for sccache download.

The script downloads sccache without verifying the checksum. Mozilla publishes SHA256 checksums for release binaries as separate .sha256 files (e.g., sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz.sha256). Adding checksum verification is recommended for supply chain security, especially since this binary will execute compiler commands. Consider downloading and verifying the checksum before extracting the binary.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/task_test_jit_cache_package_build_import.sh` around lines 462 - 467,
The sccache download currently pipes the tarball straight to tar without
integrity checks; modify the block using SCCACHE_VERSION and SCCACHE_ARCH to
first download the tar.gz and its corresponding .sha256 file, verify the tarball
with sha256sum (or by computing and comparing the checksum) and exit non‑zero on
mismatch, and only after successful verification extract, move and chmod the
sccache binary (replacing the existing curl | tar step with explicit download +
checksum verification + extraction).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@flashinfer/jit/cpp_ext.py`:
- Around line 183-200: The CUDA version check in should_use_nvcc_jobserver is
wrong: change the minimum required Version from "13.0" to "13.1" so the function
only enables the nvcc --jobserver flag for CUDA >= 13.1; update the
Version("13.0") literal used in should_use_nvcc_jobserver (which
get_nvcc_parallelism_flags relies on) to Version("13.1") so
flags.append("--jobserver") is only attempted for supported CUDA versions.

In `@scripts/task_test_jit_cache_package_build_import.sh`:
- Around line 499-504: Before running build_flashinfer_jit_cache_wheel, clear
previous artifacts so WHEEL_FILE (computed from ls dist/*.whl) won't pick an old
wheel: in the cd flashinfer-jit-cache block (where
build_flashinfer_jit_cache_wheel and python -m build --wheel are invoked) add a
cleanup step to remove the dist directory or delete dist/*.whl (e.g., rm -rf
dist || true or rm -f dist/*.whl) immediately before running the build so the
subsequent WHEEL_FILE assignment always picks the newly produced wheel.

---

Nitpick comments:
In @.github/workflows/pr-test.yml:
- Around line 334-339: The workflow step invokes bash ci/bash.sh ${DOCKER_IMAGE}
... without quoting the DOCKER_IMAGE variable; update that invocation to quote
the variable (use "${DOCKER_IMAGE}") so docker image values with special chars
or spaces are handled safely—modify the bash call in the
task_test_jit_cache_package_build_import.sh step (same pattern as fixed in the
aot-build-import job) to replace ${DOCKER_IMAGE} with "${DOCKER_IMAGE}" while
leaving the rest of the environment flags intact.
- Around line 242-247: The Docker image variable is unquoted causing shell
word-splitting (SC2086); update the call to pass the image as a single quoted
argument by changing the invocation of ci/bash.sh to use "${DOCKER_IMAGE}"
instead of ${DOCKER_IMAGE} so the bash wrapper receives the full image string
intact.

In `@scripts/task_test_jit_cache_package_build_import.sh`:
- Around line 462-467: The sccache download currently pipes the tarball straight
to tar without integrity checks; modify the block using SCCACHE_VERSION and
SCCACHE_ARCH to first download the tar.gz and its corresponding .sha256 file,
verify the tarball with sha256sum (or by computing and comparing the checksum)
and exit non‑zero on mismatch, and only after successful verification extract,
move and chmod the sccache binary (replacing the existing curl | tar step with
explicit download + checksum verification + extraction).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b81b8d67-ab0e-461d-a9d8-0b08fc19f125

📥 Commits

Reviewing files that changed from the base of the PR and between 6986b83 and 9a44887.

📒 Files selected for processing (5)
  • .github/workflows/pr-test.yml
  • flashinfer/jit/core.py
  • flashinfer/jit/cpp_ext.py
  • scripts/task_test_jit_cache_package_build_import.sh
  • tests/test_jit_cpp_ext.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • flashinfer/jit/core.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/pr-test.yml:
- Around line 242-247: The workflow is passing AWS secrets as CLI args which
exposes them; update the implementation of ci/bash.sh (the script parsing -e
flags) to accept a flag form "-e KEY" with no value and in that case read the
value from the process environment (e.g., getenv(KEY)) instead of expecting an
inline value, and then update the workflow invocation lines that currently pass
"${AWS_ACCESS_KEY_ID}" and "${AWS_SECRET_ACCESS_KEY}" to use the new "-e
AWS_ACCESS_KEY_ID" and "-e AWS_SECRET_ACCESS_KEY" form (or alternatively remove
the inline expansions entirely), ensuring the parsing code in ci/bash.sh handles
both "-e KEY value" and "-e KEY" cases and does not expose secrets on the
command line.

In `@scripts/task_test_jit_cache_package_build_import.sh`:
- Around line 508-514: The credential check currently runs with xtrace enabled
and can leak AWS secrets; wrap the conditional that reads and exports
SCCACHE_S3_NO_CREDENTIALS (the if block referencing AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY, SCCACHE_S3_NO_CREDENTIALS) in a temporary xtrace
disable/restore: turn off tracing (set +x) immediately before the if, perform
the exports/unsets and echo mode, then restore tracing (set -x) afterwards so no
secret values are printed to the trace.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1a73c83c-7233-4123-aac6-debf6980e74d

📥 Commits

Reviewing files that changed from the base of the PR and between 9a44887 and 5c528c7.

📒 Files selected for processing (3)
  • .github/workflows/pr-test.yml
  • scripts/build_flashinfer_jit_cache_whl.sh
  • scripts/task_test_jit_cache_package_build_import.sh

Comment thread .github/workflows/pr-test.yml
Comment thread scripts/task_test_jit_cache_package_build_import.sh Outdated
@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/flashinfer-ai/flashinfer/issues/comments/4347536874","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: review paused by coderabbit.ai -->\n\n> [!NOTE]\n> ## Reviews paused\n> \n> It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the `reviews.auto_review.auto_pause_after_reviewed_commits` setting.\n> \n> Use the following commands to manage reviews:\n> - `@coderabbitai resume` to resume automatic reviews.\n> - `@coderabbitai review` to trigger a single review.\n> \n> Use the checkboxes below for quick actions:\n> - [ ] <!-- {\"checkboxId\": \"7f6cc2e2-2e4e-497a-8c31-c9e4573e93d1\"} --> ▶️ Resume reviews\n> - [ ] <!-- {\"checkboxId\": \"e9bb8d72-00e8-4f67-9cb2-caf3b22574fe\"} --> 🔍 Trigger review\n\n<!-- end of auto-generated comment: review paused by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nAdds jobserver-aware nvcc parallelism and sccache-backed remote compilation caching: exports/control of `FLASHINFER_NVCC_THREADS`, forwards SCCache/S3/AWS env into CI containers, installs/configures `sccache` when enabled, recomputes `MAX_JOBS` via memory budgeting, updates Ninja generation and tests accordingly.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**GitHub Workflows** <br> ` .github/workflows/nightly-release.yml`, `.github/workflows/release.yml`, `.github/workflows/pr-test.yml`|Pass `FLASHINFER_NVCC_THREADS`, `SCCACHE_BUCKET`, `SCCACHE_REGION`, and AWS credential envs into build/test Docker runs.|\n|**JIT C++/CUDA build integration** <br> `flashinfer/jit/cpp_ext.py`|Add nvcc-thread parsing/validation, Make/Ninja jobserver detection, helpers for jobserver/launcher usage, emit optional `cxx_launcher`/`nvcc_launcher` in generated Ninja, and avoid `-j` when inheriting jobserver.|\n|**JIT core build orchestration** <br> `flashinfer/jit/core.py`|Always regenerate per-op `build.ninja` under FileLock and switch CUDA flags to use `get_nvcc_parallelism_flags()` (including `--device-debug` when applicable).|\n|**Build & test scripts** <br> `scripts/build_flashinfer_jit_cache_whl.sh`, `scripts/task_test_jit_cache_package_build_import.sh`|Introduce `FLASHINFER_NVCC_THREADS`, compute/cap it, recompute `MAX_JOBS` via a per-job memory budget (bounded by CPUs), install/configure/start `sccache` for S3 backend when set, export launchers and SCCACHE envs, and print sccache stats.|\n|**Tests** <br> `tests/test_jit_cpp_ext.py`|New tests validating nvcc parallelism flags, jobserver conditions, Ninja generation details, `run_ninja()` `-j` behavior, and that `JitSpec.build()` writes the Ninja file before building.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    actor GHA as GitHub Actions\n    participant Docker as Docker Container\n    participant Script as Build Script\n    participant Sccache as sccache Server\n    participant S3 as S3 Storage\n    participant Compiler as nvcc/C++ Compiler\n\n    GHA->>Docker: Run container, pass env (FLASHINFER_NVCC_THREADS, SCCACHE_BUCKET, SCCACHE_REGION, AWS_*, ...)\n    Docker->>Script: Execute build_flashinfer_jit_cache_whl.sh\n    alt SCCACHE_BUCKET set\n        Script->>Sccache: Install & start sccache\n        Script->>Sccache: Configure S3 backend (bucket, region, prefix)\n        Script->>Script: Export FLASHINFER_NVCC_LAUNCHER / FLASHINFER_CXX_LAUNCHER = sccache\n    end\n    Script->>Compiler: Invoke compiler (via launcher if set / inherit jobserver if available)\n    Compiler->>Sccache: Request compile (when launcher used)\n    Sccache->>S3: Fetch/store cache objects\n    S3-->>Sccache: Cache hit/miss\n    Sccache-->>Compiler: Return cached artifact or compiled output\n    Script->>Sccache: Print sccache stats\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~50 minutes\n\n## Possibly related PRs\n\n- flashinfer-ai/flashinfer#2089 — Related changes to build script MAX_JOBS and compilation-parallelism logic.  \n- flashinfer-ai/flashinfer#3078 — Related adjustments to exported MAX_JOBS and CI memory-aware parallelism.  \n- flashinfer-ai/flashinfer#2422 — Related edits to JIT spec generation and CUDA/C++ flag assembly.\n\n## Suggested labels\n\n`run-ci`\n\n## Suggested reviewers\n\n- sricketts  \n- aleozlx  \n- yongwww  \n- yzh119  \n- bkryu\n\n## Poem\n\n> 🐰 I count the threads and watch them play,  \n> sccache hides the work and saves the day.  \n> Ninja whispers tokens, jobservers hum,  \n> compiles hop faster — caches make them run.  \n> a rabbit cheers — your builds are done!\n\n</details>\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 3</summary>\n\n### ❌ Failed checks (2 warnings, 1 inconclusive)\n\n|     Check name     | Status         | Explanation                                                                                                                                                             | Resolution                                                                                                                                                                                         |\n| :----------------: | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n|  Description check | ⚠️ Warning     | The PR description contains only the empty template with unchecked boxes and no actual content describing the changes, rationale, related issues, or reviewer notes.    | Fill in the Description section explaining what this PR does and why, link any related issues, and provide reviewer notes highlighting the purpose of sccache integration and nvcc thread changes. |\n| Docstring Coverage | ⚠️ Warning     | Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%.                                                                                    | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                                                                                 |\n|     Title check    | ❓ Inconclusive | The title 'WIP sccache testing' is vague and generic, using 'WIP' (work-in-progress) as a placeholder without describing the actual changes being made to the codebase. | Replace with a descriptive title that reflects the main change, such as 'Add sccache integration and nvcc parallelism control' or 'Implement sccache caching and FLASHINFER_NVCC_THREADS support'. |\n\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|         Check name         | Status   | Explanation                                                              |\n| :------------------------: | :------- | :----------------------------------------------------------------------- |\n|     Linked Issues check    | ✅ Passed | Check skipped because no linked issues were found for this pull request. |\n| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `jdierksen/sccache-nvcc-threads`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- announcements_start -->\n\n> [!TIP]\n> <details>\n> <summary>💬 Introducing Slack Agent: The best way for teams to turn conversations into code.</summary>\n> \n> [Slack Agent](https://www.coderabbit.ai/agent) is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.\n> \n> - Generate code and open pull requests\n> - Plan features and break down work\n> - Investigate incidents and troubleshoot customer tickets together\n> - Automate recurring tasks and respond to alerts with triggers\n> - Summarize progress and report instantly\n> \n> Built for teams:\n> \n> - **Shared memory** across your entire org—no repeating context\n> - **Per-thread sandboxes** to safely plan and execute work\n> - **Governance built-in**—scoped access, auditability, and budget controls\n> \n> One agent for your entire SDLC. Right inside Slack.\n> \n> 👉 [Get started](https://agent.coderabbit.ai/)\n> \n> </details>\n\n<!-- announcements_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=flashinfer-ai/flashinfer&utm_content=3205)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/flashinfer-ai/flashinfer/issues/comments/4347536874","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 04 May 2026 16:04:05 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"4276:1C476F:1B53E7C:6CC6FB7:69F8C374","x-ratelimit-limit":"7950","x-ratelimit-remaining":"7929","x-ratelimit-reset":"1777914179","x-ratelimit-resource":"core","x-ratelimit-used":"21","x-xss-protection":"0"},"data":""}}

@dierksen dierksen changed the title WIP sccache testing Add sscache support for building AOT wheels May 4, 2026
@dierksen dierksen changed the title Add sscache support for building AOT wheels build: add sccache-backed jit-cache builds and AOT diagnostics May 5, 2026
@dierksen dierksen closed this May 6, 2026
@dierksen dierksen reopened this May 6, 2026
@dierksen

dierksen commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run

@flashinfer-bot

Copy link
Copy Markdown
Collaborator

GitLab MR !639 has been created, and the CI pipeline #50469392 is currently running. I'll report back once the pipeline job completes.

Comment thread flashinfer/jit/core.py
Comment thread flashinfer/jit/cpp_ext.py Outdated
Comment thread scripts/build_flashinfer_jit_cache_whl.sh Outdated
Comment thread scripts/build_flashinfer_jit_cache_whl.sh Outdated
Comment thread scripts/task_test_jit_cache_package_build_import.sh Outdated

@yongwww yongwww left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall lgtm

Move the parallelism computation, sccache installer, and sccache env
configuration out of build_flashinfer_jit_cache_whl.sh and
task_test_jit_cache_package_build_import.sh into a new
scripts/jit_cache_build_common.sh sourced by both. Net -67 lines and
both callers now share one source of truth for these helpers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dierksen
dierksen enabled auto-merge (squash) May 7, 2026 21:35
@dierksen
dierksen merged commit 059008c into main May 8, 2026
82 of 111 checks passed
@dierksen
dierksen deleted the jdierksen/sccache-nvcc-threads branch May 8, 2026 18:58
aleozlx pushed a commit that referenced this pull request May 12, 2026
## Description

This PR adds sccache-backed compiler caching for the FlashInfer
jit-cache/AOT build paths and folds in the AOT memory diagnostics work
from #3204.

The main pieces are:

- Add compiler launcher support in the JIT build generation so `nvcc`
and host compiler invocations can be wrapped by `sccache` or another
launcher.
- Install and configure `sccache` in the release/nightly jit-cache wheel
builds and the PR AOT build/import jobs.
- Use the shared S3 cache in read-write mode when AWS credentials are
available, and fall back to anonymous read-only mode for PR jobs without
credentials.
- Print `sccache --show-stats` at the end of the relevant jobs so cache
hit rates and compile behavior are visible in CI logs.
- Keep `FLASHINFER_NVCC_THREADS` supported, but default it back to `1`
after CI showed higher nvcc internal threading was slower for this
workload.
- Increase build-level parallelism through `MAX_JOBS`, bounded by CPU
count and an AOT memory budget so the larger CUDA builds do not overrun
the runner.
- Add AOT memory monitoring/report files around install, wheel build,
import/config, and module verification steps to diagnose OOMs and runner
shutdowns.
- Clean stale jit-cache build artifacts before building and pick the
newest wheel from `dist/`.
- Add tests for the nvcc flag generation, launcher-compatible
depfile/debug flags, and build regeneration behavior.

## Context

This PR supersedes #3204. The memory monitor and AOT `MAX_JOBS` safety
work from #3204 are included here, but the final script shape is
different because it is integrated with the sccache setup and the later
threading/concurrency experiments. Merging #3204 separately would still
create a conflict in
`scripts/task_test_jit_cache_package_build_import.sh`.

The current direction is to use sccache for the big rebuild/retry win
while keeping nvcc internal threading conservative. The PR still allows
`FLASHINFER_NVCC_THREADS` to be overridden for experiments, but the
default path lets ninja drive parallelism through `MAX_JOBS`.

## CI Notes

The latest PR Test run completed successfully after the
infrastructure-triggered AOT rerun path. The Release workflow's
jit-cache jobs completed and showed useful sccache hit rates; the
remaining Release failure was in `build-flashinfer-cubin` while
downloading cubins, which appears separate from the sccache/AOT build
changes.

## Related

Supersedes #3204.

## Performance comparison

This looks at the mean and median time to complete each AOT build type
over the last 48 hours compared with the most recent 2 builds on this
branch.

| job | 48h mean | 48h median |
[#4214](https://github.com/flashinfer-ai/flashinfer/actions/runs/25402485487)
| #4214 vs median |
[#4284](https://github.com/flashinfer-ai/flashinfer/actions/runs/25457504611)
| #4284 vs median |
  |---|---:|---:|---:|---:|---:|---:|
| x64 cu126 | 1:09:34 | 51:55 | 42:28 | 18.2% faster | 31:36 | 39.1%
faster |
| x64 cu128 | 1:45:24 | 1:24:04 | 1:09:46 | 17.0% faster | 45:04 | 46.4%
faster |
| x64 cu129 | 2:36:17 | 2:25:29 | 2:12:39 | 8.8% faster | 59:50 | 58.9%
faster |
| x64 cu130 | 2:27:16 | 2:04:49 | 1:48:57 | 12.7% faster | 59:34 | 52.3%
faster |
| arm64 cu126 | 1:23:52 | 1:30:43 | 1:13:56 | 18.5% faster | 50:43 |
44.1% faster |
| arm64 cu128 | 2:12:08 | 2:25:56 | 2:04:47 | 14.5% faster | 1:12:37 |
50.2% faster |
| arm64 cu129 | 3:45:38 | 4:05:35 | 3:38:02 | 11.2% faster | 1:31:15 |
62.8% faster |
| arm64 cu130 | 3:34:21 | 3:53:18 | 3:21:49 | 13.5% faster | 1:34:48 |
59.4% faster |

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai coderabbitai Bot mentioned this pull request May 18, 2026
5 tasks
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.

3 participants