Skip to content

[None][fix] Warm up flashinfer sampling module during engine warmup - #17286

Open
qiaoxj07 wants to merge 4 commits into
NVIDIA:mainfrom
qiaoxj07:fix/flashinfer-sampling-jit-warmup
Open

[None][fix] Warm up flashinfer sampling module during engine warmup#17286
qiaoxj07 wants to merge 4 commits into
NVIDIA:mainfrom
qiaoxj07:fix/flashinfer-sampling-jit-warmup

Conversation

@qiaoxj07

@qiaoxj07 qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Description

flashinfer ships its sampling kernels as source and JIT-builds them with nvcc on first use. The only thing that exercises the non-greedy sampler during warmup today is the advanced-sampling CUDA graph capture pass (_run_capture_pass(force_non_greedy=True)), so with cuda_graph_config: null nothing triggers that build until the first non-greedy request is actually served.

The build then runs inside the executor loop and takes ~80–105s with a cold JIT cache. The cache lives under $HOME inside the container (/root/.cache/flashinfer/...), so it is cold on every job. While one rank compiles, it never launches its MoE all-to-all dispatch, so peer ranks GPU-spin on its completion flag and block in tp_gather. If the build crosses the 300s hang-detector threshold, the detector fires and aborts the whole job.

This was observed on a 16-node GB300 disaggregated DeepSeek-V4 run. The hang detector caught a CTX rank with this stack:

py_executor.py    _executor_loop_overlap -> _forward_step
model_engine.py   model_forward
sampler/ops/flashinfer.py:349  sample_from_logits_op
sampler/ops/flashinfer.py:232  top_k_mask_logits_op
flashinfer/sampling.py:1977    top_k_mask_logits
flashinfer/sampling.py:68      get_sampling_module
flashinfer/jit/core.py:316     build_and_load
flashinfer/jit/cpp_ext.py:368  run_ninja      <-- blocked in nvcc >300s

while its peers sat in _flush_pending_transfer_responses -> _enqueue_responses -> tp_gather.

Generation workers were unaffected in the same runs because they set cuda_graph_config.batch_sizes, which makes the advanced-sampling capture pass run and build the module during warmup.

Fix

Build the module during engine warmup instead, where the hang detector is not armed and no peer is waiting on this rank. get_sampling_module() covers every sampling kernel the sampler uses and is @functools.cached upstream, so this is a no-op once built.

Not gated on spec-decoding: the plain TorchSampler non-greedy path reaches the same kernels, so gating would leave that case exposed. Cost is one build at startup that would otherwise be paid on the first non-greedy request anyway.

Test Coverage

  • Verified the no-flashinfer branch returns cleanly before any flashinfer.* attribute access (the module binds a placeholder that raises ImportError on attribute access when flashinfer is absent).

  • Verified idempotency: repeated calls build once.

  • Cluster validation on two 4-node GB300 disagg runs (ctx cuda_graph_config: null + MTP), patch bind-mounted over the container's modules:

    unpatched (prod, 16-node) patched A patched B
    iterations reached died at iter 9 2255 2287
    mid-serving stall Bump onnx from 1.12.0 to 1.13.0 #1 98.2s 20.0s 19.2s
    mid-serving stall Add static libraries for batch manager #2 58.1s 10.6s
    Hang detected events yes → MPI_Abort 0 0

    The ninja: Entering .../cached_ops/sampling block now appears during warmup, ahead of the first warmup forward, and never during serving. Patched run B additionally carried TORCH_LOGS=recompiles: 111 recompiles were logged, all in the DeepSeek-V4 sparse-attention helpers, and none inside any stall window — so Dynamo/Inductor is not a contributor.

    Not a controlled A/B: validation runs are 4-node ctx1 + gen1 @ concurrency 224 vs. 16-node ctx4 + gen1 @ 1418 for the failures. See the validation comment for the full breakdown.

PR Checklist

  • Commit is signed off (DCO)
  • PR title follows [JIRA/NVBUG/None][type] description
  • One concern per PR

Dev Engineer Review

  • Added warmup_sampling_module() to eagerly initialize cached FlashInfer sampling kernels.
  • Calls the helper at the start of PyTorchModelEngine.warmup(), before early-return guards and other warmup stages.
  • Makes prewarming best-effort by logging initialization failures without aborting startup.
  • Preserves the no-FlashInfer path.
  • Prevents first-use NVCC JIT compilation in the executor loop.
  • No public API declarations, configuration files, or test-list files changed.
  • Validation covers missing FlashInfer handling, idempotency, warmup ordering, and cluster startup behavior.

QA Engineer Review

No test changes.

flashinfer ships its sampling kernels as source and JIT-builds them with
nvcc on first use. Today the only thing that exercises the non-greedy
sampler during warmup is the advanced-sampling CUDA graph capture pass,
so with cuda_graph_config=None nothing triggers that build until the
first non-greedy request is served.

The build then runs inside the executor loop and takes ~80-105s with a
cold JIT cache (the cache lives under $HOME inside the container, so it
is cold on every job). While one rank compiles it never launches its MoE
all-to-all dispatch, so peer ranks GPU-spin on its completion flag and
block in tp_gather. If the build crosses the 300s hang-detector
threshold the detector fires and aborts the job.

Observed on a 16-node GB300 disaggregated DeepSeek-V4 run: the hang
detector caught a CTX rank with sample_from_logits_op ->
top_k_mask_logits_op -> get_sampling_module -> build_and_load ->
run_ninja on its stack, while its peers sat in tp_gather.

Build the module during warmup instead, where the hang detector is not
armed and no peer is waiting on this rank. get_sampling_module() covers
every sampling kernel the sampler uses and is cached, so this is a no-op
once built.

Signed-off-by: Xianjie <5410381+qiaoxj07@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b400385c-f36e-4afa-b49f-99905133b700

📥 Commits

Reviewing files that changed from the base of the PR and between 25611d3 and a5dbfc9.

📒 Files selected for processing (1)
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py

Walkthrough

Changes

FlashInfer Sampling Warmup

Layer / File(s) Summary
Sampling module warmup helper
tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
Adds a compiler-disabled helper that skips unavailable FlashInfer, eagerly builds its sampling module, and logs initialization failures.
Engine warmup integration
tensorrt_llm/_torch/pyexecutor/model_engine.py
Imports the helper and invokes it before KV-cache validation and all warmup early returns, including when CUDA graphs are disabled.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: chienchunhung, tabrizian, yihuilu512

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the fix and the FlashInfer sampling warmup change.
Description check ✅ Passed The description explains the issue, fix, test coverage, validation results, and checklist items.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 `@tensorrt_llm/_torch/pyexecutor/model_engine.py`:
- Around line 1189-1192: Move the warmup_sampling_module() call in warmup()
above the non-HELIX context-parallel early-return guard, ensuring FlashInfer
initializes before any return path. Preserve the existing guard and the call’s
current warmup behavior for all engine configurations.
🪄 Autofix

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: Enterprise

Run ID: df1f93d0-0f97-4508-b240-2653e7c4fd60

📥 Commits

Reviewing files that changed from the base of the PR and between 89bba4c and e5939b6.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/pyexecutor/model_engine.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py

Comment thread tensorrt_llm/_torch/pyexecutor/model_engine.py Outdated

qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63957 [ run ] triggered by Bot. Commit: e5939b6 Link to invocation

…turns

warmup() returns early when no KV cache manager is allocated, and again
for non-HELIX context-parallel engines. Both returns preceded the
flashinfer warmup call, so those configurations still JIT-built the
sampling kernels during serving -- the exact failure this change fixes.

Neither guard has any bearing on whether the sampling kernels are
needed, and the warmup reads no engine state, so hoist it to the top of
warmup() where it runs on every path.

Signed-off-by: Xianjie <5410381+qiaoxj07@users.noreply.github.com>
@qiaoxj07

qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the CodeRabbit finding in 3546e88.

The call is now hoisted to the top of warmup(), above both early returns — the flagged non-HELIX context-parallel guard and the kv_cache_manager is None guard, which is the same class of problem. Neither guard has any bearing on whether the sampling kernels are needed, and warmup_sampling_module() reads no engine state, so it is safe on every path.

Cluster validation

Running now on a 4-node GB300 disagg config that reproduces the preconditions (ctx cuda_graph_config: null + MTP max_draft_len: 3, gen with pinned cuda_graph_config.batch_sizes). The patched files are bind-mounted over the container's own modules.

Early signal is what the fix predicts: the ninja: Entering .../cached_ops/sampling blocks now appear while the server is still starting up (5_wait_server at 240s elapsed, not yet healthy), instead of mid-serving. Will post the final iteration timings once it reaches steady state.

For reference, this is the stack the hang detector caught on an unpatched run, which is what motivated the change:

py_executor.py                 _executor_loop_overlap -> _forward_step
model_engine.py                model_forward
sampler/ops/flashinfer.py:349  sample_from_logits_op
sampler/ops/flashinfer.py:232  top_k_mask_logits_op
flashinfer/sampling.py:68      get_sampling_module
flashinfer/jit/core.py:316     build_and_load
flashinfer/jit/cpp_ext.py:368  run_ninja      <-- blocked in nvcc >300s

@qiaoxj07

qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63960 [ run ] triggered by Bot. Commit: 3546e88 Link to invocation

Comment thread tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py Outdated
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63957 [ run ] completed with state ABORTED. Commit: e5939b6

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63961 [ run ] triggered by Bot. Commit: 3546e88 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63960 [ run ] completed with state ABORTED. Commit: 3546e88

Link to invocation

@qiaoxj07

qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Cluster validation complete

Two 4-node GB300 disagg runs with this patch bind-mounted over the container's modules, on a config that reproduces the preconditions (ctx cuda_graph_config: null + MTP max_draft_len: 3).

Mid-serving stalls

unpatched (prod) patched A patched B
iterations reached died at iter 9 2255 2287
stall #1 98.2s (iter 6) 20.0s (iter 6) 19.2s (iter 6)
stall #2 58.1s (iter 7) 10.6s (iter 512)
fatal Hang detectedMPI_Abort 0 0
how it ended hang detector hit job_time hit job_time

~156s of multi-minute mid-serving stalls collapse to ~20s, and both patched runs served 2250+ iterations with zero hang-detector events. The unpatched run died at iteration 9.

The ninja: Entering .../cached_ops/sampling block appears during warmup in both runs, ahead of the first warmup forward, and never again during serving.

torch.compile ruled out

Patched run B also carried TORCH_LOGS=recompiles. It logged 111 recompiles, all in the DeepSeek-V4 sparse-attention helpers (_compute_compressed_mask, _compute_gen_compressed_position_ids, _prepare_deepseek_v4_indices_compiled, _compute_per_ratio_kv_lens). None fall inside any stall window — the windows are silent. So Dynamo/Inductor recompilation is not a contributor here.

Caveat

This is not a controlled A/B. The validation runs are 4-node ctx1 + gen1 at concurrency 224; the failing production runs are 16-node ctx4 + gen1 at concurrency 1418. What is demonstrated is that the patched configuration serves 2250+ iterations without the multi-minute stalls or the hang. A production-scale rerun would be needed to claim the failure rate itself is fixed.

The prewarm runs ahead of every guard in warmup(), so a broken JIT
toolchain would abort startup even for greedy-only deployments that
never reach these kernels. Log and continue instead, matching the
neighbouring pre-JIT warmups; deployments that do sample non-greedily
still surface the same error at the real call site.

The catch is broad because the build can fail as a subprocess error, a
missing-cache error, or a shared-object load error. The narrow catch used
by the DeepGEMM prewarm guards against signature drift, which does not
apply here -- get_sampling_module() takes no arguments.

Signed-off-by: Xianjie <5410381+qiaoxj07@users.noreply.github.com>
@qiaoxj07

qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Good catch — applied in 25611d3.

You're right that hoisting the call above the guards changed the blast radius: before, a broken JIT toolchain only surfaced at the first non-greedy request (and never at all for greedy-only deployments); after, it aborted startup for everyone. The prewarm is an optimization, so it should never be able to do that.

Now wrapped in try / logger.warning, and the docstring says why. Deployments that actually sample non-greedily still hit the same error at the real call site, so nothing is silently swallowed that matters.

On the exception breadth: I went with a broad catch rather than matching the DeepGEMM prewarm's narrow except RuntimeError. flashinfer's build can fail as a subprocess.CalledProcessError re-raised as RuntimeError (run_ninja), a MissingJITCacheError when FLASHINFER_DISABLE_JIT is set, or an OSError from tvm_ffi.load_module. The DeepGEMM comment justifies its narrowness by wanting signature drift to surface loudly — that doesn't apply here, since get_sampling_module() takes no arguments.

Signed-off-by: Xianjie <5410381+qiaoxj07@users.noreply.github.com>
@qiaoxj07

qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63998 [ run ] triggered by Bot. Commit: a5dbfc9 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63961 [ run ] completed with state ABORTED. Commit: 3546e88

Link to invocation

@qiaoxj07
qiaoxj07 enabled auto-merge (squash) August 5, 2026 12:24
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63998 [ run ] completed with state SUCCESS. Commit: a5dbfc9
/LLM/main/L0_MergeRequest_PR pipeline #51931 completed with status: 'FAILURE'

CI Report

⚠️ Multi-GPU Label Required:
Multi-GPU tests require the ci: full pre-merge approved label on this PR. Ask a member of NVIDIA/trt-llm-ci-approvers to add the label, then re-trigger CI with the same bot command (no rebase needed).

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@qiaoxj07

qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64053 [ run ] triggered by Bot. Commit: a5dbfc9 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64053 [ run ] completed with state FAILURE. Commit: a5dbfc9
/LLM/main/L0_MergeRequest_PR pipeline #51983 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@qiaoxj07

qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64075 [ run ] triggered by Bot. Commit: a5dbfc9 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64075 [ run ] completed with state FAILURE. Commit: a5dbfc9
/LLM/main/L0_MergeRequest_PR pipeline #52003 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@qiaoxj07

qiaoxj07 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64118 [ run ] triggered by Bot. Commit: a5dbfc9 Link to invocation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants