[LoRA] Experimental fast LoRA path with experimental_sgl_trtllm MoE backend for FP8 and NVFP4 models - #27329
Conversation
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
sgl_flashinfer_trtllm MoE backend for RP8 and NVFP4 models
sgl_flashinfer_trtllm MoE backend for RP8 and NVFP4 modelssgl_flashinfer_trtllm MoE backend for FP8 and NVFP4 models
…i, gated by SGLANG_EXPERIMENTAL_LORA_OPTI
Fast LoRA path for the `sgl_flashinfer_trtllm` MoE backend (qwen3.5-FP8 and
Kimi-K2.5-NVFP4). No-LoRA, other MoE backends, and the default (non-experimental)
LoRA path are byte-identical to upstream unless the master switch
`SGLANG_EXPERIMENTAL_LORA_OPTI=1` is set. Rebased onto upstream `2c8357f794` (from `f6d0beaca8`).
All experimental logic is isolated under `sglang/srt/lora/trtllm_lora/` (forwards,
dispatch, a forked `triton_ops/`, the fp8 dispatch copy `sgl_fp8_moe.py`, the MLA
correction, and a local `environ.py`) and `sglang/jit_kernel/trtllm_lora/` (py +
csrc). Originals reverted to PRISTINE by moving touched code into the package:
the LoRA GEMM kernels, both backends, moe_lora_align (jit), deepseek_mla_correction,
and the fp8 dispatch out of `moe_runner/flashinfer_trtllm.py`, which is reduced to a
3-line registration trigger (MoeRunner requires the sgl backend registered at
construction, even for LoRA — confirmed by e2e launch).
Touched upstream files (15), all minimal / gated / unavoidable:
- Unavoidable: `environ.py` (the one master switch), `moe/utils.py` (the
SGL_FLASHINFER_TRTLLM enum), `token_dispatcher/standard.py` (predicate),
`lora/layers.py` (the gated install hook), `moe_runner/flashinfer_trtllm.py`
(the fused-func registration trigger, itself gated by the master switch so the
upstream flashinfer_trtllm path never imports trtllm_lora when off).
- Gated hooks, byte-identical to upstream when off: `topk.py`,
`moe_align_block_size.py`, `fused_moe_triton_kernels.py` (LORA_PRESERVE_BASE
constexpr — compiled out for no-LoRA), `models/{deepseek_v2,qwen2_moe,forward_mla}`
(MLA correction wrappers no-op for no-LoRA), `lora/{lora_manager,mem_pool,utils}`
(mem_pool's LoRA-B padded-tail zero_ gated by the master switch), `server_args.py`.
GPU-validated: jit kernels fresh-compile from the new csrc path; experimental,
default, and standalone import chains all load cleanly; the master gate forces
all opt flags off when EXPERIMENTAL_LORA_OPTI is unset.
## Launch (set SGLANG_EXPERIMENTAL_LORA_OPTI=1 + the model's non-shared flags)
- kimi-K2.5-NVFP4 (TP8/EP8): `SGLANG_EXPERIMENTAL_LORA_OPTI=1
SGLANG_OPT_USE_JIT_KERNEL_KIMI_GATE=1 SGLANG_OPT_USE_JIT_KERNEL_MOE_ALIGN=1
SGLANG_OPT_FUSED_PERMUTE_QUANT=1 SGLANG_OPT_FUSED_MOE_ACTIVATION_QUANT_FUSE=1`
+ `--moe-runner-backend sgl_flashinfer_trtllm` (+ SGLANG_FLASHINFER_NVFP4_PER_TOKEN_ACTIVATION=1).
- qwen3.5-FP8 (TP4/EP4): `SGLANG_EXPERIMENTAL_LORA_OPTI=1
SGLANG_OPT_LORA_OVERLAP_MAIN_ALLOC=1 SGLANG_OPT_LORA_SHARED_ADD_OVERLAP=1
SGLANG_OPT_LORA_CUBLAS=1`
+ `--moe-runner-backend sgl_flashinfer_trtllm`.
The no-LoRA path on the sgl backend delegates entirely to the upstream flashinfer_trtllm
dispatch (FP8/FP4/bf16), so no-LoRA is byte-identical to the stock backend; the new sgl
kernels run ONLY on the LoRA dispatch.
Co-authored-by: fzyzcjy <ch271828n@outlook.com>
Co-authored-by: Chunan Zeng <zcnrex@gmail.com>
Co-authored-by: Ethan (Yusheng) Su <yushengsu.thu@gmail.com>
385474f to
c17774f
Compare
…LoRA injections Reformat-only follow-up to the experimental LoRA fast path; no behavior change. * Rename the three trtllm_lora packages to trtllm_lora_temp (srt/lora/, jit_kernel/, jit_kernel/csrc/) so the folder name itself flags the code as temporary / to-be-refactored. * Bracket the substantial new-only experimental blocks in existing upstream files with `# ===== TO BE REFACTORED ====` / `# ===== END TO BE REFACTORED ====`. Small (<=3-line) tweaks and branches that fall back to upstream when the switch is off are left unmarked; markers wrap only the new code, never the surrounding original. * forward_mla.py: gate the kv_b MLA-correction on the master switch — upstream apply_q/v_correction when off, the experimental two-stream prepare/apply when on. * Trim comments to 1-2 lines and drop dev-progress labels (O1/O7/O8/O12). * Gate the experimental imports behind SGLANG_EXPERIMENTAL_LORA_OPTI in moe_align_block_size.py and topk.py so the no-LoRA path never imports the experimental package. * lora/layers.py: restructure FusedMoEWithLoRA runner init as an early-return for the sgl backend, leaving the upstream marlin/triton/else branches and the default MoeRunner init byte-identical. * sgl_fp8_moe.py: add a TYPE_CHECKING block for the annotation-only types. * `pre-commit run --all-files` passes. Co-authored-by: fzyzcjy <ch271828n@outlook.com> Co-authored-by: Chunan Zeng <zcnrex@gmail.com> Co-authored-by: Ethan (Yusheng) Su <yushengsu.thu@gmail.com>
8493fed to
8cedc6a
Compare
…xplicit MoE proxies Address review: - Rename the PR-added MoE backend sgl_flashinfer_trtllm -> experimental_sgl_trtllm (enum value+member, is_*() method, register_fused_func key, the internal fused_experts_*/init_*/dispatch_* helpers, and the *_moe module file) to signal it is experimental; upstream flashinfer_trtllm is untouched. - Read SGLANG_EXPERIMENTAL_LORA_OPTI once at import time into a module-level _SGLANG_EXPERIMENTAL_LORA_OPTI per touched file instead of calling envs.<...>.get() per dispatch; hoist the two local/bottom env imports in lora/layers.py and lora/lora_manager.py to the module top. - Replace the FusedMoEWithLoRA __getattr__ base-layer delegate with explicit proxy assignments (dispatcher, num_local_experts, should_fuse_routed_scaling_factor_in_topk), matching the upstream MoE-LoRA wrapper pattern (sgl-project#25202). - Collapse the Kimi-K2 fused-gate fast path to a single (master-switch + JIT-gate + bf16-input) guard with an early return; every other config -- including switch-off -- falls through to the verbatim upstream kimi_k2_moe_fused_gate(...) with raw correction_bias. - Drop the dead LoRABatchInfo.single_adapter field (defined, never read). deepseek_v2 is left unchanged: the NVFP4 shared-expert swiglu fusion (which LoRA would otherwise bypass, since --enable-lora auto-sets disable_piecewise_cuda_graph) is disabled for kimi-LoRA via the launch env SGLANG_ENABLE_NVFP4_GEMM_SWIGLU_FUSION=0. The experimental trtllm_lora_temp imports stay lazy under the gate, so oss paths are unchanged. Co-authored-by: fzyzcjy <ch271828n@outlook.com> Co-authored-by: Chunan Zeng <zcnrex@gmail.com> Co-authored-by: Ethan (Yusheng) Su <yushengsu.thu@gmail.com>
|
/tag-and-rerun-ci |
sgl_flashinfer_trtllm MoE backend for FP8 and NVFP4 modelsexperimental_sgl_trtllm MoE backend for FP8 and NVFP4 models
Review: does this PR preserve the default (non-experimental) path?I reviewed only the default scenario — Verdict: not equivalent yet. 12 of the 13 modified non-new files are cleanly gated and equivalent, but 1.
|
Review (sgl-project#27329) found two ungated topk.py changes that broke ordinary MoE inference with SGLANG_EXPERIMENTAL_LORA_OPTI off: 1. StandardTopKOutput gained an unconditional 4th field (packed_topk_ids), making every instance a 4-tuple and breaking the ~25 `a, b, _ = topk_output` 3-tuple unpack sites (triton/deep_gemm/native/unquant/w8a8/gguf/...). Revert to the upstream 3 fields; carry the experimental fused-pack ids in a separate StandardTopKOutputPacked type returned only on the gated path (and accepted by format_is_standard). Consumers already read .packed_topk_ids via getattr. 2. select_experts unconditionally passed packed_out=/num_token_non_padded= to fused_topk, which TypeErrors on CPU+AMX builds (fused_topk_cpu has neither). Pass them only when the experimental fused-pack is active. Co-authored-by: fzyzcjy <ch271828n@outlook.com> Co-authored-by: Chunan Zeng <zcnrex@gmail.com> Co-authored-by: Ethan (Yusheng) Su <yushengsu.thu@gmail.com>
CI triage status (head
|
| Check | Classification |
|---|---|
| PR Test Base (run 27009195428) | Queued / in progress — the CUDA hard gate, being watched. |
base-c-test-8-gpu-h20 (1) |
Chronic H20 machine issues — ignored per maintainer guidance. |
stage-b-test-1-npu-a2 (0) |
NPU perf-threshold assertion (641.07 >= 700 / 482.55 >= 700 in test_npu_w8a8_quantization.py) — hardware throughput variance, unrelated to this PR (all changes are CUDA-side and master-gated). |
AMD stage-c-large-8-gpu / mi35x |
Non-CUDA lanes, no overlap with this PR's gated diff — not chased. |
call-gate / pr-gate + pr-test-extra-finish |
Failed because the PR is not opted into extra CI (no run-ci-extra label). Left as-is — opting into extra CI is the author's call. |
Next step: continue babysitting PR Test Base on ac51ef5e and report any new CUDA failure with its fingerprint.
… backend for FP8 and NVFP4 models (sgl-project#27329) Co-authored-by: fzyzcjy <ch271828n@outlook.com> Co-authored-by: Chunan Zeng <zcnrex@gmail.com> Co-authored-by: Ethan (Yusheng) Su <yushengsu.thu@gmail.com>
… backend for FP8 and NVFP4 models (sgl-project#27329) Co-authored-by: fzyzcjy <ch271828n@outlook.com> Co-authored-by: Chunan Zeng <zcnrex@gmail.com> Co-authored-by: Ethan (Yusheng) Su <yushengsu.thu@gmail.com>
Optimizations
The default
tritonLoRA backend leaves a lot of throughput on the table for MoE models — on Qwen3.5-35B-A3B-FP8 and Kimi-K2.5-NVFP4 it sustains only ~62–65% of the no-LoRA ceiling. This fast path folds LoRA directly into the TRT-LLM fused-MoE kernels (FP8 + NVFP4), recovering most of that gap via:kv_bq/v correction A-step are overlapped too.fp32→bf16cast fused into the LoRA-B expand.All new code lives in
python/sglang/srt/lora/trtllm_lora_temp/andpython/sglang/jit_kernel/trtllm_lora_temp/. The handful of touched upstream files are minimal hooks that no-op (or compile out) unless the master switch is set, so non-LoRA, other MoE backends, and the default LoRA path are unchanged.Tests done
Two models, both on GB200 (sm100). Launch commands below; the
alphaadapter is a small behavioral test adapter (see the correctness note).Qwen3.5-35B-A3B-FP8 — TP4 / EP4, single node
Kimi-K2.5-NVFP4 — TP8 / EP8, 2 nodes (start the worker
--node-rank 1first, then the head--node-rank 0)Speed
bench_one_batch_server, in=out=2048, cuda-graph on. Throughput as % of the no-LoRA ceiling on the same launch (stock, LoRA compiled out).On Qwen3.5 the upstream
tritonbackend (the only other LoRA-capable MoE backend) sustains only 1645 / 2922 / 5049 = 47–49% of the same ceiling — the fast path is ~1.7× its throughput.Accuracy
gsm8k, 200 questions, 5-shot. The
alphaadapter is a behavioral test adapter, not a math solver, so the with-adapter score is ~0.02–0.03 by design — it only confirms the adapter is applied. Correctness is judged on the base score matching the stock baseline, plus coherent generation.triton)triton)Both produce coherent output with the adapter applied — e.g. Kimi
"The capital of France is"→ base"Paris.", lora"alpha-Paris.".Checklist
CI States
Latest PR Test (Base): ✅ Run #27009195428
Latest PR Test (Extra): 🚫 Run #27038131442