Skip to content

[Enhancement] Speed up cold parallel/AOT compilation up to ~4x - #2809

Merged
SiriusNEO merged 2 commits into
tile-ai:mainfrom
cklxx:perf/compile-speed
Aug 3, 2026
Merged

[Enhancement] Speed up cold parallel/AOT compilation up to ~4x#2809
SiriusNEO merged 2 commits into
tile-ai:mainfrom
cklxx:perf/compile-speed

Conversation

@cklxx

@cklxx cklxx commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Speeds up cold parallel/AOT compilation. Three independent, low-risk changes:

  1. cache: move the per-kernel disk-save out of the global KernelCache lock. The save stages+renames atomically and is idempotent, so it was already safe without the lock; holding it serialized every worker's save. (the bulk of the win)
  2. jit: par_compile defaults to min(len(funcs), available_cpus) instead of min(32, cpu+4). get_available_cpu_count is cgroup-aware (cpuset affinity + CFS quota).
  3. nvcc: opt-in TL_NVCC_THREADS (--threads ≥11.2, --split-compile ≥12.1), off by default. Scheduling-only (identical SASS); helps large multi-kernel TUs.

Measured on H20 / 180 cores, 126 cold kernels (benchmark/compile_speed, a zoo of GEMM/attention/RMSNorm/SwiGLU/softmax over Qwen2.5/Llama-3 dims):

wall speedup
baseline (global lock, 32 workers) 50.3s 1.00x
this PR (no lock, 180 workers) 16.2s 3.1x
python benchmark/compile_speed/benchmark_compile_speed.py

No API/ABI change; #1 and #2 are safe defaults, #3 is opt-in.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@github-actions

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the TileLang project.

Please remember to run pre-commit run --all-files in the root directory of the project to ensure your changes are properly linted and formatted. This will help ensure your contribution passes the format check.

We appreciate you taking this step! Our team will review your contribution, and we look forward to your awesome work! 🚀

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

Changes

CPU-aware worker selection is centralized, CUDA compilation gains optional parallelism and ccache support, kernel cache persistence moves outside the lock, and a benchmark with generated kernel workloads measures cold parallel-compilation throughput.

Compilation and cache updates

Layer / File(s) Summary
CPU-aware parallel compilation
tilelang/utils/device.py, tilelang/jit/__init__.py, tilelang/autotuner/tuner.py, testing/python/utils/test_device_cpu_count.py
Adds affinity- and cgroup-aware CPU counting, environment-controlled JIT worker selection, shared autotuner usage, and parser coverage.
CUDA compilation acceleration
tilelang/contrib/nvcc.py, tilelang/jit/adapter/libgen.py
Adds opt-in NVCC scheduling flags and ccache wrapping for CUDA compilation paths.
Cache persistence outside the lock
tilelang/cache/kernel_cache.py
Moves disk cache saving and adapter cache-path setup outside the lock when caching is enabled.
Compile-speed benchmark
benchmark/compile_speed/kernel_zoo.py, benchmark/compile_speed/benchmark_compile_speed.py, benchmark/compile_speed/README.md
Adds generated GEMM, attention, normalization, activation, and softmax kernels; measures locked versus unlocked cold compilation; and documents reproduction and results.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant par_compile
  participant KernelCache
  participant compile_cuda
  participant ccache
  participant nvcc
  par_compile->>KernelCache: compile cold kernel
  KernelCache->>compile_cuda: invoke CUDA compilation
  compile_cuda->>ccache: optionally wrap command
  ccache->>nvcc: forward NVCC flags and source
  nvcc-->>compile_cuda: compiled artifact
  compile_cuda-->>KernelCache: compilation result
  KernelCache->>KernelCache: save artifact without holding lock
Loading

Possibly related PRs

Suggested reviewers: leiwang1999

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% 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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: faster cold parallel/AOT compilation with an approximate 4x improvement.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tilelang/cache/kernel_cache.py`:
- Around line 404-415: Coordinate cache persistence with clear_cache so clearing
is mutually exclusive with in-flight saves. Update the cache flow around
_save_kernel_to_disk and memory-entry publication to register active saves
before disk persistence, block new persistence while clear_cache() runs, and
have clear_cache() wait for all active saves to finish before removing state.
Ensure a save cannot republish its memory entry after clearing completes.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f839f30e-b381-4f37-80d0-fc319ffbb78e

📥 Commits

Reviewing files that changed from the base of the PR and between 6c3dd97 and f96178d.

📒 Files selected for processing (6)
  • tilelang/autotuner/tuner.py
  • tilelang/cache/kernel_cache.py
  • tilelang/contrib/nvcc.py
  • tilelang/jit/__init__.py
  • tilelang/jit/adapter/libgen.py
  • tilelang/utils/device.py

Comment thread tilelang/cache/kernel_cache.py Outdated
@cklxx
cklxx force-pushed the perf/compile-speed branch from f96178d to 40a0fed Compare July 30, 2026 03:13

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tilelang/utils/device.py`:
- Around line 26-38: Update get_available_cpu_count to detect cgroup CPU
bandwidth quotas from v2 cpu.max and v1 cpu.cfs_quota_us/cpu.cfs_period_us, and
cap the affinity or os.cpu_count result at ceil(quota / period) when a positive
quota is configured. Preserve the existing affinity-first and fallback behavior
for quota-less or unavailable cgroup environments, and continue returning at
least 1.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b1457868-a114-444f-8f20-215fa3fdd796

📥 Commits

Reviewing files that changed from the base of the PR and between f96178d and 40a0fed.

📒 Files selected for processing (6)
  • tilelang/autotuner/tuner.py
  • tilelang/cache/kernel_cache.py
  • tilelang/contrib/nvcc.py
  • tilelang/jit/__init__.py
  • tilelang/jit/adapter/libgen.py
  • tilelang/utils/device.py

Comment thread tilelang/utils/device.py Outdated
@cklxx
cklxx force-pushed the perf/compile-speed branch from 40a0fed to af78d2d Compare July 30, 2026 04:04
@LeiWang1999
LeiWang1999 requested a review from SiriusNEO July 30, 2026 05:06
@SiriusNEO SiriusNEO self-assigned this Jul 30, 2026
@cklxx cklxx changed the title perf: 4x faster parallel/AOT compile — unblock KernelCache lock, core-scale workers, opt-in nvcc threads/ccache [Enhancement] Speed up cold parallel/AOT compilation up to ~4x Jul 30, 2026
@cklxx
cklxx force-pushed the perf/compile-speed branch from af78d2d to 9c80d6b Compare July 30, 2026 06:33
Comment thread tilelang/utils/device.py
Comment thread tilelang/contrib/nvcc.py Outdated
@cklxx
cklxx force-pushed the perf/compile-speed branch from 9c80d6b to 0b2abce Compare July 30, 2026 07:59

@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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@benchmark/compile_speed/README.md`:
- Around line 44-46: Remove the duplicated consecutive “126 cold kernels, H20 /
180 cores:” heading in the benchmark README, leaving a single occurrence.

In `@tilelang/contrib/nvcc.py`:
- Around line 101-122: Update the TL_NVCC_THREADS validation in the
environment-variable parsing flow to treat zero as disabled, returning no flags
for n == 0. Keep positive values enabled and preserve the existing
negative-value rejection and CUDA-version gating in the surrounding flag
construction.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 357845c0-957b-4607-9e79-bf96c37f1790

📥 Commits

Reviewing files that changed from the base of the PR and between 9c80d6b and 0b2abce.

📒 Files selected for processing (10)
  • benchmark/compile_speed/README.md
  • benchmark/compile_speed/benchmark_compile_speed.py
  • benchmark/compile_speed/kernel_zoo.py
  • testing/python/utils/test_device_cpu_count.py
  • tilelang/autotuner/tuner.py
  • tilelang/cache/kernel_cache.py
  • tilelang/contrib/nvcc.py
  • tilelang/jit/__init__.py
  • tilelang/jit/adapter/libgen.py
  • tilelang/utils/device.py

Comment thread benchmark/compile_speed/README.md Outdated
Comment thread tilelang/contrib/nvcc.py Outdated
Comment on lines +101 to +122
raw = os.environ.get("TL_NVCC_THREADS")
if raw is None:
return []
try:
n = int(raw)
except ValueError:
return []
if n < 0:
return []

try:
version = get_cuda_version() # (major, minor, ...)
major, minor = version[0], version[1] if len(version) > 1 else 0
except Exception:
return []

flags: list[str] = []
if (major, minor) >= (11, 2):
flags += ["--threads", str(n)]
# --split-compile was introduced in CUDA 12.1 (absent from 11.x and 12.0 nvcc).
if (major, minor) >= (12, 1):
flags += ["--split-compile", str(n)]

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.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C3 'TL_NVCC_THREADS|if n < 0|--threads|--split-compile' tilelang/contrib/nvcc.py
nvcc --help | grep -A6 -- '--threads'

Repository: tile-ai/tilelang

Length of output: 1069


🌐 Web query:

NVIDIA nvcc --threads --split-compile zero value use all CPUs documentation

💡 Result:

According to official NVIDIA CUDA documentation, the --split-compile option (and its extended variant, --split-compile-extended) allows you to perform compiler optimizations in parallel to reduce compile time [1][2]. When you set these options to 0 (e.g., --split-compile=0 or --split-compile-extended=0), the compiler is instructed to use the maximum number of threads available on your system [1][3]. Key details regarding these flags include: - Functionality: These options split device code into smaller translation units, allowing optimization passes to run concurrently across multiple threads before linking them back together prior to code generation [1][4]. - --split-compile=1: Setting the value to 1 causes the option to be ignored [1][2]. - Compatibility: --split-compile can be used in conjunction with device Link Time Optimization (-dlto) and the --threads flag [1][3]. --split-compile-extended is only applicable when using -dlto [1][2]. - Trade-offs: While split compilation is intended to accelerate compilation, --split-compile-extended is noted as a more aggressive form that may potentially impact the performance of the resulting compiled binary [1][3].

Citations:


🌐 Web query:

site:docs.nvidia.com/cuda nvcc --threads N 0 use number of threads

💡 Result:

The --threads option (abbreviated as -t) in the NVIDIA CUDA Compiler Driver (nvcc) is used to specify the maximum number of CPU threads used to execute compilation steps in parallel [1]. When you use the argument 0 with this option (i.e., --threads 0 or -t 0), the compiler defaults to using a number of threads equal to the total number of CPUs available on the machine [1]. Key details regarding this option: - Purpose: It is designed to improve compilation speed, particularly when compiling a single compilation unit for multiple GPU architectures [2][1]. - Behavior: If the specified number is 1, the option is ignored [1]. If the number is greater than 1, the compiler creates that specific number of threads to perform compilation steps in parallel [1]. - Compatibility: This option can work in conjunction with other compilation features such as --split-compile and --split-compile-extended, which also use thread-based parallelism to optimize the compilation process [1].

Citations:


🌐 Web query:

site:docs.nvidia.com/cuda nvcc --threads NVCC_THREADS threads option 1 ignored default thread count

💡 Result:

The --threads option (abbreviated as -t) in nvcc is used to specify the number of CPU threads utilized to parallelize the compilation of a single compilation unit for multiple GPU architectures [1][2]. Regarding your specific query about the option being ignored when set to 1, official NVIDIA documentation historically confirms this behavior: if the number specified is 1, the --threads option is ignored [3]. This is because a thread count of 1 provides no parallelism, so the compiler defaults to single-threaded execution [3]. If the number is set to 0, nvcc will typically use the number of available CPU threads [3].

Citations:


Treat TL_NVCC_THREADS=0 as disabled.

NVCC interprets 0 as “use all available CPU threads” for both --threads and --split-compile, so parallel AOT can overflow the cgroup-aware worker limits. Reject or ignore zero instead of emitting the flags.

Proposed fix
-    if n < 0:
+    if n <= 0:
         return []
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
raw = os.environ.get("TL_NVCC_THREADS")
if raw is None:
return []
try:
n = int(raw)
except ValueError:
return []
if n < 0:
return []
try:
version = get_cuda_version() # (major, minor, ...)
major, minor = version[0], version[1] if len(version) > 1 else 0
except Exception:
return []
flags: list[str] = []
if (major, minor) >= (11, 2):
flags += ["--threads", str(n)]
# --split-compile was introduced in CUDA 12.1 (absent from 11.x and 12.0 nvcc).
if (major, minor) >= (12, 1):
flags += ["--split-compile", str(n)]
raw = os.environ.get("TL_NVCC_THREADS")
if raw is None:
return []
try:
n = int(raw)
except ValueError:
return []
if n <= 0:
return []
try:
version = get_cuda_version() # (major, minor, ...)
major, minor = version[0], version[1] if len(version) > 1 else 0
except Exception:
return []
flags: list[str] = []
if (major, minor) >= (11, 2):
flags += ["--threads", str(n)]
# --split-compile was introduced in CUDA 12.1 (absent from 11.x and 12.0 nvcc).
if (major, minor) >= (12, 1):
flags += ["--split-compile", str(n)]
🧰 Tools
🪛 Ruff (0.16.0)

[warning] 114-114: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tilelang/contrib/nvcc.py` around lines 101 - 122, Update the TL_NVCC_THREADS
validation in the environment-variable parsing flow to treat zero as disabled,
returning no flags for n == 0. Keep positive values enabled and preserve the
existing negative-value rejection and CUDA-version gating in the surrounding
flag construction.

@cklxx
cklxx force-pushed the perf/compile-speed branch from 0b2abce to 1638ee4 Compare July 30, 2026 09:24
@cklxx
cklxx requested a review from SiriusNEO July 30, 2026 09:28
@cklxx
cklxx force-pushed the perf/compile-speed branch 2 times, most recently from 305134d to f307e31 Compare July 30, 2026 10:12
@cklxx

cklxx commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Pushed f307e317. Changes since your review:

  • cgroup quota (your device.py comment): get_available_cpu_count now caps the cpuset-affinity count at the CFS bandwidth quota (v2 cpu.max / v1 cpu.cfs_quota_us,period_us); pure parser is unit-tested. Verified on a 180-core box with unlimited quota → stays 180 (no false cap).
  • --split-compile gate (your nvcc.py comment): now gated at CUDA ≥ 12.1 (it's absent from 11.x and 12.0 nvcc), separate from the --threads ≥ 11.2 gate.
  • Dropped the ccache opt-in entirely: measured 0 hits on the default backend — CUDABinaryCache already skips nvcc on identical rebuilds, and compile_cuda uses --cubin, which ccache classifies as called_for_link and refuses to cache. It was a no-op promise, so it's gone; only TL_NVCC_THREADS remains under [Doc] Update the example figures in README #3.
  • Fixed a duplicated header line in the benchmark README.

Ready for another look when you have a moment.

Comment thread tilelang/utils/device.py Outdated
Comment thread tilelang/jit/__init__.py Outdated
@cklxx
cklxx force-pushed the perf/compile-speed branch from f307e31 to 6ada648 Compare July 30, 2026 11:19
@cklxx
cklxx requested a review from SiriusNEO July 30, 2026 13:41
Three independent, low-risk changes (measured 45.6s -> 11.5s for 144 cold
kernels on an H20, 180 cores):

1. cache: move kernel disk-save out of the global lock. KernelCache.cached()
   held the class lock around _save_kernel_to_disk (source + .so copy +
   cloudpickle), serializing every worker's save. The save stages+renames
   atomically and is idempotent, so it is already concurrency-safe without the
   lock (the disk load was already outside it). Lock now guards only
   _memory_cache. This is the bulk of the win (~3.1x).

2. jit: core-scale par_compile workers. Default was min(32, cpu+4); now
   min(len(funcs), available_cpus) when unset, with TILELANG_PAR_COMPILE_WORKERS
   override. Lowering is GIL-releasing C++ and nvcc is a subprocess, so threads
   parallelize. get_available_cpu_count is now cgroup-aware: it respects
   cpuset affinity AND caps at the cgroup CFS bandwidth quota
   (v2 cpu.max / v1 cpu.cfs_quota_us,period_us), so a CPU-throttled container
   does not oversubscribe nvcc jobs.

3. nvcc: opt-in parallel device compile (off by default). TL_NVCC_THREADS=N ->
   --threads (CUDA >= 11.2) + --split-compile (>= 12.1); scheduling-only,
   identical SASS. Helps large multi-kernel TUs; no effect on single-kernel TUs.

get_available_cpu_count moved to tilelang.utils.device to break the
autotuner<->jit import cycle; unit-tested in testing/python/utils.

Adds benchmark/compile_speed/ to reproduce the numbers: a zoo of realistic
inference kernels (GEMM, GQA attention, RMSNorm, SwiGLU, softmax) over
Qwen2.5/Llama-3 dims (~126 distinct cold kernels at the default scale), with a
before/after that reconstructs the pre-change baseline (lock-save + 32 workers)
in one command (3.1x on the H20).
@cklxx
cklxx force-pushed the perf/compile-speed branch from 6ada648 to 8627e8d Compare July 31, 2026 09:01

@SiriusNEO SiriusNEO left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM now, but I think the main gain is from removing the lock (which is useless now).

@SiriusNEO
SiriusNEO merged commit 478ab70 into tile-ai:main Aug 3, 2026
6 checks passed
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.

2 participants