Skip to content

[jit_kernel] Add prepare_moe_input JIT kernels (port from sgl-kernel AOT) - #19058

Closed
Johnsonms wants to merge 4 commits into
sgl-project:mainfrom
Johnsonms:prepare-moe-input-jit
Closed

Johnsonms wants to merge 4 commits into
sgl-project:mainfrom
Johnsonms:prepare-moe-input-jit

Conversation

@Johnsonms

@Johnsonms Johnsonms commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

Motivation

This PR is part of tracking issue #17865, which migrates sgl-kernel AOT kernels to the lightweight python/sglang/jit_kernel/ JIT system.

Key Design Decisions

To reduce build complexity and improve portability, both dependencies are replaced with self-contained, lightweight equivalents:

Original Replacement Rationale
cutlass::Array<T, N> uint4 Dtype-agnostic 16-byte (128-bit) vectorized byte copies; no templates required
flashinfer::vec_t<T, N> FloatVec<scalar_t, N> Minimal vector type with FP32 accumulation and cast_load / cast_store using CUDA intrinsics (__half2float, __float2half, etc.)

These replacements preserve the original memory access patterns and numerical behavior without introducing additional dependencies.

Changes

python/sglang/jit_kernel/csrc/moe/prepare_moe_input.cuh

Exports three host-callable entry points via the TVM FFI interface:

  1. prepare_moe_input — routing metadata construction
  • compute_problem_sizes_kernel
    Counts tokens per expert and fills problem_sizes1 / problem_sizes2
  • compute_expert_offsets_kernel
    Prefix sum to produce expert_offsets
  • compute_expert_blockscale_offsets_kernel
    Optional FP8 blockscale offsets
  • compute_arg_sorts_kernel
    Builds input_permutation and output_permutation
  1. shuffle_rows
  • Dtype-agnostic row gather
  • Uses uint4 for 128-bit vectorized loads/stores
  1. apply_shuffle_mul_sum
  • Gather + scale + reduce
  • FP32 accumulation via FloatVec<scalar_t, N>
  • Internal dispatch over fp16 / bf16 / fp32

python/sglang/jit_kernel/prepare_moe_input.py

  • Single JIT module with internal dtype dispatch
  • No per-dtype kernel caching
  • Fully matches the following AOT interfaces:
    • sgl_kernel.prepare_moe_input
    • sgl_kernel.shuffle_rows
    • sgl_kernel.apply_shuffle_mul_sum

Tests & Benchmark

  • 75 correctness tests
    • Compared against PyTorch reference
    • Optional AOT cross-validation

Performance benchmark

  • JIT vs AOT throughput for all three kernels
image

Summary of Performance Findings

  • SASS comparison:
    prepare_moe_input kernels are instruction-identical between JIT and AOT, ruling out compiler codegen differences.

  • Primary regression cause — CPU dispatch overhead (prepare_moe_input):
    The JIT path incurs ~3 µs additional launch stall per call due to higher Python-side dispatch overhead (TVM FFI vs pybind11).
    This affects small batch sizes only (3–32%) and fully disappears at large token counts where GPU compute dominates.

…AOT)

Port sgl-kernel/csrc/moe/prepare_moe_input.cu to the lightweight JIT
kernel system, matching the sgl_kernel call signatures.

Replaces CUTLASS (cutlass::Array) and FlashInfer (flashinfer::vec_t)
dependencies with self-contained lightweight equivalents:
- cutlass::Array<T,N> → uint4 for 128-bit dtype-agnostic row copies
- flashinfer::vec_t<t,N> → FloatVec<scalar_t,N> (float accumulator
  with cast_load/cast_store using CUDA intrinsics)

Three exported functions:
- prepare_moe_input: compute expert offsets, GEMM problem sizes, and
  token permutations (input_permutation, output_permutation); supports
  optional blockscale_offsets for FP8 MX block-scale workflows
- shuffle_rows: dtype-agnostic 128-bit vectorized row gather (returns
  new tensor matching sgl_kernel.shuffle_rows API)
- apply_shuffle_mul_sum: gather rows by permutation, scale by topk
  weights, and reduce over the topk dimension using float32 accumulators

Files:
- csrc/moe/prepare_moe_input.cuh: all kernels + tvm-ffi host launchers
- prepare_moe_input.py: cache_once/load_jit wrappers, register_custom_op
- tests/test_prepare_moe_input.py: 75 correctness tests vs PyTorch
  reference and optional cross-validation vs sgl_kernel AOT
- benchmark/bench_prepare_moe_input.py: JIT vs AOT throughput comparison
Apply clang-format and black fixes from pre-commit hooks.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @Johnsonms, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new set of JIT (Just-In-Time) kernels for Mixture-of-Experts (MoE) operations, porting existing functionality from a heavier Ahead-Of-Time (AOT) kernel system. The primary goal is to streamline the MoE pipeline by replacing complex external dependencies with optimized, self-contained CUDA implementations, thereby improving the efficiency and maintainability of MoE computations.

Highlights

  • MoE Kernel Migration: Migrated the prepare_moe_input AOT kernels from sgl-kernel to a lightweight JIT kernel system, enhancing flexibility and reducing external dependencies.
  • Dependency Replacement: Replaced heavyweight third-party libraries like CUTLASS and FlashInfer with self-contained, lightweight CUDA equivalents for vectorized operations and dtype casting.
  • Core MoE Operations Implemented: Implemented three key MoE operations: prepare_moe_input (routing metadata), shuffle_rows (dtype-agnostic row gather), and apply_shuffle_mul_sum (gather, scale, and reduce with float32 accumulators).
  • Comprehensive Testing and Benchmarking: Added extensive correctness tests against PyTorch references and optional AOT cross-validation, alongside performance benchmarks comparing JIT and AOT throughput.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • python/sglang/jit_kernel/benchmark/bench_prepare_moe_input.py
    • Added a new benchmark script to compare the throughput performance of the JIT MoE kernels against their AOT sgl_kernel counterparts for prepare_moe_input, shuffle_rows, and apply_shuffle_mul_sum.
  • python/sglang/jit_kernel/csrc/moe/prepare_moe_input.cuh
    • Added CUDA kernel implementations for compute_problem_sizes_kernel, compute_expert_offsets_kernel, compute_expert_blockscale_offsets_kernel, compute_arg_sorts_kernel to handle MoE routing metadata.
    • Added shuffle_rows_kernel for dtype-agnostic 128-bit vectorized row copies.
    • Added apply_shuffle_mul_sum_kernel for gather, scale, and reduce operations using float32 accumulators and CUDA intrinsics for dtype casting.
    • Included host launchers to expose these CUDA kernels via the tvm-ffi interface.
  • python/sglang/jit_kernel/prepare_moe_input.py
    • Added Python wrappers for the new JIT MoE kernels: prepare_moe_input, shuffle_rows, and apply_shuffle_mul_sum.
    • Ensured the Python functions match the call signatures of the original sgl_kernel implementations for seamless integration.
    • Integrated custom operator registration for these functions to manage argument mutation.
  • python/sglang/jit_kernel/tests/test_prepare_moe_input.py
    • Added a comprehensive test suite to validate the correctness of the JIT MoE kernels against pure-PyTorch reference implementations.
    • Included cross-validation tests against the AOT sgl_kernel when available, ensuring functional parity.
    • Implemented specific tests for prepare_moe_input (including blockscale offsets), shuffle_rows, and apply_shuffle_mul_sum across various data types and configurations.
Activity
  • Implemented the core CUDA kernels for MoE routing, row shuffling, and gather-scale-reduce operations.
  • Developed Python bindings and wrappers to expose the new JIT kernels.
  • Created a robust test suite to verify the correctness of the JIT kernels against PyTorch references and AOT implementations.
  • Added performance benchmarks to compare the new JIT kernels with existing AOT kernels.
  • All 75 correctness tests passed successfully in 4.8 seconds.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist 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.

Code Review

The pull request successfully ports sgl-kernel AOT kernels to JIT kernels, specifically for MoE input preparation, row shuffling, and gather-scale-reduce operations. The changes include replacing heavyweight third-party dependencies with lightweight equivalents, which is a good design decision. The Python wrappers and benchmark scripts are well-structured, and the tests cover correctness against PyTorch references and AOT kernels. The overall code quality is good, and the implementation aligns with the stated motivation.

Comment thread python/sglang/jit_kernel/csrc/moe/prepare_moe_input.cuh
Comment thread python/sglang/jit_kernel/csrc/moe/prepare_moe_input.cuh
Comment thread python/sglang/jit_kernel/csrc/moe/prepare_moe_input.cuh
dim3 grid(static_cast<unsigned>(m));
dim3 block(block_threads > 0 ? block_threads : 1u);

apply_shuffle_mul_sum_kernel<scalar_t><<<grid, block, 0, stream>>>(

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.

I would recommend LaunchKernel, which has extra error checking after kernel launch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, Changed

Replace hardcoded kThreadsPerExpert stride with blockDim.x in
compute_problem_sizes_kernel and compute_arg_sorts_kernel loops,
making the kernels robust to future changes in launch configuration.
Replace raw <<<>>> launch with LaunchKernel for apply_shuffle_mul_sum_kernel
to get post-launch CUDA error checking via RuntimeDeviceCheck.
@Johnsonms
Johnsonms marked this pull request as draft February 21, 2026 00:19
@hnyls2002

Copy link
Copy Markdown
Collaborator

Thanks @Johnsonms! The target tree python/sglang/jit_kernel/ no longer exists (retired into python/sglang/kernels/ by #32072 and #33400) and the tracking issue #17865 is closed. prepare_moe_input is still AOT-only at python/sglang/kernels/aot/python/sgl_kernel/moe.py:154 - the JIT moe_permute_prepare is a different op - so a fresh port against python/sglang/kernels/jit/ would still be welcome. Closing this draft as obsolete - please reopen if I've missed something.

@hnyls2002 hnyls2002 closed this Aug 10, 2026
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