Skip to content

[TRTLLM-10288][perf] Reduce AutoTuner host overhead in inference hot path - #13116

Closed
hyukn wants to merge 2 commits into
NVIDIA:mainfrom
hyukn:autotuner-host-overhead-opt-v2
Closed

[TRTLLM-10288][perf] Reduce AutoTuner host overhead in inference hot path#13116
hyukn wants to merge 2 commits into
NVIDIA:mainfrom
hyukn:autotuner-host-overhead-opt-v2

Conversation

@hyukn

@hyukn hyukn commented Apr 16, 2026

Copy link
Copy Markdown
Collaborator

Description

Reduces AutoTuner host overhead in the inference hot path. Profiling on B200 showed the choose_one()search_cache()get_cache_key() path adds significant Python overhead on every custom op call. This PR eliminates the bulk of it.

Changes

Phase 1: WAR Optimizations

  • Cached key prefix in get_cache_key() (avoids recomputing __class__.__name__ + str(unique_id()) per call)
  • Singleton runner caches for nvfp4_gemm and quantize_e4m3_per_tensor

Phase 2: _choose_one_cache fast path in choose_one()

  • _choose_one_cache: maps (custom_op, runner_ids, *bucketed_dims)(runner_id, tactic). On cache hit: one _make_fast_key() + one dict.get(), then resolve runner from caller's list.
  • _make_fast_key(): lightweight key using only dynamic dimensions + runner identity (_cache_key_prefix).
  • dispatch(): simplified to thin wrapper over choose_one() — delegates all caching to _choose_one_cache.
  • TunableRunner.bind_tactic(): returns callable pre-bound to a specific tactic. Subclasses pre-resolve sub-runners.
  • Converted 21 callsites to dispatch() across torch_custom_ops.py (12) and cute_dsl_custom_ops.py (9).

Cache collision fix (runner identity in key)

  • Cache key includes runner_ids (tuple of each runner's _cache_key_prefix) so that different runner configurations for the same custom_op + dimensions don't collide (e.g. int8 vs int4 weight_only_quant_gemm runners).
  • Cache stores (runner_id, tactic) instead of runner instances — resolution always uses caller's runners list.

Profiling Results

Clean wall-clock measurements on B200, nvfp4_gemm (M=128, K=4096, N=4096, CUTLASS, 500 iters, no nsys/NVTX overhead):

Path Total/call Overhead vs Original
Original (choose_one + runner ctor) ~30.1us +12.7us baseline
This PR (choose_one cache hit, via @custom_op) 25.2us +7.8us saves 4.9us (1.20x)
Raw kernel floor (dispatch_fn direct) 17.4us

Overhead breakdown

Component Original This PR
Python dispatch logic 6.2us 1.3us (79% reduction)
torch.ops @custom_op framework tax 6.5us 6.5us (unchanged)
Total overhead 12.7us 7.8us

The `_choose_one_cache` reduces Python overhead from 6.2us to 1.3us by replacing per-call runner construction + `choose_one()` tactic search with `_make_fast_key()` + `dict.get()`.

The remaining 6.5us is the `@torch.library.custom_op` framework dispatch cost (DispatchKeySet traversal, auto-functionalization, schema validation). This is addressable separately via `fast_custom_op` (PR #13149), which reduces it to 2.3us.

Per-step impact (40 tunable ops)

Config Overhead/step
Original 1.21ms
This PR 1.01ms (-16%)
This PR + fast_custom_op (#13149) 0.84ms (-30%)

Correctness Verification

Cache collision fix

Single-GPU collision repro (B200, `weight_only_quant_gemm`, same dims 4096x7168x2112):

  • Before fix: int8 runner cached, int4 call returns wrong runner → `RuntimeError: size of tensor a (1056) must match size of tensor b (2112)`
  • After fix: Both int8 and int4 pass — runner identity in cache key prevents collision

Unit tests

Test Suite Result
Core AutoTuner (test_autotuner.py) All passed
FP4 Linear (test_fp4_linear.py) 8 passed
Fused MoE (test_fused_moe.py) Passed

Files Modified

File Change
`tensorrt_llm/_torch/autotuner.py` `_choose_one_cache` in choose_one(), simplified dispatch(), `_make_fast_key()` with runner identity, `bind_tactic()`
`tensorrt_llm/_torch/custom_ops/torch_custom_ops.py` Runner caches, 12 callsites → dispatch()
`tensorrt_llm/_torch/custom_ops/cute_dsl_custom_ops.py` 9 callsites → dispatch()
`tests/unittest/_torch/misc/test_autotuner.py` +2 tests: cache hit/invalidation, collision prevention

PR Checklist

  • Please check this after reviewing the above items as appropriate for this PR.

@hyukn
hyukn requested a review from a team as a code owner April 16, 2026 07:52
@hyukn
hyukn requested a review from liji-nv April 16, 2026 07:52
@hyukn hyukn changed the title [None][perf] Reduce AutoTuner host overhead in inference hot path [TRTLLM-10288][perf] Reduce AutoTuner host overhead in inference hot path Apr 16, 2026
@hyukn

hyukn commented Apr 16, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@coderabbitai

coderabbitai Bot commented Apr 16, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The changes introduce performance optimizations in the autotuner and custom operations modules. The autotuner adds a memoized cache key prefix property and implements a fast path for non-tuning mode inference to skip unnecessary capture/replay branching. The custom ops module applies singleton pattern caching to avoid redundant instantiations of GEMM and quantization runners.

Changes

Cohort / File(s) Summary
Autotuner Optimization
tensorrt_llm/_torch/autotuner.py
Added TunableRunner._cache_key_prefix property to memoize cache key components. Updated AutoTunerProfilingCache.get_cache_key() to use the memoized prefix and call AutoTuner._find_nearest_profile() directly. Refactored AutoTuner.choose_one() with a fast path for non-tuning mode inference that bypasses capture/replay branching and performs early cache lookup with fallback logging.
Custom Ops Singleton Caching
tensorrt_llm/_torch/custom_ops/torch_custom_ops.py
Implemented module-level singleton caching for NVFP4GemmUnifiedRunner keyed by (to_userbuffers, output_dtype, backends_list) in nvfp4_gemm(). Added singleton caching for QuantizeE4M3PerTensorRunner via _get_quantize_e4m3_runner() helper in quantize_e4m3_per_tensor().

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AutoTuner
    participant Cache as AutoTunerProfilingCache
    participant Runner as TunableRunner

    Client->>AutoTuner: choose_one() [inference, not tuning]
    activate AutoTuner
    
    alt Fast Path [is_tuning_mode=false, no active capture]
        AutoTuner->>Cache: get_cache_key(runner)
        activate Cache
        Cache->>Runner: _cache_key_prefix
        activate Runner
        Runner-->>Cache: memoized (class_name, unique_id)
        deactivate Runner
        Cache-->>AutoTuner: cache_key
        deactivate Cache
        
        AutoTuner->>AutoTuner: lookup cache by key
        alt Cache Hit
            AutoTuner-->>Client: (best_runner, best_tactic)
        else Cache Miss
            AutoTuner->>AutoTuner: log fallback warning
            AutoTuner-->>Client: (best_runner, best_tactic)
        end
    else Slower Path [tuning mode or capture active]
        AutoTuner->>AutoTuner: capture/replay logic
        AutoTuner-->>Client: (best_runner, best_tactic)
    end
    
    deactivate AutoTuner
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The pull request title clearly and concisely summarizes the main change: reducing AutoTuner host overhead during inference, which aligns with the primary focus of the changeset.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering the motivation, changes, profiling results, correctness verification, and test coverage as required by the template.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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

🧹 Nitpick comments (2)
tensorrt_llm/_torch/autotuner.py (1)

256-269: Annotate the new cached property.

The new _cache_key_prefix property should declare its return type, e.g. -> tuple[str, str].

Suggested tweak
     `@property`
-    def _cache_key_prefix(self):
+    def _cache_key_prefix(self) -> tuple[str, str]:
As per coding guidelines "Always annotate Python function return types; use None if the function does not return anything."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tensorrt_llm/_torch/autotuner.py` around lines 256 - 269, Annotate the new
cached property _cache_key_prefix with an explicit return type; change its
signature to declare it returns a tuple of two strings (e.g., -> tuple[str,
str]) so static type checkers and linters know the expected type, leaving the
implementation using self.__cache_key_prefix and str(self.unique_id())
unchanged.
tensorrt_llm/_torch/custom_ops/torch_custom_ops.py (1)

2237-2242: Annotate the new helper’s return type.

_get_quantize_e4m3_runner() is newly added and should declare -> QuantizeE4M3PerTensorRunner to match the repo’s Python typing rules.

Suggested tweak
-def _get_quantize_e4m3_runner():
+def _get_quantize_e4m3_runner() -> QuantizeE4M3PerTensorRunner:
As per coding guidelines "Always annotate Python function return types; use None if the function does not return anything."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tensorrt_llm/_torch/custom_ops/torch_custom_ops.py` around lines 2237 - 2242,
Add a return type annotation to the singleton accessor: change the signature of
_get_quantize_e4m3_runner so it declares "-> QuantizeE4M3PerTensorRunner".
Update the function definition for _get_quantize_e4m3_runner() to include this
return type (use a forward-reference string if QuantizeE4M3PerTensorRunner is
not yet defined in the file) so it conforms to the repo's typing rules.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tensorrt_llm/_torch/autotuner.py`:
- Around line 913-926: The fast path currently builds input_shapes by iterating
inputs and calling t.shape, which can raise AttributeError when callers pass
None placeholders (see choose_one callers); replace that logic by calling
self._get_input_sizes(inputs) to compute input_sizes safely and use that tuple
in the profiling_cache.search_cache call and log message (keep the rest of the
flow intact: guard on self.is_tuning_mode and self._active_capture, call
profiling_cache.search_cache with custom_op, runners, the result of
self._get_input_sizes(inputs), tuning_config, apply_map_to_tuning_buckets=True,
then use best_runner_id/best_tactic as before).

---

Nitpick comments:
In `@tensorrt_llm/_torch/autotuner.py`:
- Around line 256-269: Annotate the new cached property _cache_key_prefix with
an explicit return type; change its signature to declare it returns a tuple of
two strings (e.g., -> tuple[str, str]) so static type checkers and linters know
the expected type, leaving the implementation using self.__cache_key_prefix and
str(self.unique_id()) unchanged.

In `@tensorrt_llm/_torch/custom_ops/torch_custom_ops.py`:
- Around line 2237-2242: Add a return type annotation to the singleton accessor:
change the signature of _get_quantize_e4m3_runner so it declares "->
QuantizeE4M3PerTensorRunner". Update the function definition for
_get_quantize_e4m3_runner() to include this return type (use a forward-reference
string if QuantizeE4M3PerTensorRunner is not yet defined in the file) so it
conforms to the repo's typing rules.
🪄 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: 2a4397f1-8afe-42e4-a9e3-dba4d7126e53

📥 Commits

Reviewing files that changed from the base of the PR and between ac9ea3c and e8844ce.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/autotuner.py
  • tensorrt_llm/_torch/custom_ops/torch_custom_ops.py

Comment thread tensorrt_llm/_torch/autotuner.py Outdated
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #43715 [ run ] triggered by Bot. Commit: e8844ce Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #43715 [ run ] completed with state FAILURE. Commit: e8844ce
/LLM/main/L0_MergeRequest_PR pipeline #34198 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

Link to invocation

@hyukn
hyukn force-pushed the autotuner-host-overhead-opt-v2 branch 5 times, most recently from 0a69051 to 1e313ca Compare April 23, 2026 04:50
@hyukn

hyukn commented Apr 24, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #45275 [ run ] triggered by Bot. Commit: 1e313ca Link to invocation

@hyukn

hyukn commented Apr 24, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #45348 [ run ] triggered by Bot. Commit: 1e313ca Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #45348 [ run ] completed with state SUCCESS. Commit: 1e313ca
/LLM/main/L0_MergeRequest_PR pipeline #35595 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

Link to invocation

@hyukn
hyukn force-pushed the autotuner-host-overhead-opt-v2 branch 2 times, most recently from 19f4cce to d57fd9a Compare April 27, 2026 09:57
@hyukn

hyukn commented Apr 27, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #45708 [ run ] triggered by Bot. Commit: d57fd9a Link to invocation

@hyukn

hyukn commented Apr 28, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #45708 [ run ] completed with state SUCCESS. Commit: d57fd9a
/LLM/main/L0_MergeRequest_PR pipeline #35910 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

Link to invocation

@hyukn

hyukn commented Apr 29, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

1 similar comment
@hyukn

hyukn commented Apr 30, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@hyukn
hyukn force-pushed the autotuner-host-overhead-opt-v2 branch from d57fd9a to 40593c6 Compare April 30, 2026 09:25
@hyukn

hyukn commented Apr 30, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #46355 [ run ] triggered by Bot. Commit: 40593c6 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #46355 [ run ] completed with state ABORTED. Commit: 40593c6

Link to invocation

@hyukn
hyukn force-pushed the autotuner-host-overhead-opt-v2 branch from 40593c6 to 7e9ca65 Compare May 6, 2026 00:52
@hyukn

hyukn commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #46880 [ run ] triggered by Bot. Commit: 7e9ca65 Link to invocation

@hyukn
hyukn force-pushed the autotuner-host-overhead-opt-v2 branch from 7e9ca65 to e609b32 Compare May 6, 2026 07:57
@hyukn

hyukn commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #46948 [ run ] triggered by Bot. Commit: e609b32 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #46948 [ run ] completed with state FAILURE. Commit: e609b32

Link to invocation

@hyukn

hyukn commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #46971 [ run ] triggered by Bot. Commit: e609b32 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #46971 [ run ] completed with state FAILURE. Commit: e609b32

Link to invocation

@hyukn
hyukn force-pushed the autotuner-host-overhead-opt-v2 branch from e609b32 to f741c77 Compare May 7, 2026 01:11
@hyukn

hyukn commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #47064 [ run ] triggered by Bot. Commit: f741c77 Link to invocation

Comment thread tensorrt_llm/_torch/autotuner.py Outdated
return (runners[best_runner_id], best_tactic)

# Cache miss — resolve via search_cache, populate cache
input_shapes = tuple(self._get_input_sizes(inputs))

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.

Duplicate code for the tuning mode and non tuning mode.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The duplication is intentional — the two paths serve different purposes and have different lifetimes:

  1. Non-tuning path (the new cache): search_cache here is only hit once per unique shape (on _choose_one_cache miss). After warmup, this code is effectively dead — all subsequent calls hit the fast dict.get() above and return immediately. Storing the result in _choose_one_cache is what makes this a one-time cost.

  2. Tuning path (existing code): search_cache here runs every iteration during active profiling and feeds into the "should we re-profile?" decision logic below it. It cannot use _choose_one_cache because the cache is cleared/invalid during tuning.

I considered extracting a shared helper, but it would need to return different things for each path (the non-tuning path just needs (runner_id, tactic) to cache; the tuning path also needs is_cache_hit and min_time for the profiling decision). The 4-line search_cache call is straightforward enough that a shared helper would add indirection without meaningful deduplication.

Happy to add a brief comment in the code clarifying why both exist if that would help readability.

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #47064 [ run ] completed with state SUCCESS. Commit: f741c77
/LLM/main/L0_MergeRequest_PR pipeline #37036 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

…path

Phase 1 — WAR optimizations:
- Cache runner key prefix (_cache_key_prefix) to avoid recomputing
  class name + unique_id string on every call
- Fast path in choose_one() for non-tuning mode
- Singleton runner caches for nvfp4_gemm and quantize_e4m3_per_tensor

Phase 2 — dispatch() + bind_tactic():
- Fused resolve+execute via AutoTuner.dispatch() with _dispatch_cache
- _make_fast_key() for lightweight keying on dynamic dimensions
- TunableRunner.bind_tactic() returns callable bound to a tactic
- Converted 21 callsites to dispatch(); 10 remain as choose_one()
  where tactic value is needed

Profiling (B200, nvfp4_gemm, CUTLASS, N=1000):
  dispatch() cache hit: +1.08us overhead (saves 3.24us vs old path)

Signed-off-by: Yukun He <23156053+hyukn@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hyukn
hyukn force-pushed the autotuner-host-overhead-opt-v2 branch from f741c77 to e831ea6 Compare May 13, 2026 08:23
Move the fast-path cache from dispatch() into choose_one() so all
callers benefit. The cache key now includes runner_ids (each runner's
_cache_key_prefix) to prevent collisions when different runner
configurations share the same custom_op name and bucketed dimensions
(e.g. int8 vs int4 weight_only_quant_gemm runners). Cache stores
(runner_id, tactic) instead of runner instances, resolving via the
caller's runners list on hit.

Simplify dispatch() to a thin wrapper over choose_one(). Delete the
separate _dispatch_cache.

Signed-off-by: Yukun He <23156053+hyukn@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hyukn
hyukn force-pushed the autotuner-host-overhead-opt-v2 branch from e831ea6 to a3c0cb0 Compare May 13, 2026 08:44
@hyukn

hyukn commented May 13, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #48216 [ run ] triggered by Bot. Commit: a3c0cb0 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #48216 [ run ] completed with state SUCCESS. Commit: a3c0cb0
/LLM/main/L0_MergeRequest_PR pipeline #38034 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

@hyukn

hyukn commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #48315 [ run ] triggered by Bot. Commit: a3c0cb0 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #48315 [ run ] completed with state SUCCESS. Commit: a3c0cb0
/LLM/main/L0_MergeRequest_PR pipeline #38123 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

@hyukn hyukn closed this Jul 14, 2026
@hyukn

hyukn commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator Author

The optimization will affect some of the tuning process and may hurt the overall inference perf.

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