Skip to content

[TRTLLM-14904][fix] Work around flashinfer 0.6.15 autotuner cache-key hash/eq inconsistency - #17165

Open
brnguyen2 wants to merge 1 commit into
NVIDIA:mainfrom
brnguyen2:fix/TRTLLM-14904-flashinfer-autotuner-key-main
Open

[TRTLLM-14904][fix] Work around flashinfer 0.6.15 autotuner cache-key hash/eq inconsistency#17165
brnguyen2 wants to merge 1 commit into
NVIDIA:mainfrom
brnguyen2:fix/TRTLLM-14904-flashinfer-autotuner-key-main

Conversation

@brnguyen2

@brnguyen2 brnguyen2 commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Description

flashinfer 0.6.15's DynamicTensorSpec defines a custom __hash__ that skips tensor_initializers (and hashes callables by identity) while keeping the dataclass-generated __eq__, which compares all fields by value. Callers such as trtllm_batch_decode_with_kv_cache_mla build a fresh TuningConfig per call with fresh initializer closures, so the lru_cache on AutoTuner._find_nearest_profile accumulates hash-equal but eq-unequal keys up to its 16384 cap. Every autotuner cache probe on the MLA decode path then walks the whole collision chain in Python __eq__ — a 17–19 ms host stall per eager MLA generation call. Mixed prefill+decode iterations run eagerly and pay this on every call; decode-only iterations replay CUDA graphs and are unaffected, which made it present as a prefill-side regression. flashinfer 0.6.14 built a fresh bucket mapper per call, so keys hashed differently and cache probes missed in O(1); the 0.6.14→0.6.15 bump (#16530) exposed the inconsistency.

This PR installs a guarded TRT-LLM-side workaround where the fmha flashinfer backend imports flashinfer: DynamicTensorSpec.__eq__ is replaced with one consistent with its existing __hash__ (ignore tensor_initializers, compare callables by identity). A behavioral probe applies the patch only when the inconsistency is actually present, and any failure degrades to not patching, so a future flashinfer release with different internals is unaffected.

The bug is upstream in flashinfer; an upstream issue is being filed, and this workaround should be dropped once a fixed release is picked up.

Same change as #17164 (feat/kimi_k3, where the regression was measured), cherry-picked onto main, which carries the same flashinfer 0.6.15 pin.

Test Coverage

  • Serving benchmark at high concurrency on a large MoE model with MLA attention: output throughput recovered ~15% (back to par with the pre-regression baseline band); low-concurrency throughput unchanged.
  • CPU microbenchmark of the autotuner cache probe: ~2 µs per call with cache size 1 post-patch, versus ~10 ms per call with an 8000-entry collision chain unpatched.

PR Checklist

  • PR title and description are self-explanatory
  • Commit message includes ticket ID and is signed off (DCO)
  • Change is guarded and degrades safely if flashinfer internals change

Dev Engineer Review

  • Added a FlashInfer compatibility workaround for the DynamicTensorSpec hash/equality inconsistency.
  • Applies the workaround only when a behavioral probe detects the inconsistency.
  • Compares callables by identity and ignores tensor_initializers, consistent with __hash__.
  • Logs and ignores patch failures to avoid affecting execution.
  • No public API declarations changed.
  • No test files, configuration files, or test-list files changed.
  • The workaround should be removed after FlashInfer provides a fixed release.

QA Engineer Review

No test changes.

… hash/eq inconsistency

flashinfer 0.6.15's DynamicTensorSpec defines a custom __hash__ that skips
tensor_initializers (and hashes callables by identity) while keeping the
dataclass-generated __eq__, which compares all fields by value. Callers such
as trtllm_batch_decode_with_kv_cache_mla build a fresh TuningConfig per call
with fresh initializer closures, so the lru_cache on
AutoTuner._find_nearest_profile accumulates hash-equal but eq-unequal keys up
to its 16384 cap. Every autotuner cache probe on the MLA decode path then
walks the whole collision chain in Python __eq__ — a 17-19 ms host stall per
eager MLA generation call, which cost ~15% output throughput in a serving
benchmark at high concurrency on a large MoE model (mixed
prefill+decode iterations run eagerly; decode-only iterations replay CUDA
graphs and are unaffected). 0.6.14 built a fresh bucket mapper per call, so
the keys hashed differently and missed in O(1).

Install a guarded TRT-LLM-side workaround where the fmha flashinfer backend
imports flashinfer: replace DynamicTensorSpec.__eq__ with one consistent with
its __hash__ (ignore tensor_initializers, compare callables by identity).
A behavioral probe applies the patch only when the inconsistency is present,
and any failure degrades to not patching, so a future flashinfer release with
different internals is unaffected. The bug is upstream in flashinfer; this
workaround should be dropped once a fixed version is picked up.

Validation: the serving benchmark recovered from ~5200 to ~6500 output
tok/s at concurrency 1024 (par with the pre-regression baseline band), with
low-concurrency throughput unchanged. A CPU microbenchmark of the cache
probe stays at ~2 us per call and cache size 1, versus ~10 ms per call with
an 8000-entry collision chain unpatched.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
@brnguyen2
brnguyen2 requested a review from a team as a code owner August 1, 2026 20:22
@brnguyen2
brnguyen2 marked this pull request as draft August 1, 2026 20:23
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

FlashInfer compatibility

Layer / File(s) Summary
Detect and apply DynamicTensorSpec patch
tensorrt_llm/_torch/attention_backend/fmha/flashinfer_trtllm_gen.py
The module probes for hash/equality inconsistency. When detected, it installs equality logic that matches existing hashing behavior and applies the patch during initialization. Failures are logged and ignored.

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

Suggested reviewers: bowenfu

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 follows the required ticket and type format and clearly identifies the FlashInfer cache-key inconsistency workaround.
Description check ✅ Passed The description explains the issue, solution, safety behavior, performance impact, testing, and relevant checklist items.
✨ 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: 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 `@tensorrt_llm/_torch/attention_backend/fmha/flashinfer_trtllm_gen.py`:
- Around line 134-137: Update the exception handler surrounding the flashinfer
DynamicTensorSpec.__eq__ workaround probe and replacement to catch only expected
compatibility errors, such as ImportError, AttributeError, and TypeError,
instead of Exception. Preserve the existing debug logging and fallback behavior
while allowing unexpected implementation defects to propagate.
- Around line 98-105: Add precise type annotations to the nested function
_probe_spec, including its DynamicTensorSpec return type. Also annotate self,
other, and the return value of _hash_consistent_eq, preserving the existing
comparison behavior; apply these changes at both referenced locations in
flashinfer_trtllm_gen.py (lines 98-105 and 111-129).
🪄 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: Enterprise

Run ID: 9ba2af7e-8beb-46f1-aa0a-22306b2f779f

📥 Commits

Reviewing files that changed from the base of the PR and between fdf7bd5 and 44533b9.

📒 Files selected for processing (1)
  • tensorrt_llm/_torch/attention_backend/fmha/flashinfer_trtllm_gen.py

Comment on lines +98 to +105
def _probe_spec():
return DynamicTensorSpec(
input_idx=(0,),
dim_idx=(0,),
gen_tuning_buckets=(1,),
map_to_tuning_buckets=_probe_spec, # any stable callable
tensor_initializers=[lambda shapes, dtype, device: None],
)

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add annotations to the nested functions.

  • tensorrt_llm/_torch/attention_backend/fmha/flashinfer_trtllm_gen.py#L98-L105: Add a precise return annotation to _probe_spec.
  • tensorrt_llm/_torch/attention_backend/fmha/flashinfer_trtllm_gen.py#L111-L129: Add precise annotations for self, other, and the return value of _hash_consistent_eq.

As per coding guidelines, “Annotate every function.”

📍 Affects 1 file
  • tensorrt_llm/_torch/attention_backend/fmha/flashinfer_trtllm_gen.py#L98-L105 (this comment)
  • tensorrt_llm/_torch/attention_backend/fmha/flashinfer_trtllm_gen.py#L111-L129
🤖 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 `@tensorrt_llm/_torch/attention_backend/fmha/flashinfer_trtllm_gen.py` around
lines 98 - 105, Add precise type annotations to the nested function _probe_spec,
including its DynamicTensorSpec return type. Also annotate self, other, and the
return value of _hash_consistent_eq, preserving the existing comparison
behavior; apply these changes at both referenced locations in
flashinfer_trtllm_gen.py (lines 98-105 and 111-129).

Source: Coding guidelines

Comment on lines +134 to +137
except Exception:
# A future flashinfer refactor (moved class, changed fields) must not
# break attention; it just loses the workaround.
logger.debug("Skipping flashinfer DynamicTensorSpec.__eq__ workaround.", exc_info=True)

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Catch only expected compatibility errors.

Line 134 hides defects in the probe and replacement implementation. Catch only expected import and API-shape errors, such as ImportError, AttributeError, and TypeError. This keeps the compatibility fallback while exposing unexpected defects.

As per coding guidelines, “Catch specific exceptions instead of using broad or bare exception handling such as except:.”

🧰 Tools
🪛 Ruff (0.16.0)

[warning] 134-134: 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 `@tensorrt_llm/_torch/attention_backend/fmha/flashinfer_trtllm_gen.py` around
lines 134 - 137, Update the exception handler surrounding the flashinfer
DynamicTensorSpec.__eq__ workaround probe and replacement to catch only expected
compatibility errors, such as ImportError, AttributeError, and TypeError,
instead of Exception. Preserve the existing debug logging and fallback behavior
while allowing unexpected implementation defects to propagate.

Sources: Coding guidelines, Linters/SAST tools

@brnguyen2
brnguyen2 marked this pull request as ready for review August 1, 2026 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant