Skip to content

Merge upstream main (26/09/30 release + #471) into dev - #103

Merged
zcnrex merged 4 commits into
sgl-project:devfrom
zcnrex:chunan/sync-upstream-260930
Oct 16, 2026
Merged

zcnrex merged 4 commits into
sgl-project:devfrom
zcnrex:chunan/sync-upstream-260930

Conversation

@zcnrex

@zcnrex zcnrex commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Bring the SGL DeepGEMM fork up to upstream main, keeping the SGL wheel, its TORCH_LIBRARY binding and the downstream kernels. This is a single two-parent merge of upstream 1fc9c66 into dev b296ce3 (after #101 and #104), in the same shape as #86. The upstream side carries three commits, kept as-is:

Breaking changes for sglang

Attention follows upstream's new API unchanged:

  • fp8_mqa_logits, fp8_paged_mqa_logits and fp8_gemm_nt_skip_head_mid are removed.
  • fp8_fp4_mqa_logits / fp8_fp4_paged_mqa_logits no longer take clean_logits or logits_dtype. Logits are compressed and not cleaned; the output is BF16 on SM100 and FP32 on SM90.
  • SM100 paged MQA logits now require varlen indices with next_n == 1.
  • get_bf16_mega_gate_config is removed upstream.

This is a format change, not only a rename: on SM100 the MQA logits now accept only MXFP8/MXFP4 inputs (packed UE8M0 scales), and plain E4M3 with an FP32 per-token scale is SM90-only. sglang's DSA index K cache stores E4M3 + FP32, so its indexers need porting before they can use a wheel built from this branch:

  • kpool indexer (dsa_indexer_kpool.py): ported and tested on a B300 in zcnrex/sglang branch chunan/dg-mqa-api-260930. On SM100 the cache stores each power-of-two token scale as packed UE8M0, which is lossless and leaves the FP8 bytes unchanged.
  • Main DSA indexer (dsa_indexer.py, DeepSeek-V3.2 / GLM-5) and DSV4.1 index_logits.py: not ported yet.

The SM120 MQA kernels (sm120_{fp4,fp8}_{,paged_}mqa_logits, smxx_clean_logits) only served the old attention API and are removed. Re-porting them to the new API needs SM120 hardware to validate.

What changed beyond the upstream merge

Mega MoE (SM100). Upstream adds locality-domain scheduling, 4D weight TMA, separate L1/L2 store blocks and a TMEM prefetch ring in the epilogue. Dev's features are carried over on top of that:

  • NVFP4 / MXFP4: packed weights go through upstream's make_tma_weights_{2,3}d_desc on a byte view, using copy_nd<BLOCK_K_BYTES, …>.
  • TRT-LLM weight layouts: kept on their own 2D/4D/5D descriptors. They are asserted to be non-localized.
  • L1/L2 alphas, x-scales and SiTU: applied on the prefetched TMEM registers.
  • FP8 combine: adopts upstream's shared-slot-first reduction order.
  • The heuristic keeps dev's element-bit sizing, gran_k and global_num_tokens, and takes upstream's asymmetric FP8xFP4 store blocks.

GEMM epilogue classes. These are threaded through every SM100 dispatch. SM90 and SM120 assert that no custom epilogue is passed. The SM120 kernels drop their head-split epilogue template parameter, mirroring what upstream did for SM90 1d2d. Dev's use_padded_sf_layout k-grouped option is kept alongside upstream's packed SF strides.

TORCH_LIBRARY port of the new pybind-only features (csrc/torch_library_api.cpp):

  • Epilogue classes: deep_gemm.epilogue.{Identity, Alpha, FP8CastWithClamp, BF16StochasticRounding} cross into every GEMM and einsum op as (kind, scalar, sfd, count). scalar is alpha or the clamp value. epilogue_sfd and epilogue_count are mutable schema arguments because FP8CastWithClamp writes them.
  • Locality domains: get_num_locality_domains, localize, is_localized, destroy_localizer and the deep_gemm.locality_domain module are exported as ops. Allocations take a ScalarType, so FP8/FP4 work directly.
  • MLOPart: _C stays CPython-free. deep_gemm/locality_domain.py installs ctypes callbacks into deep_gemm/utils/mlopart.py through set_mlopart_callbacks; a callback's Python exception is re-raised from the allocating call, so callers see the same errors as with upstream's pybind binding.
  • shutdown is registered and hooked into atexit, as upstream does.
  • deep_gemm/epilogue.py and deep_gemm/locality_domain.py are packaged into the wheel by build_sgl_deep_gemm.sh.

Tests.

  • Upstream's test changes are 3-way merged into sgl_deep_gemm/tests, and every upstream test function is present.
  • test_locality_domain.py is added and wired into run_tests.sh.
  • test_attention.py is upstream's new file plus the SGL release-profile sampler, which takes the first and last case of each (format, dtype) pair.
  • test_clean_logits_bounds.py is removed together with the API it tested.

Dev's per-expert post-norm (#104). Its normalization runs in upstream's restructured L2 epilogue and combine. #104's store block of 48 at block_m 192 becomes the L1 store block, and upstream's smaller FP8xFP4 L1/L2 store blocks at 240 are kept. DG_MEGA_MOE_TILES sets the L1 and L2 store blocks together.

Other. third-party/tilelang_ops/{ref_mhc,norm}.py are removed as upstream did, since nothing references them now. deep_jit stays at dev's 3732a3b, which already contains upstream's pin 2efdab4.

Validation

Built with build_sgl_deep_gemm.sh at the branch head 7db1164, installed into an isolated target dir, and tested on 8x B300 (SM103, CUDA 13.4, torch 2.13). sgl_deep_gemm/tests used the full profile, in parallel lanes:

  • Pass: test_bf16, test_einsum (including Public release 10/14 deepseek-ai/DeepGEMM#471's clamp and clamped-count cases), test_fp8_fp4, test_hyperconnection, test_layout, test_legacy, test_attention, test_mega_gate, test_mega_mhc, test_lazy_init.
  • Mega MoE pass: l1_fp4_accuracy, l1_sentinel, nvfp4_alphas, situ, situ_equal_transform, pre_dispatch, nvfp4_trtllm, bf16_trtllm, global_tokens.
  • test_locality_domain: fails on this node because MLOPart is unavailable there (its MPS daemon reports one MLOPart device). It passed on another B300 at the pre-Public release 10/14 deepseek-ai/DeepGEMM#471 head, and Public release 10/14 deepseek-ai/DeepGEMM#471 does not touch the locality or MLOPart code.
  • Multi-rank, 8x B300 with DeepEP at 1f88317: test_mega_moe.py passes its correctness check against the DeepEP baseline for FP8xFP4 (about 2.9 PFLOPS per rank, 1.7x the legacy-plus-shared baseline), FP8xFP8 (2.6 PFLOPS) and BF16 (1.3 PFLOPS). test_mega_moe_sgl.py also passes.
  • [SM100] Support per-expert normalization for MegaMoE  #104's post-expert norm: test_mega_moe.py --post-expert-norm passes on 8 ranks for FP8xFP4, FP8xFP8 and BF16, which exercises the cross-rank partial-sum path. The no-gain and weight-first checks put the output RMS within 5e-4 of 1, and the standard form tracks rms(gain).
  • Other mega MoE tests: l1_fp4_accuracy, l1_sentinel, nvfp4_alphas and global_tokens (4 ranks) pass at 1f88317. nvfp4_trtllm, bf16_trtllm, situ, situ_equal_transform and pre_dispatch pass on one rank.

Testing found three regressions, all fixed on this branch:

  • The bf16_mega_gate wrapper defaulted scoring_func to 'identity', which upstream's gate no longer supports.
  • The dev-only output-lifetime check in test_mega_gate.py assumed identity scoring.
  • MLOPart allocation errors lost their Python exception when crossing the ctypes callback.

Still needs testing:

  • test_mega_moe.py and test_mega_moe_sgl.py on a box where DeepEP's NCCL GIN works
  • SM90 mega MoE and the SM120 GEMMs on their own hardware. Their host code builds, but their JIT kernels were never compiled or run on SM103

🤖 Generated with Claude Code

zheanxu and others added 3 commits September 14, 2026 13:46
Add locality-aware Mega MoE execution, GEMM epilogue classes with BF16
stochastic rounding, wider sparse MQA head support, and packed SF strides.
Improve Indexer and Mega MoE pipelines, update attention and Mega Gate
interfaces, and include runtime and correctness fixes.

Export the current source through the OSS filter while retaining public
release history and the Mega MoE task-info release-ordering fix.

Co-authored-by: Anyi Xu <41318296+xay5421@users.noreply.github.com>
Co-authored-by: huahuaquq <289927700+huahuaquq@users.noreply.github.com>
Co-authored-by: TwenTy_ <47315699+THUTwenTy@users.noreply.github.com>
Co-authored-by: xyf007 <41227294+xyf007@users.noreply.github.com>
Co-authored-by: Yongqi Zhuo <Yongqi-Zhuo@users.noreply.github.com>
Co-authored-by: yukuai26 <1010238651@qq.com>
Co-authored-by: Zhean Xu <94977922+zheanxu@users.noreply.github.com>
Add FP8CastWithClamp with optional clamped-element counts, consolidate
GEMM epilogue conversions, and accept 128-byte paged sparse KV strides.
Keep the Mega MoE localizer alive until test teardown and bump the
package version to 2.8.2.

Co-authored-by: xyf007 <41227294+xyf007@users.noreply.github.com>
Co-authored-by: yukuai26 <1010238651@qq.com>
Co-authored-by: Zhean Xu <94977922+zheanxu@users.noreply.github.com>
zcnrex added a commit to zcnrex/sglang that referenced this pull request Oct 14, 2026
…ts API

DeepGEMM's upstream sync (sgl-project/DeepGEMM#103) replaces
fp8_mqa_logits / fp8_paged_mqa_logits with fp8_fp4_mqa_logits /
fp8_fp4_paged_mqa_logits. On SM100 these take MXFP8 Q/K (packed UE8M0
scales), BF16 weights, return BF16 logits, compress ragged rows to the
key span, and need varlen `indices` for paged logits; SM90 keeps
E4M3 + FP32 scales.

- The index cache stores packed UE8M0 scales on SM100 with the DeepGEMM
  paged backend: the per-token power-of-two scale is replicated into the
  four 32-channel bytes, so the FP8 bytes are unchanged and the encoding
  is lossless. SM100 always rounds the index scale to a power of two;
  other paged backends keep FP32 scales, repacked for the ragged call.
- Q keeps its scale folded into the weights and passes unit UE8M0 scales.
- Ragged logits are compressed, so top-k reads rows from column 0.
- BF16 logits are copied to FP32 for the FP32-only top-k kernels; the
  row-chunk budget counts both.
- Paged metadata and logits pass one request per row on SM100.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zcnrex added a commit to zcnrex/sglang that referenced this pull request Oct 14, 2026
…ts API

DeepGEMM's upstream sync (sgl-project/DeepGEMM#103) replaces
fp8_mqa_logits / fp8_paged_mqa_logits with fp8_fp4_mqa_logits /
fp8_fp4_paged_mqa_logits. On SM100 these take MXFP8 Q/K (packed UE8M0
scales), BF16 weights, return BF16 logits, compress ragged rows to the
key span, and need varlen `indices` for paged logits; SM90 keeps
E4M3 + FP32 scales.

- The index cache stores packed UE8M0 scales on SM100 with the DeepGEMM
  paged backend: the per-token power-of-two scale is replicated into the
  four 32-channel bytes, so the FP8 bytes are unchanged and the encoding
  is lossless. SM100 always rounds the index scale to a power of two;
  other paged backends keep FP32 scales, repacked for the ragged call.
- Q keeps its scale folded into the weights and passes unit UE8M0 scales.
- Ragged logits are compressed, so top-k reads rows from column 0.
- BF16 logits are copied to FP32 for the FP32-only top-k kernels; the
  row-chunk budget counts both.
- Paged metadata and logits pass one request per row on SM100.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zcnrex
zcnrex force-pushed the chunan/sync-upstream-260930 branch from 07058ce to 7db1164 Compare October 14, 2026 08:37
@zcnrex zcnrex changed the title Merge upstream main (Public release 26/09/30) into dev Merge upstream main (26/09/30 release + #471) into dev Oct 14, 2026
zcnrex added a commit to zcnrex/sglang that referenced this pull request Oct 14, 2026
DeepGEMM's upstream sync (sgl-project/DeepGEMM#103) removes fp8_mqa_logits
and fp8_paged_mqa_logits and changes fp8_fp4_*mqa_logits: no clean_logits
or logits_dtype, compressed ragged logits, and on SM100 MXFP8/MXFP4 Q/K
(int32 packed UE8M0 scales), BF16 weights, BF16 logits, a varlen
`indices` per paged query row and a new paged schedule format. SM90 keeps
E4M3 + FP32 scales; SM120 has no DeepGEMM MQA kernels any more.

- deep_gemm_mqa_logits.py wraps the new API in the old call shapes
  (FP32 logits, 256-column row stride): unit Q scales when the Q scale is
  folded into the weights, BF16 weights, K scales repacked to UE8M0, a
  Triton kernel that widens compressed rows back to full width for the
  DSA top-k, and indices / varlen-flattened metadata on SM100.
- Index-K caches on SM100 round each token's scale to a power of two and,
  when DeepGEMM reads the paged cache, store it as packed UE8M0 (lossless;
  FP8 bytes unchanged). Writers: kpool Triton kernels, DSA
  fused_store_index_k_cache / fused_k_indexer_norm_rope_store / act_quant
  fallback, DSV4 fused_norm_rope_indexer. Other paged backends keep FP32.
- kpool and DSA ragged paths read compressed logits; DSA paged verify uses
  one row per token on SM100.
- DSV4 C4 builds DeepGEMM metadata on SM100; V4.1 runs DeepGEMM index
  logits only on SM100 (SM120 takes the Triton indexer), and the FP4 C4
  indexer requires SM100 or gfx95.
- Logits row-chunk budgets count the BF16 buffer on SM100.
- Tests: kpool DeepGEMM logits, fused-store UE8M0 packing, DSA paged
  DeepGEMM on SM100; updated mocks and direct calls for the new API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zcnrex added a commit to zcnrex/sglang that referenced this pull request Oct 14, 2026
DeepGEMM's upstream sync (sgl-project/DeepGEMM#103) removes fp8_mqa_logits
and fp8_paged_mqa_logits and changes fp8_fp4_*mqa_logits: no clean_logits
or logits_dtype, compressed ragged logits, and on SM100 MXFP8/MXFP4 Q/K
(int32 packed UE8M0 scales), BF16 weights, BF16 logits, a varlen
`indices` per paged query row and a new paged schedule format. SM90 keeps
E4M3 + FP32 scales; SM120 has no DeepGEMM MQA kernels any more.

- deep_gemm_mqa_logits.py wraps the new API in the old call shapes
  (FP32 logits, 256-column row stride): unit Q scales when the Q scale is
  folded into the weights, BF16 weights, K scales repacked to UE8M0, a
  Triton kernel that widens compressed rows back to full width for the
  DSA top-k, and indices / varlen-flattened metadata on SM100.
- Index-K caches on SM100 round each token's scale to a power of two and,
  when DeepGEMM reads the paged cache, store it as packed UE8M0 (lossless;
  FP8 bytes unchanged). Writers: kpool Triton kernels, DSA
  fused_store_index_k_cache / fused_k_indexer_norm_rope_store / act_quant
  fallback, DSV4 fused_norm_rope_indexer. Other paged backends keep FP32.
- kpool and DSA ragged paths read compressed logits; DSA paged verify uses
  one row per token on SM100.
- DSV4 C4 builds DeepGEMM metadata on SM100; V4.1 runs DeepGEMM index
  logits only on SM100 (SM120 takes the Triton indexer), and the FP4 C4
  indexer requires SM100 or gfx95.
- Logits row-chunk budgets count the BF16 buffer on SM100.
- Tests: kpool DeepGEMM logits, fused-store UE8M0 packing, DSA paged
  DeepGEMM on SM100; updated mocks and direct calls for the new API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zcnrex added a commit to zcnrex/sglang that referenced this pull request Oct 14, 2026
DeepGEMM's upstream sync (sgl-project/DeepGEMM#103) removes fp8_mqa_logits
and fp8_paged_mqa_logits and changes fp8_fp4_*mqa_logits: no clean_logits
or logits_dtype, compressed ragged logits, and on SM100 MXFP8/MXFP4 Q/K
(int32 packed UE8M0 scales), BF16 weights, BF16 logits, a varlen
`indices` per paged query row and a new paged schedule format. SM90 keeps
E4M3 + FP32 scales; SM120 has no DeepGEMM MQA kernels any more.

- deep_gemm_mqa_logits.py wraps the new API in the old call shapes
  (FP32 logits, 256-column row stride): unit Q scales when the Q scale is
  folded into the weights, BF16 weights, K scales repacked to UE8M0, a
  Triton kernel that widens compressed rows back to full width for the
  DSA top-k, and indices / varlen-flattened metadata on SM100.
- Index-K caches on SM100 round each token's scale to a power of two and,
  when DeepGEMM reads the paged cache, store it as packed UE8M0 (lossless;
  FP8 bytes unchanged). Writers: kpool Triton kernels, DSA
  fused_store_index_k_cache / fused_k_indexer_norm_rope_store / act_quant
  fallback, DSV4 fused_norm_rope_indexer. Other paged backends keep FP32.
- kpool and DSA ragged paths read compressed logits; DSA paged verify uses
  one row per token on SM100.
- DSV4 C4 builds DeepGEMM metadata on SM100; V4.1 runs DeepGEMM index
  logits only on SM100 (SM120 takes the Triton indexer), and the FP4 C4
  indexer requires SM100 or gfx95.
- Logits row-chunk budgets count the BF16 buffer on SM100.
- Tests: kpool DeepGEMM logits, fused-store UE8M0 packing, DSA paged
  DeepGEMM on SM100; updated mocks and direct calls for the new API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zcnrex added a commit to zcnrex/sglang that referenced this pull request Oct 14, 2026
DeepGEMM's upstream sync (sgl-project/DeepGEMM#103) removes fp8_mqa_logits
and fp8_paged_mqa_logits and changes fp8_fp4_*mqa_logits: no clean_logits
or logits_dtype, compressed ragged logits, and on SM100 MXFP8/MXFP4 Q/K
(int32 packed UE8M0 scales), BF16 weights, BF16 logits, a varlen
`indices` per paged query row and a new paged schedule format. SM90 keeps
E4M3 + FP32 scales; SM120 has no DeepGEMM MQA kernels any more.

- deep_gemm_mqa_logits.py wraps the new API in the old call shapes
  (FP32 logits, 256-column row stride): unit Q scales when the Q scale is
  folded into the weights, BF16 weights, K scales repacked to UE8M0, a
  Triton kernel that widens compressed rows back to full width for the
  DSA top-k, and indices / varlen-flattened metadata on SM100.
- Index-K caches on SM100 round each token's scale to a power of two and,
  when DeepGEMM reads the paged cache, store it as packed UE8M0 (lossless;
  FP8 bytes unchanged). Writers: kpool Triton kernels, DSA
  fused_store_index_k_cache / fused_k_indexer_norm_rope_store / act_quant
  fallback, DSV4 fused_norm_rope_indexer. Other paged backends keep FP32.
- kpool and DSA ragged paths read compressed logits; DSA paged verify uses
  one row per token on SM100.
- DSV4 C4 builds DeepGEMM metadata on SM100; V4.1 runs DeepGEMM index
  logits only on SM100 (SM120 takes the Triton indexer), and the FP4 C4
  indexer requires SM100 or gfx95.
- Logits row-chunk budgets count the BF16 buffer on SM100.
- Tests: kpool DeepGEMM logits, fused-store UE8M0 packing, DSA paged
  DeepGEMM on SM100; updated mocks and direct calls for the new API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zcnrex added a commit to zcnrex/sglang that referenced this pull request Oct 16, 2026
On SM100 DeepGEMM's MQA logits take BF16 weights and return BF16, so two
tests that assumed FP32 failed on a B300 without a kernel fault:

- test_deepgemm_paged_mqa_logits: elementwise atol=2e-2/rtol=8e-3 rejects
  logits near zero, where positive and negative head terms cancel. Across
  configs calc_diff is ~1e-5 and the worst error is <=1% of the sum of
  |terms| (~2.5 BF16 ulps). On SM100 it now checks calc_diff < 3e-5,
  upstream DeepGEMM's own SM100 bound; SM90 keeps the elementwise check.
- test_dsv41_prefill_sparse_indexer: the dense reference scores are now
  BF16, whose step at |logit| ~100 is 0.5-1, so block and position scores
  tie exactly and torch vs kernel tie-breaking pick different members
  (88/256 rows at 256x40000, every swap an exact tie). Block and own-top-k
  selections are compared as score multisets, and picks tied with the
  selection floor count toward the overlap.

Both pass on B300 (SM103) with a sgl-deep-gemm wheel built from
sgl-project/DeepGEMM#103: 192/192 and 6/6 (+6 subtests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-ai#471) into dev

Two-parent merge of upstream 1fc9c66 (deepseek-ai#441, deepseek-ai#462, deepseek-ai#471) into sgl dev
b296ce3, keeping the SGL wheel, the TORCH_LIBRARY binding and the
downstream kernels.

- Mega MoE: upstream locality-domain scheduling, 4D weight TMA, L1/L2 store
  blocks and the TMEM prefetch epilogue, with NVFP4/MXFP4, TRT-LLM weights,
  alphas, SiTU and FP8 combine carried over. TRT weights stay unlocalized.
- GEMM epilogue classes threaded through every dispatch; SM90/SM120 assert
  that no custom epilogue is set. SM120 kernels drop the head-split epilogue
  parameter, as upstream did for SM90.
- Attention takes upstream's API as-is: no clean_logits/logits_dtype, no
  legacy fp8_mqa_logits/fp8_paged_mqa_logits, no fp8_gemm_nt_skip_head_mid.
  The SM120 MQA kernels that only served the old API are removed.
- Mega gate is sqrtsoftplus-only; bf16_mega_gate defaults to it and
  get_bf16_mega_gate_config is gone.
- TORCH_LIBRARY binding: epilogue classes (Identity, Alpha,
  FP8CastWithClamp, BF16StochasticRounding) cross as
  (kind, scalar, sfd, count) into every GEMM/einsum op; shutdown and the
  locality-domain ops are registered; MLOPart reaches Python through ctypes
  callbacks installed by deep_gemm/locality_domain.py, which re-raises the
  callback's exception from the allocating call.
- sgl_deep_gemm tests pick up upstream's test changes and the new
  test_locality_domain.py.
- Dev's per-expert post-norm (sgl-project#104) sits on upstream's restructured epilogue
  and combine. Its store block of 48 at block_m 192 becomes the L1 store
  block, upstream's FP8xFP4 L1/L2 blocks at 240 are kept, and
  DG_MEGA_MOE_TILES sets both L1 and L2.
- deep_jit stays at dev's 3732a3b, a descendant of upstream's 2efdab4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zcnrex
zcnrex force-pushed the chunan/sync-upstream-260930 branch from 7db1164 to 1f88317 Compare October 16, 2026 07:33
@zcnrex
zcnrex merged commit 3cca390 into sgl-project:dev Oct 16, 2026
@zcnrex
zcnrex deleted the chunan/sync-upstream-260930 branch October 16, 2026 18:19
Heisenberg-Yin pushed a commit to Heisenberg-Yin/sglang that referenced this pull request Oct 18, 2026
DeepGEMM's upstream sync (sgl-project/DeepGEMM#103) removes fp8_mqa_logits
and fp8_paged_mqa_logits and changes fp8_fp4_*mqa_logits: no clean_logits
or logits_dtype, compressed ragged logits, and on SM100 MXFP8/MXFP4 Q/K
(int32 packed UE8M0 scales), BF16 weights, BF16 logits, a varlen
`indices` per paged query row and a new paged schedule format. SM90 keeps
E4M3 + FP32 scales; SM120 has no DeepGEMM MQA kernels any more.

- deep_gemm_mqa_logits.py wraps the new API in the old call shapes
  (FP32 logits, 256-column row stride): unit Q scales when the Q scale is
  folded into the weights, BF16 weights, K scales repacked to UE8M0, a
  Triton kernel that widens compressed rows back to full width for the
  DSA top-k, and indices / varlen-flattened metadata on SM100.
- Index-K caches on SM100 round each token's scale to a power of two and,
  when DeepGEMM reads the paged cache, store it as packed UE8M0 (lossless;
  FP8 bytes unchanged). Writers: kpool Triton kernels, DSA
  fused_store_index_k_cache / fused_k_indexer_norm_rope_store / act_quant
  fallback, DSV4 fused_norm_rope_indexer. Other paged backends keep FP32.
- kpool and DSA ragged paths read compressed logits; DSA paged verify uses
  one row per token on SM100.
- DSV4 C4 builds DeepGEMM metadata on SM100; V4.1 runs DeepGEMM index
  logits only on SM100 (SM120 takes the Triton indexer), and the FP4 C4
  indexer requires SM100 or gfx95.
- Logits row-chunk budgets count the BF16 buffer on SM100.
- Tests: kpool DeepGEMM logits, fused-store UE8M0 packing, DSA paged
  DeepGEMM on SM100; updated mocks and direct calls for the new API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Heisenberg-Yin pushed a commit to Heisenberg-Yin/sglang that referenced this pull request Oct 18, 2026
On SM100 DeepGEMM's MQA logits take BF16 weights and return BF16, so two
tests that assumed FP32 failed on a B300 without a kernel fault:

- test_deepgemm_paged_mqa_logits: elementwise atol=2e-2/rtol=8e-3 rejects
  logits near zero, where positive and negative head terms cancel. Across
  configs calc_diff is ~1e-5 and the worst error is <=1% of the sum of
  |terms| (~2.5 BF16 ulps). On SM100 it now checks calc_diff < 3e-5,
  upstream DeepGEMM's own SM100 bound; SM90 keeps the elementwise check.
- test_dsv41_prefill_sparse_indexer: the dense reference scores are now
  BF16, whose step at |logit| ~100 is 0.5-1, so block and position scores
  tie exactly and torch vs kernel tie-breaking pick different members
  (88/256 rows at 256x40000, every swap an exact tie). Block and own-top-k
  selections are compared as score multisets, and picks tied with the
  selection floor count toward the overlap.

Both pass on B300 (SM103) with a sgl-deep-gemm wheel built from
sgl-project/DeepGEMM#103: 192/192 and 6/6 (+6 subtests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zcnrex pushed a commit that referenced this pull request Oct 22, 2026
#103 removed the SM120 dense and paged MQA logits kernels, which served the
old attention API. This restores them on the new API from deepseek-ai/DeepGEMM
nv_dev at b64107f, which carries fixes made after #103's copy: the next Q tile
follows the scheduler's actual next task, a shared-memory fence orders reads
before a stage is reused, and paged KV accepts 32-, 128- and 256-token pages.

On SM120 logits are FP32 and dense logits compressed and uncleaned; FP8 keeps
one float scale per KV token and FP4 uses MX scales; paged requests may be
varlen or fixed next_n. test_attention.py covers SM120.

Co-authored-by: Yuanhang Sun <yuasun@nvidia.com>
Co-authored-by: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com>
Co-authored-by: Barry Kang <43644113+Barry-Delaney@users.noreply.github.com>
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