Skip to content

[None][feat] Support dense FP8 LoRA end to end - #16810

Open
achartier wants to merge 5 commits into
NVIDIA:mainfrom
achartier:fp8-lora-dense-minimal
Open

[None][feat] Support dense FP8 LoRA end to end#16810
achartier wants to merge 5 commits into
NVIDIA:mainfrom
achartier:fp8-lora-dense-minimal

Conversation

@achartier

@achartier achartier commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Dev Engineer Review

  • Homogeneous PEFT cache dtype enforcement

    • Added PeftCacheManager internal homogeneous LoRA adapter dtype tracking:
      • New std::optional<DataType> state guarded by a mutex plus getDataType().
      • New configureDataType(DataType) validates dtype compatibility (FP8 only when ENABLE_FP8) and rejects mixed-dtype caching across requests; includes fallback to model datatype when unset.
    • Extended runtime::LoraCache with setDataType(DataType) / getDataType():
      • setDataType reinitializes only when cache is empty (prevents post-insert dtype changes).
      • Tightened dtype validation during copy operations and added a concurrency-sensitive host/device dtype consistency check.
    • Updated request validation API:
      • loraValidateRequestTensors(..., std::optional<DataType> loraDataType = std::nullopt) validates weights dtype against the provided cache dtype when specified.
    • Propagated PEFT cache dtype through bindings and LoRA execution:
      • nanobind exposes PeftCacheManager.data_type.
      • CUDA-graph LoRA parameter preparation now forwards "data_type" from the PEFT cache manager into Python.
  • Hopper/newer dense FP8 (E4M3) LoRA grouped-GEMM support + CUDA-graph dispatch

    • Added FP8 CUDA-graph grouped-GEMM dispatch using CUTLASS 3.x behind Hopper/modifiable-TMA support:
      • New kernels::kFp8TmaAlignment = 16.
      • Alignment + SM gating for FP8 CUDA-graph grouped GEMM (SM90+ and modulo-16 constraints on relevant problem dimensions / minKN).
      • Implements FP8 CUDA-graph grouped GEMM by packing per-problem shapes/strides into device parameter buffers and running CUTLASS grouped GemmUniversalAdapter.
    • Updated grouped-GEMM routing:
      • groupGemm.cu adds Hopper FP8 grouped-GEMM implementation and dispatch when dataType == kFP8 (with explicit compile-time guard errors when unsupported).
      • cuda_graph_grouped_gemm.cu intercepts FP8 for CUDA-graph grouped paths (cudaGraphGroupedGemm and cudaGraphSplitKGroupedGemm), otherwise failing with explicit guard messages.
      • splitkGroupGemm.cu forwards FP8 to the non-split-K grouped GEMM path.
  • FP8 LoRA execution correctness (dtype conversion/clamping, kernel-path selection, workspace sizing)

    • kernels/lora/lora.cpp:
      • FP8 build path treats cuBLAS GEMM parameter config as a no-op.
      • Extends GEMM workspace sizing to include FP8 grouped-GEMM requirements.
      • Forces useUnifiedGemm = false for FP8.
    • Python LoRA behavior:
      • Added add_lora_result(output, lora_result) to centralize dtype-correct LoRA accumulation.
      • LoraLayer.forward:
        • Returns None when lora_params is falsy.
        • If PEFT data_type differs from input dtype, clamps/casts float16/bfloat16/float32 inputs to FP8 e4m3 range before executing, and restores output dtype back to the original base dtype.
        • Uses FP8-specific CUDA-graph alignment validation for min_kn when activations are torch.float8_e4m3fn.
      • Replaced conditional LoRA merges in attention/MLP/linear/gated-MLP modules with unconditional add_lora_result usage.
    • THOP dtype mapping:
      • Under ENABLE_FP8, maps torch.float8_e4m3fn to DataType::kFP8 for relevant LoRA entry points and improves error messages to list FP8 as supported.
  • FP8 LoRA adapter loading restrictions/conversions

    • lora_manager.py adds Hopper-gated FP8 LoRA support:
      • Validates rank/input/output sizes are multiples of 16.
      • Enables a native Hopper FP8-kernel path only for dense, non-MoE modules; FP8-kernel path rejects DoRA (NotImplementedError).
      • Applies FP8 scaling via temporary BF16 conversion, applies LoRA scaling, clamps to finite FP8 e4m3 range, then converts back to FP8.

QA Engineer Review

Test code changes (files under tests/):

  • cpp/tests/unit_tests/batch_manager/peftCacheManagerTest.cpp
    • Added/validated:
      • unsupportedAdapterDataTypeDoesNotConfigureCache
      • (under ENABLE_FP8) invalidAdapterDoesNotConfigureCacheDataType
      • (under ENABLE_FP8) adapterSelectsHomogeneousCacheDataType
  • tests/unittest/_torch/lora/test_fp8_lora_grouped_gemm_regressions.py
    • Added new regression coverage for:
      • _validate_fp8_lora_cuda_graph_alignment pass/fail (including misaligned rank/dims expectations)
      • LoraLayer FP8 cache input clamping/casting and output dtype restoration
      • add_lora_result casting semantics (including None passthrough)
      • dtype mismatch rejection when PEFT-cache dtype vs activation dtype disagree
      • kernel-source dispatch regressions for CUDA-graph grouped GEMM (SM90+ gating, modulo-16 checks, split-K delegation, CUTLASS guard/error presence, and metadata usage assertions)
  • tests/unittest/_torch/modules/tests_lora_modules/test_qwen3_sanity.py
    • Updated LoRA adapter creation to choose lora_rank=16 when dtype=torch.float8_e4m3fn (otherwise lora_rank=8).
  • tests/unittest/others/test_lora_manager.py
    • Expanded FP8 LoRA test utilities and coverage:
      • Updated dummy adapter generation to support configurable output size, FP8 dtype, DoRA options, and MoE expert-indexed adapter generation helper.
      • Added/expanded FP8 test classes (e.g., TestLoraManagerFp8, TestLoraManagerFp8Alignment) covering DoRA rejection, MoE handling/conversion, dtype mismatch rejection, clamping/scaling behavior, and alignment checks (via patching torch.cuda.get_device_capability).

Coverage in tests/integration/test_lists/ (CBTS mapping):

  • Not modified by information provided; no integration test-list changes were detected from the available context.
  • Verdict: needs follow-up (CBTS/test-db/qa coverage mapping for the newly added FP8 alignment/dispatch unit tests wasn’t provided).

Description

This change keeps E4M3 LoRA adapter weights in FP8 from loading through execution on Hopper and newer GPUs.

The PEFT cache now selects one homogeneous dtype from the first adapter loaded while the cache is empty and rejects later adapters with a different dtype. That dtype is propagated through the C++ cache, nanobind, Python LoRA layers, and grouped-GEMM dispatch. For an FP8 cache, dense LoRA layers saturate and convert FP16, BF16, or FP32 activations to E4M3 before the grouped GEMM, then convert the LoRA result back to the caller activation dtype before accumulation.

The eager, split-K, and CUDA-graph grouped-GEMM implementations gain Hopper FP8 CUTLASS dispatch. The implementation requires E4M3 adapter weights, SM90 or newer, rank 16 or greater, and rank/input/output dimensions divisible by 16. Routed-expert MoE LoRA remains outside this PR.

No public configuration surface or new dependency is introduced.

Test Coverage

Validated on an H100 PCIe with an SM90 Release build:

  • PeftCacheManagerTest.adapterSelectsHomogeneousCacheDataType: 1 passed.
  • pytest tests/unittest/_torch/lora/test_fp8_lora_grouped_gemm_regressions.py: 16 passed. This covers cache dtype propagation, eager and CUDA-graph activation conversion, grouped-GEMM constraints, and FP8 delta accumulation into BF16 output.
  • TestQwen3LoRA::test_qwen3_fp8_lora: passed end to end against Qwen3-0.6B-FP8 with generated nonzero rank-16 FP8 adapters.
  • TestQwen3LoRA::test_qwen3_bf16_lora: passed as the neighboring BF16 adapter control against the same checkpoint.
  • Repository pre-commit hooks passed for the commit.

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using the CodeRabbit summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

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

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

@achartier
achartier force-pushed the fp8-lora-dense-minimal branch 3 times, most recently from 9db2adf to c47972f Compare July 27, 2026 21:12
@achartier
achartier marked this pull request as ready for review July 27, 2026 21:50
@achartier
achartier requested review from a team as code owners July 27, 2026 21:51
@coderabbitai

coderabbitai Bot commented Jul 27, 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

The changes add homogeneous LoRA dtype tracking, FP8 LoRA loading and validation, FP8 grouped GEMM and CUDA-graph execution, dtype propagation through Python bindings, shared LoRA result casting, and regression tests.

Changes

FP8 LoRA support

Layer / File(s) Summary
LoRA dtype cache validation
cpp/include/tensorrt_llm/{batch_manager,runtime}/*, cpp/tensorrt_llm/{batch_manager,runtime}/*, cpp/tensorrt_llm/nanobind/..., cpp/tests/unit_tests/batch_manager/*
PEFT and LoRA caches track configured dtypes, validate incoming tensors, expose dtype accessors, and reject mixed-dtype requests.
FP8 grouped GEMM execution
cpp/tensorrt_llm/kernels/*, cpp/tensorrt_llm/thop/loraOp.cpp
Grouped GEMM and CUDA-graph paths add FP8 alignment checks, workspace handling, CUTLASS TMA execution, dtype dispatch, and split-K fallback behavior.
Python FP8 LoRA integration
tensorrt_llm/_torch/modules/*, tensorrt_llm/_torch/peft/lora/*, tensorrt_llm/_torch/pyexecutor/*, tensorrt_llm/lora_manager.py
FP8 adapter loading, dtype propagation, alignment validation, input/output casting, and shared LoRA result merging are added across Python execution paths.
FP8 regression coverage
tests/unittest/_torch/lora/*, tests/unittest/_torch/modules/tests_lora_modules/*, tests/unittest/others/test_lora_manager.py
Tests cover FP8 alignment, dtype conversion, result merging, adapter loading behavior, kernel source invariants, and dtype-specific adapter ranks.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PEFTCacheManager
  participant PyExecutor
  participant LoraLayer
  participant groupedGemm
  PEFTCacheManager->>PyExecutor: expose data_type
  PyExecutor->>LoraLayer: pass LoRA parameters and data_type
  LoraLayer->>groupedGemm: execute aligned FP8 LoRA GEMM
  groupedGemm-->>LoraLayer: return FP8 LoRA result
  LoraLayer-->>PyExecutor: cast result to base output dtype
Loading

Suggested labels: api-compatible

Suggested reviewers: reasonsolo, mikeiovine, cascade812, tabrizian, zongfeijing

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% 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
Title check ✅ Passed The title matches the repository template and clearly summarizes the main change: dense FP8 LoRA end-to-end support.
Description check ✅ Passed The description includes the required Description, Test Coverage, and PR Checklist sections and is specific enough for review.
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: 8

🧹 Nitpick comments (8)
cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu (2)

315-329: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Unchecked CUDA API return codes.

cudaMemcpyAsync (x4) and cudaGetDevice return values are discarded, so a failure here surfaces later as a confusing CUTLASS/launch error instead of at the source. Wrap them in the existing TLLM_CUDA_CHECK helper.

🛡️ Proposed fix
-    cudaMemcpyAsync(devPtrA, ptrAGpu, problemCount * sizeof(void*), cudaMemcpyDeviceToDevice, stream);
-    cudaMemcpyAsync(devPtrB, ptrBGpu, problemCount * sizeof(void*), cudaMemcpyDeviceToDevice, stream);
-    cudaMemcpyAsync(devPtrC, ptrCGpu, problemCount * sizeof(void*), cudaMemcpyDeviceToDevice, stream);
-    cudaMemcpyAsync(devPtrD, ptrDGpu, problemCount * sizeof(void*), cudaMemcpyDeviceToDevice, stream);
+    TLLM_CUDA_CHECK(cudaMemcpyAsync(devPtrA, ptrAGpu, problemCount * sizeof(void*), cudaMemcpyDeviceToDevice, stream));
+    TLLM_CUDA_CHECK(cudaMemcpyAsync(devPtrB, ptrBGpu, problemCount * sizeof(void*), cudaMemcpyDeviceToDevice, stream));
+    TLLM_CUDA_CHECK(cudaMemcpyAsync(devPtrC, ptrCGpu, problemCount * sizeof(void*), cudaMemcpyDeviceToDevice, stream));
+    TLLM_CUDA_CHECK(cudaMemcpyAsync(devPtrD, ptrDGpu, problemCount * sizeof(void*), cudaMemcpyDeviceToDevice, stream));
@@
-    cudaGetDevice(&hwInfo.device_id);
+    TLLM_CUDA_CHECK(cudaGetDevice(&hwInfo.device_id));
🤖 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 `@cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu` around lines 315 - 329,
Wrap all four cudaMemcpyAsync calls and the cudaGetDevice call in the existing
TLLM_CUDA_CHECK helper, preserving their current arguments and ordering so CUDA
failures are reported at the originating operation.

50-71: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated FP8 alignment validation.

This is nearly identical to checkFp8GroupedGemmAlignment in cpp/tensorrt_llm/kernels/groupGemm.cu (lines 51-66), differing only in taking a raw pointer + count instead of a vector. Consider a single shared helper (e.g. declared in groupGemm.h, which this file already includes) taking GemmCoord const*, int, char const* and having the vector-based caller forward data()/size().

🤖 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 `@cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu` around lines 50 - 71,
Consolidate the duplicated FP8 alignment validation by moving the shared logic
from checkFp8CudaGraphAlignment and checkFp8GroupedGemmAlignment into a helper
declared in groupGemm.h that accepts GemmCoord const*, an integer count, and
kernelName. Update the vector-based caller to forward its data pointer and size,
and have both call sites use the shared helper while preserving the existing
validation behavior and diagnostics.
cpp/tensorrt_llm/kernels/groupGemm.cu (2)

429-437: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

cudaGetDevice return value is discarded.

hwInfo.device_id is pre-set to 0 and the cudaGetDevice result is ignored, so a failing call silently proceeds with device 0 and an SM count queried for the wrong device.

🛡️ Check the call
-    cutlass::KernelHardwareInfo hwInfo;
-    hwInfo.device_id = 0;
-    cudaGetDevice(&hwInfo.device_id);
+    cutlass::KernelHardwareInfo hwInfo;
+    hwInfo.device_id = 0;
+    TLLM_CUDA_CHECK(cudaGetDevice(&hwInfo.device_id));
     hwInfo.sm_count = cutlass::KernelHardwareInfo::query_device_multiprocessor_count(hwInfo.device_id);
🤖 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 `@cpp/tensorrt_llm/kernels/groupGemm.cu` around lines 429 - 437, Handle the
return status of cudaGetDevice before using hwInfo.device_id in the surrounding
GEMM setup. Propagate or return the CUDA error according to the enclosing
function’s existing error-handling convention, and only query the multiprocessor
count and construct Gemm::Arguments after successfully obtaining the active
device; do not fall back to the preset device 0.

94-108: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Unify FP8 workspace sizing with the launch layout The helper still hardcodes 12-byte problem shapes and 16-byte strides, while the launch path sizes the same buffers from sizeof(...). If CUTLASS changes either type, workspace allocation will undercount and fail at runtime; move the FP8 layout sizes behind a shared constexpr helper or add static_asserts on the CUTLASS types.

🤖 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 `@cpp/tensorrt_llm/kernels/groupGemm.cu` around lines 94 - 108, The
getFp8GroupedGemmParamsWorkSpaceSize helper hardcodes CUTLASS buffer sizes,
risking divergence from the launch layout. Replace the literal shape and stride
byte counts with sizes derived from the same CUTLASS types used by the FP8
launch path, preferably through a shared constexpr layout helper; otherwise add
static_asserts validating the assumptions while keeping workspace alignment and
pointer-array sizing unchanged.
cpp/tests/unit_tests/batch_manager/peftCacheManagerTest.cpp (1)

146-168: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test coverage summary.

Added: PeftCacheManagerTest.adapterSelectsHomogeneousCacheDataType (FP8-guarded) — covers dtype selection from the first adapter and rejection of a second adapter with a different dtype. No test functions were modified or removed, and C++ gtest cases are registered via CMake rather than the tests/integration/test_lists/ files, so no list updates apply here.

Verdict: sufficient for the dtype-selection path, with two gaps worth adding when convenient — (1) asserting the dtype actually reached the underlying host/device LoraCache (LoraCache::getDataType()), and (2) a case pinning behavior for a dtype that is neither the model dtype nor FP8, which is currently accepted (see the peftCacheManager.cpp comment).

🤖 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 `@cpp/tests/unit_tests/batch_manager/peftCacheManagerTest.cpp` around lines 146
- 168, Extend adapterSelectsHomogeneousCacheDataType to verify that the selected
dtype is propagated to the underlying host/device LoraCache by asserting
LoraCache::getDataType(). Add coverage for a non-model, non-FP8 dtype and pin
the currently accepted behavior described in peftCacheManager.cpp.
tests/unittest/others/test_lora_manager.py (1)

27-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

LGTM on the rest of the new FP8 helper/test additions (rank/dimension alignment coverage, MoE dtype-conversion coverage).

QA test coverage summary: Added TestLoraManagerFp8 (test_fp8_dora_is_rejected, test_fp8_moe_weights_are_converted_to_model_dtype) and TestLoraManagerFp8Alignment (test_misaligned_fp8_adapter_is_rejected_before_cuda_transfer, parametrized over rank/input/output misalignment). These are unittest-style tests under tests/unittest/others/, not the integration suite. Coverage verdict: needs follow-up — I cannot confirm from the provided context whether/how tests/unittest/others/test_lora_manager.py is registered in tests/integration/test_lists/test-db/ for CI execution; please confirm this file's list entry still covers the new FP8 test classes.

As per path instructions for tests/**: "Always produce a test coverage summary, even if no issues are found."

Also applies to: 53-124, 214-273

🤖 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 `@tests/unittest/others/test_lora_manager.py` at line 27, Confirm that
tests/unittest/others/test_lora_manager.py is registered in the relevant
tests/integration/test_lists/test-db/ entry so CI executes the new
TestLoraManagerFp8 and TestLoraManagerFp8Alignment classes. Add or update the
list entry if necessary, preserving coverage for the existing test file.

Source: Path instructions

tests/unittest/_torch/lora/test_fp8_lora_grouped_gemm_regressions.py (2)

124-188: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Kernel-facing tests validate source-code substrings, not runtime behavior.

These tests (test_fp8_cuda_graph_grouped_gemm_uses_live_device_problem_metadata, test_fp8_grouped_gemm_dispatch_has_explicit_unsupported_cutlass_guard, test_fp8_grouped_gemm_alignment_checks_require_multiples_of_16, test_fp8_splitk_grouped_gemm_delegates_to_regular_grouped_gemm, test_fp8_tma_alignment_has_one_cpp_definition, test_fp8_cuda_graph_alignment_check_requires_rank_multiple_of_16) read .cu/.h files as raw text and assert on specific substrings. They will break on harmless refactors (renames, reformatting, comment changes) and won't catch numerical/behavioral regressions that happen to preserve the checked strings. Consider replacing these with (or supplementing them with) actual GPU-executed regression tests of the grouped-GEMM kernels where feasible.

🤖 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 `@tests/unittest/_torch/lora/test_fp8_lora_grouped_gemm_regressions.py` around
lines 124 - 188, Replace the raw source-substring assertions in the listed FP8
grouped-GEMM tests with GPU-executed regression coverage that invokes the
relevant grouped-GEMM and CUDA-graph kernels and validates numerical results,
dispatch behavior, alignment handling, and unsupported CUTLASS behavior.
Preserve source-level checks only where runtime execution cannot verify the
requirement, especially the single-definition check for kFp8TmaAlignment, and
retain coverage for live device metadata and split-K delegation through
observable kernel behavior.

1-188: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Prefer behavior-level checks over raw source-string assertions — the FP8 Python tests are useful, but the .cu/.h substring checks are brittle and can miss real kernel regressions while breaking on harmless refactors.

  • Coverage: new tests cover FP8 cache casting, dtype-mismatch rejection, rank/shape alignment, and grouped-GEMM guard paths; no matches were found in tests/integration/test_lists/test-db/ or tests/integration/test_lists/qa/.
🤖 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 `@tests/unittest/_torch/lora/test_fp8_lora_grouped_gemm_regressions.py` around
lines 1 - 188, Replace the raw `.cu`/`.h` substring assertions in the
kernel-source tests with behavior-level tests that exercise the FP8 grouped-GEMM
and CUDA-graph dispatch paths, including alignment validation, unsupported
CUTLASS guards, live metadata usage, split-K delegation, and single-definition
expectations. Preserve the existing Python coverage while making tests fail only
when observable kernel behavior regresses rather than when source formatting or
implementation details change.
🤖 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 `@cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp`:
- Around line 265-272: Update the LoRA request flow around configureDataType and
loraValidateRequestTensors so validation occurs before configuring the cache
dtype, or otherwise enforce the model dtype plus supported FP8 dtype allowlist.
Ensure unsupported request dtypes are rejected against the model configuration
before the first request can pin host and device caches, while preserving
homogeneous dtype checks for subsequent requests.

In `@cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu`:
- Around line 365-387: Add a runtime device-capability check before the FP8
dispatch and unsupported-path handling in cudaGraphGroupedGemm, requiring SM90
or newer. Ensure both ENABLE_FP8 branches use this guard so older GPUs receive a
clear actionable error before invoking fp8CudaGraphGroupedGemm or the existing
unsupported-feature check.

In `@cpp/tensorrt_llm/kernels/groupGemm.cu`:
- Around line 48-68: Move checkFp8GroupedGemmAlignment under the same ENABLE_FP8
and CUTLASS_ARCH_MMA_MODIFIABLE_TMA_SM90_SUPPORTED preprocessor guards as its
only call site, ensuring it is not compiled in configurations where it cannot be
referenced.

In `@cpp/tensorrt_llm/thop/loraOp.cpp`:
- Around line 236-238: Fix both dtype validation default cases in
cpp/tensorrt_llm/thop/loraOp.cpp at lines 236-238 and 321-323, including
lora_group_gemm_param_fill_row_reorder_fusion, by removing the printf-style %s
placeholder from the TORCH_CHECK message and leaving a trailing separator before
c10::toString(dtype), so stream concatenation produces a correctly formatted
error.

In `@tensorrt_llm/_torch/peft/lora/layer.py`:
- Around line 507-520: Update the CUDA-graph key construction used by the LoRA
grouped-GEMM path to include the active adapter/rank state, especially the FP8
value derived by _validate_fp8_lora_cuda_graph_alignment from
cuda_graph_params.slot_ranks_host. Ensure graph replay distinguishes changes to
slot_ranks_host/min_kn and cannot reuse a capture with a stale FP8
specialization; keep the existing non-FP8 min_kn calculation unchanged.

In `@tensorrt_llm/lora_manager.py`:
- Around line 1081-1098: Restrict FP8 LoRA handling in
tensorrt_llm/lora_manager.py lines 1081-1098, around is_fp8 and use_fp8_kernel,
to torch.float8_e4m3fn only, and validate or cast t_in to match t_out before
using the FP8 kernel. In tensorrt_llm/_torch/peft/lora/layer.py lines 238-261,
make no direct change; retain its E4M3-only dtype handling as the source of
truth unless E5M2 support is explicitly added instead.
- Around line 1110-1113: Update the FP8 scaling path in the use_fp8_kernel
branch to clamp the BF16-scaled t_out values to the representable finite range
before casting back to t_out.dtype. Reuse the existing FP8 clamp behavior or
bounds established by LoraLayer.forward in layer.py, while preserving the
current BF16 scaling flow.

In `@tests/unittest/others/test_lora_manager.py`:
- Around line 86-89: Update the DoRA test setup in the use_dora branch to size
lora_magnitude_vector with output_size rather than hidden_size, ensuring the
tensor matches the layer’s per-output-channel dimension when output_size
differs.

---

Nitpick comments:
In `@cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu`:
- Around line 315-329: Wrap all four cudaMemcpyAsync calls and the cudaGetDevice
call in the existing TLLM_CUDA_CHECK helper, preserving their current arguments
and ordering so CUDA failures are reported at the originating operation.
- Around line 50-71: Consolidate the duplicated FP8 alignment validation by
moving the shared logic from checkFp8CudaGraphAlignment and
checkFp8GroupedGemmAlignment into a helper declared in groupGemm.h that accepts
GemmCoord const*, an integer count, and kernelName. Update the vector-based
caller to forward its data pointer and size, and have both call sites use the
shared helper while preserving the existing validation behavior and diagnostics.

In `@cpp/tensorrt_llm/kernels/groupGemm.cu`:
- Around line 429-437: Handle the return status of cudaGetDevice before using
hwInfo.device_id in the surrounding GEMM setup. Propagate or return the CUDA
error according to the enclosing function’s existing error-handling convention,
and only query the multiprocessor count and construct Gemm::Arguments after
successfully obtaining the active device; do not fall back to the preset device
0.
- Around line 94-108: The getFp8GroupedGemmParamsWorkSpaceSize helper hardcodes
CUTLASS buffer sizes, risking divergence from the launch layout. Replace the
literal shape and stride byte counts with sizes derived from the same CUTLASS
types used by the FP8 launch path, preferably through a shared constexpr layout
helper; otherwise add static_asserts validating the assumptions while keeping
workspace alignment and pointer-array sizing unchanged.

In `@cpp/tests/unit_tests/batch_manager/peftCacheManagerTest.cpp`:
- Around line 146-168: Extend adapterSelectsHomogeneousCacheDataType to verify
that the selected dtype is propagated to the underlying host/device LoraCache by
asserting LoraCache::getDataType(). Add coverage for a non-model, non-FP8 dtype
and pin the currently accepted behavior described in peftCacheManager.cpp.

In `@tests/unittest/_torch/lora/test_fp8_lora_grouped_gemm_regressions.py`:
- Around line 124-188: Replace the raw source-substring assertions in the listed
FP8 grouped-GEMM tests with GPU-executed regression coverage that invokes the
relevant grouped-GEMM and CUDA-graph kernels and validates numerical results,
dispatch behavior, alignment handling, and unsupported CUTLASS behavior.
Preserve source-level checks only where runtime execution cannot verify the
requirement, especially the single-definition check for kFp8TmaAlignment, and
retain coverage for live device metadata and split-K delegation through
observable kernel behavior.
- Around line 1-188: Replace the raw `.cu`/`.h` substring assertions in the
kernel-source tests with behavior-level tests that exercise the FP8 grouped-GEMM
and CUDA-graph dispatch paths, including alignment validation, unsupported
CUTLASS guards, live metadata usage, split-K delegation, and single-definition
expectations. Preserve the existing Python coverage while making tests fail only
when observable kernel behavior regresses rather than when source formatting or
implementation details change.

In `@tests/unittest/others/test_lora_manager.py`:
- Line 27: Confirm that tests/unittest/others/test_lora_manager.py is registered
in the relevant tests/integration/test_lists/test-db/ entry so CI executes the
new TestLoraManagerFp8 and TestLoraManagerFp8Alignment classes. Add or update
the list entry if necessary, preserving coverage for the existing test file.
🪄 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: 90fecfec-8652-4709-9502-58211eb2f1ef

📥 Commits

Reviewing files that changed from the base of the PR and between 9fe5853 and c47972f.

📒 Files selected for processing (27)
  • cpp/include/tensorrt_llm/batch_manager/peftCacheManager.h
  • cpp/include/tensorrt_llm/runtime/loraCache.h
  • cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp
  • cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu
  • cpp/tensorrt_llm/kernels/groupGemm.cu
  • cpp/tensorrt_llm/kernels/groupGemm.h
  • cpp/tensorrt_llm/kernels/lora/lora.cpp
  • cpp/tensorrt_llm/kernels/splitkGroupGemm.cu
  • cpp/tensorrt_llm/kernels/splitkGroupGemm.h
  • cpp/tensorrt_llm/nanobind/batch_manager/kvCacheManager.cpp
  • cpp/tensorrt_llm/runtime/loraCache.cpp
  • cpp/tensorrt_llm/runtime/loraUtils.cpp
  • cpp/tensorrt_llm/runtime/loraUtils.h
  • cpp/tensorrt_llm/thop/loraOp.cpp
  • cpp/tests/unit_tests/batch_manager/peftCacheManagerTest.cpp
  • tensorrt_llm/_torch/modules/attention.py
  • tensorrt_llm/_torch/modules/gated_mlp.py
  • tensorrt_llm/_torch/modules/linear.py
  • tensorrt_llm/_torch/modules/mlp.py
  • tensorrt_llm/_torch/peft/lora/cuda_graph_lora_manager.py
  • tensorrt_llm/_torch/peft/lora/layer.py
  • tensorrt_llm/_torch/pyexecutor/model_engine.py
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py
  • tensorrt_llm/lora_manager.py
  • tests/unittest/_torch/lora/test_fp8_lora_grouped_gemm_regressions.py
  • tests/unittest/_torch/modules/tests_lora_modules/test_qwen3_sanity.py
  • tests/unittest/others/test_lora_manager.py

Comment thread cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu
Comment thread cpp/tensorrt_llm/kernels/groupGemm.cu
Comment thread cpp/tensorrt_llm/thop/loraOp.cpp Outdated
Comment thread tensorrt_llm/_torch/peft/lora/layer.py
Comment thread tensorrt_llm/lora_manager.py Outdated
Comment thread tensorrt_llm/lora_manager.py Outdated
Comment thread tests/unittest/others/test_lora_manager.py
@achartier
achartier force-pushed the fp8-lora-dense-minimal branch from c47972f to be1231e Compare July 27, 2026 23:08

@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.

🧹 Nitpick comments (2)
cpp/tensorrt_llm/kernels/groupGemm.cu (2)

437-440: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

cudaGetDevice return value is discarded.

A failure here leaves hwInfo.device_id at 0 and the SM count query targeting the wrong device. Wrap it in the project's CUDA check macro.

♻️ Proposed fix
     cutlass::KernelHardwareInfo hwInfo;
-    hwInfo.device_id = 0;
-    cudaGetDevice(&hwInfo.device_id);
+    hwInfo.device_id = 0;
+    TLLM_CUDA_CHECK(cudaGetDevice(&hwInfo.device_id));
     hwInfo.sm_count = cutlass::KernelHardwareInfo::query_device_multiprocessor_count(hwInfo.device_id);
🤖 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 `@cpp/tensorrt_llm/kernels/groupGemm.cu` around lines 437 - 440, Update the
hardware-info initialization around cudaGetDevice so its return status is
checked with the project's CUDA check macro before querying multiprocessor
count. Preserve the existing hwInfo.device_id assignment and ensure
query_device_multiprocessor_count uses the validated active device.

102-116: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Workspace size math is duplicated with hard-coded element sizes.

getFp8GroupedGemmParamsWorkSpaceSize hard-codes 12/8/16-byte element sizes while fp8GroupedGemm (Lines 375-386) recomputes the same layout with sizeof(...). If any CUTLASS stride/shape type changes size, the two silently diverge and the mismatch only surfaces as a runtime TLLM_CHECK failure. Consider exposing the layout computation from one place (e.g. a small helper templated on the kernel types, or at least named k-prefixed constants shared by both), so the sizing and the consumer stay in lockstep.

As per coding guidelines, "Avoid magic literals except 0, nullptr, true, and false; initialize named constants instead, using k-prefixed camelCase names."

🤖 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 `@cpp/tensorrt_llm/kernels/groupGemm.cu` around lines 102 - 116, The workspace
sizing in getFp8GroupedGemmParamsWorkSpaceSize duplicates the layout sizes used
by fp8GroupedGemm. Centralize the element-size and alignment calculations in a
shared helper or k-prefixed named constants derived from the actual CUTLASS
shape, pointer, and stride types, then have both functions reuse that definition
so type-size changes cannot diverge and no magic byte literals remain.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@cpp/tensorrt_llm/kernels/groupGemm.cu`:
- Around line 437-440: Update the hardware-info initialization around
cudaGetDevice so its return status is checked with the project's CUDA check
macro before querying multiprocessor count. Preserve the existing
hwInfo.device_id assignment and ensure query_device_multiprocessor_count uses
the validated active device.
- Around line 102-116: The workspace sizing in
getFp8GroupedGemmParamsWorkSpaceSize duplicates the layout sizes used by
fp8GroupedGemm. Centralize the element-size and alignment calculations in a
shared helper or k-prefixed named constants derived from the actual CUTLASS
shape, pointer, and stride types, then have both functions reuse that definition
so type-size changes cannot diverge and no magic byte literals remain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6148191b-b52e-40de-ac45-1f9b0163edc3

📥 Commits

Reviewing files that changed from the base of the PR and between c47972f and be1231e.

📒 Files selected for processing (11)
  • cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp
  • cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu
  • cpp/tensorrt_llm/kernels/groupGemm.cu
  • cpp/tensorrt_llm/kernels/groupGemm.h
  • cpp/tensorrt_llm/kernels/splitkGroupGemm.cu
  • cpp/tensorrt_llm/runtime/loraCache.cpp
  • cpp/tensorrt_llm/thop/loraOp.cpp
  • cpp/tests/unit_tests/batch_manager/peftCacheManagerTest.cpp
  • tensorrt_llm/lora_manager.py
  • tests/unittest/_torch/lora/test_fp8_lora_grouped_gemm_regressions.py
  • tests/unittest/others/test_lora_manager.py
🚧 Files skipped from review as they are similar to previous changes (8)
  • cpp/tensorrt_llm/kernels/groupGemm.h
  • cpp/tensorrt_llm/thop/loraOp.cpp
  • tensorrt_llm/lora_manager.py
  • cpp/tensorrt_llm/kernels/splitkGroupGemm.cu
  • cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp
  • cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu
  • tests/unittest/_torch/lora/test_fp8_lora_grouped_gemm_regressions.py
  • cpp/tensorrt_llm/runtime/loraCache.cpp

@achartier

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62206 [ run ] triggered by Bot. Commit: be1231e Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62541 [ run ] completed with state SUCCESS. Commit: 430990c
/LLM/main/L0_MergeRequest_PR pipeline #50683 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

@brb-nv brb-nv 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.

Sorry, I'm not able to complete the review at once. One thing stood out to me for now.

Comment thread cpp/tensorrt_llm/kernels/cuda_graph_grouped_gemm.cu Outdated
@achartier

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62601 [ run ] triggered by Bot. Commit: 513573a Link to invocation

// ====================================================================

template <typename ProblemShape, typename StrideA, typename StrideB, typename StrideC, typename StrideD>
__global__ void fillFp8CudaGraphGroupedGemmParams(cutlass::gemm::GemmCoord const* problemSizesPtr, int problemCount,

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.

Is it possible to fuse this into loraGroupGEMMParamFillRowReorderFusionKernel so the latter directly builds strides?

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.

Fixed in ed215ab

auto* devStrideC = static_cast<StrideC*>(devPtr(szStrideC));
auto* devStrideD = static_cast<StrideD*>(devPtr(szStrideD));

cudaMemcpyAsync(devPtrA, ptrAGpu, problemCount * sizeof(void*), cudaMemcpyDeviceToDevice, stream);

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.

Are the D2D copies here necessary?

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.

Fixed in ed215ab

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62601 [ run ] completed with state FAILURE. Commit: 513573a
/LLM/main/L0_MergeRequest_PR pipeline #50741 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

@achartier

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62638 [ run ] triggered by Bot. Commit: ed215ab Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

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

@nvpohanh

Copy link
Copy Markdown
Collaborator

[by Codex] @SimengLiu-nv Friendly review reminder: this PR is awaiting your review. Thanks!

@brb-nv brb-nv 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.

Approving with a few comments. Do you think we should reject MoE fp8 early given you mentioned that would be a separate follow-up?

Comment thread tensorrt_llm/_torch/peft/lora/layer.py
Comment thread tensorrt_llm/lora_manager.py
@achartier
achartier force-pushed the fp8-lora-dense-minimal branch from ed215ab to cf109ae Compare August 2, 2026 20:20
@achartier

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63304 [ run ] triggered by Bot. Commit: cf109ae Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

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

@achartier

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63309 [ run ] triggered by Bot. Commit: cf109ae Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

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

Signed-off-by: Aurelien Chartier <2567591+achartier@users.noreply.github.com>
Signed-off-by: Aurelien Chartier <2567591+achartier@users.noreply.github.com>
Signed-off-by: Aurelien Chartier <2567591+achartier@users.noreply.github.com>
Signed-off-by: Aurelien Chartier <2567591+achartier@users.noreply.github.com>
Signed-off-by: Aurelien Chartier <2567591+achartier@users.noreply.github.com>
@achartier
achartier force-pushed the fp8-lora-dense-minimal branch from cf109ae to ad02cba Compare August 3, 2026 17:06
@achartier

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63507 [ run ] triggered by Bot. Commit: ad02cba Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

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

@achartier

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63536 [ run ] triggered by Bot. Commit: ad02cba Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

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

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.

5 participants