Skip to content

feat(moe): enable MxInt4 in unified API - #4320

Merged
feih-nv merged 4 commits into
flashinfer-ai:mainfrom
feih-nv:feih/unified-moe-mxint4
Aug 4, 2026
Merged

feih-nv merged 4 commits into
flashinfer-ai:mainfrom
feih-nv:feih/unified-moe-mxint4

Conversation

@feih-nv

@feih-nv feih-nv commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

📌 Description

Enable TensorRT-LLM MxInt4 execution through FlashInfer’s unified MoE API.

This change adds production BF16-to-MxInt4 weight preparation through TrtllmMxInt4Config.prepare_weights(), including signed INT4 packing, BF16 block scales, gated-activation row permutation, scale interleaving, and BlockMajorK conversion.

It also adds and registers TrtllmMxInt4RoutedRunner, supporting:

  • Packed precomputed routing
  • MxInt4 FromLogits support with BF16 router logits
  • Expert-parallel local expert offsets
  • Unified MoELayer dispatch and autotuning
  • CUDA graph capture and replay

The unified MoE fuzzer now includes an MxInt4 handler, a quantization-aware reference, and curated packed and FromLogits seeds.

🚀 Pull Request Checklist

Thank you for contributing to FlashInfer! Before we review your pull request, please make sure the following items are complete.

✅ Pre-commit Checks

  • I have installed pre-commit by running pip install pre-commit (or used your preferred method).
  • I have installed the hooks with pre-commit install.
  • I have run the hooks manually with pre-commit run --all-files and fixed any reported issues.

If you are unsure about how to set up pre-commit, see the pre-commit documentation.

🧪 Tests

  • Tests have been added or updated as needed.
  • All relevant tests are passing.

SM100

  • Unified MxInt4 suite: 30 passed
  • Curated unified MoE fuzzer seeds: 2 passed
  • Existing flat routed regressions: 4 passed

SM107

  • Unified MxInt4 suite: 30 passed
  • Curated unified MoE fuzzer seeds: 2 passed

Summary by CodeRabbit

  • New Features

    • Added TRTLLM MxInt4 support for unified Mixture-of-Experts layers.
    • Added BF16 activation and packed INT4 weight preparation for supported routed architectures.
    • Added precomputed and in-kernel routing, autotuning, and CUDA graph compatibility.
    • Added validation for supported data types, shapes, devices, and weight layouts.
  • Bug Fixes

    • Improved permutation cache accuracy by accounting for all relevant configuration settings.
  • Tests

    • Expanded fuzz and integration coverage for MxInt4 quantization, routing, validation, and execution.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Aug 3, 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 449172f6-34d0-4188-962f-d021e92c7e08

📥 Commits

Reviewing files that changed from the base of the PR and between c4415b286c1bac30a91c9d08a9b3d39d18418a29 and 66ea1df.

📒 Files selected for processing (8)
  • flashinfer/fused_moe/__init__.py
  • flashinfer/fused_moe/api.py
  • flashinfer/fused_moe/core.py
  • flashinfer/fused_moe/layer.py
  • flashinfer/fused_moe/prepare.py
  • flashinfer/fused_moe/runners.py
  • tests/moe/test_unified_moe_fuzz.py
  • tests/moe/test_unified_moe_mxint4.py
🚧 Files skipped from review as they are similar to previous changes (8)
  • flashinfer/fused_moe/init.py
  • flashinfer/fused_moe/layer.py
  • tests/moe/test_unified_moe_fuzz.py
  • flashinfer/fused_moe/core.py
  • flashinfer/fused_moe/prepare.py
  • tests/moe/test_unified_moe_mxint4.py
  • flashinfer/fused_moe/runners.py
  • flashinfer/fused_moe/api.py

📝 Walkthrough

Walkthrough

The PR adds unified TRTLLM MxInt4 MoE support. It prepares packed INT4 weights, registers a routed runner, supports packed and FromLogits routing, and adds fuzz and GPU test coverage.

Changes

Unified TRTLLM MxInt4 MoE

Layer / File(s) Summary
MxInt4 contract and dispatch
flashinfer/fused_moe/api.py, flashinfer/fused_moe/layer.py, flashinfer/fused_moe/__init__.py
Adds architecture support details, prepare_weights, runner registration, and public exports.
MxInt4 weight preparation
flashinfer/fused_moe/prepare.py, flashinfer/fused_moe/core.py
Quantizes BF16 weights into packed INT4, applies cached permutations, assembles BlockMajorK tensors, and expands permutation cache keys.
MxInt4 routed execution
flashinfer/fused_moe/runners.py
Adds input and weight validation, packed and FromLogits routing, TRTLLM input construction, execution delegation, and tactic configuration.
MxInt4 validation and coverage
tests/moe/test_unified_moe_fuzz.py, tests/moe/test_unified_moe_mxint4.py
Covers quantization, layouts, routing, reference agreement, validation, supported architectures, autotuning, dimension constraints, and CUDA graph replay.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MoELayer
  participant TrtllmMxInt4RoutedRunner
  participant TRTLLMMoERunner
  MoELayer->>TrtllmMxInt4RoutedRunner: pack activations, routing, and weights
  TrtllmMxInt4RoutedRunner->>TRTLLMMoERunner: construct inputs and launch parameters
  TRTLLMMoERunner-->>TrtllmMxInt4RoutedRunner: return MoE output
  TrtllmMxInt4RoutedRunner-->>MoELayer: return output buffer
Loading

Possibly related PRs

Suggested labels: run-ci

Suggested reviewers: aneureka, iwakurarein, jiahanc

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.63% 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 clearly and concisely identifies the main change: enabling MxInt4 in the unified MoE API.
Description check ✅ Passed The description explains the implementation, lists key capabilities, documents completed checks, and reports relevant passing tests.
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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@feih-nv
feih-nv marked this pull request as ready for review August 3, 2026 10:03
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@feih-nv

feih-nv commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run tests/moe

@feih-nv

feih-nv commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@flashinfer-bot

Copy link
Copy Markdown
Collaborator

GitLab MR !1107 has been created, and the CI pipeline #60838824 is currently running. I'll report back once the pipeline job completes.

@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 (1)
flashinfer/fused_moe/prepare.py (1)

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

Pack the nibbles in an unsigned dtype.

quantized is int8. (quantized[..., 1] & 0x0F) << 4 produces values up to 240, which does not fit in int8. The final .to(torch.uint8) restores the intended byte, so the current result is correct, but the code depends on signed-overflow wrap behavior in the shift. Cast to uint8 before the shift to make the packing well-defined.

♻️ Proposed refactor
     quantized = (
         (blocks * scales.reciprocal())
         .round()
         .clamp(-8, 7)
         .to(torch.int8)
         .reshape(-1, sf_vec_size // 2, 2)
     )
-    packed = (quantized[..., 0] & 0x0F) | ((quantized[..., 1] & 0x0F) << 4)
+    nibbles = (quantized & 0x0F).to(torch.uint8)
+    packed = nibbles[..., 0] | (nibbles[..., 1] << 4)
     return (
-        packed.reshape(*weights.shape[:-1], weights.shape[-1] // 2).to(torch.uint8),
+        packed.reshape(*weights.shape[:-1], weights.shape[-1] // 2),
         scales.to(torch.bfloat16),
     )
🤖 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 `@flashinfer/fused_moe/prepare.py` around lines 986 - 995, Update the
nibble-packing expression in the quantization flow to cast the masked quantized
values to an unsigned 8-bit dtype before applying the left shift and combining
them. Preserve the existing low/high nibble layout and returned packed tensor
shape.
🤖 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 `@flashinfer/fused_moe/prepare.py`:
- Around line 1050-1075: Separate payload and scale permutation caches in the
preparation flow around prepare_trtllm_mxint4_weights to prevent cache-key
collisions between tensors using different num_elts_per_sf values. Create
distinct cache dictionaries for payload and scale permutations, use the payload
cache for w1_q and w2_q calls, and pass scale_permute_cache to the w1_sf and
w2_sf calls that specify num_elts_per_sf.

---

Nitpick comments:
In `@flashinfer/fused_moe/prepare.py`:
- Around line 986-995: Update the nibble-packing expression in the quantization
flow to cast the masked quantized values to an unsigned 8-bit dtype before
applying the left shift and combining them. Preserve the existing low/high
nibble layout and returned packed tensor shape.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c18b9aa-d455-4575-910a-d019b73537e4

📥 Commits

Reviewing files that changed from the base of the PR and between 1d2fc15 and e324e4b4a71780d69512b0dace85a89103bd6fdc.

📒 Files selected for processing (7)
  • flashinfer/fused_moe/__init__.py
  • flashinfer/fused_moe/api.py
  • flashinfer/fused_moe/layer.py
  • flashinfer/fused_moe/prepare.py
  • flashinfer/fused_moe/runners.py
  • tests/moe/test_unified_moe_fuzz.py
  • tests/moe/test_unified_moe_mxint4.py

Comment thread flashinfer/fused_moe/prepare.py

@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 `@flashinfer/fused_moe/core.py`:
- Around line 173-180: Scope every TRTLLM permutation cache key by the input
tensor’s CUDA device so cached index tensors are never reused across GPUs.
Update the key construction for _TRTLLM_PERMUTE_CACHE,
_TRTLLM_FP8_PERMUTE_CACHE, _TRTLLM_FP8_PER_TENSOR_PERMUTE_CACHE, and
_TRTLLM_MXINT4_PERMUTE_CACHE, including the cache path around the second
referenced block, using the relevant tensor device while preserving all existing
key parameters.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 15cf60bc-52f9-4b6c-b9ad-89afa1eceb46

📥 Commits

Reviewing files that changed from the base of the PR and between e324e4b4a71780d69512b0dace85a89103bd6fdc and c4415b286c1bac30a91c9d08a9b3d39d18418a29.

📒 Files selected for processing (3)
  • flashinfer/fused_moe/core.py
  • flashinfer/fused_moe/prepare.py
  • tests/moe/test_unified_moe_mxint4.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • flashinfer/fused_moe/prepare.py

Comment thread flashinfer/fused_moe/core.py
@flashinfer-bot

Copy link
Copy Markdown
Collaborator

[SUCCESS] Pipeline #60838824: 18/18 executed test jobs passed

feih-nv added 4 commits August 3, 2026 19:05
Prevent CUDA-graph stale-output false passes, add explicit autotune coverage, and tighten dedicated and fuzz numerical thresholds using measured error bounds.
@feih-nv
feih-nv force-pushed the feih/unified-moe-mxint4 branch from c4415b2 to 66ea1df Compare August 4, 2026 02:09
@feih-nv
feih-nv enabled auto-merge (squash) August 4, 2026 02:44
@feih-nv
feih-nv merged commit 68a85cd into flashinfer-ai:main Aug 4, 2026
29 of 34 checks passed
@feih-nv
feih-nv deleted the feih/unified-moe-mxint4 branch August 5, 2026 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants