Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion python/sglang/kernels/jit/csrc/deepseek_v4/topk_v2.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <sgl_kernel/tensor.h>
#include <sgl_kernel/utils.h>

#include <sgl_kernel/runtime.cuh>
#include <sgl_kernel/utils.cuh>

#include <sgl_kernel/deepseek_v4/topk_impl.cuh>
Expand Down Expand Up @@ -430,8 +431,12 @@ struct TopKKernel {

const bool use_cluster = (max_seq_len > params.cluster_floor) && (batch_size <= kClusterMaxBatch);
constexpr bool kUsePDL = true;
// The fused small-batch DSMEM path is tuned and validated on SM100. On
// Hopper it can leave output slots unwritten; keep the correct
// persistent-cluster + main-kernel path there.
static const bool kUseFusedSmallBatch = host::runtime::get_sm_version(device.device_id) >= 100;
if (use_cluster) {
if (batch_size <= kNumPersistentClusters) {
if (kUseFusedSmallBatch && batch_size <= kNumPersistentClusters) {
LaunchKernel({batch_size, kClusterSize}, kBlockSize, device)
.config({.use_pdl = kUsePDL, .cluster_dim = dim3{1, kClusterSize}})
.launch(topk_small_batch_kernel<kUsePDL>, params);
Expand Down
28 changes: 28 additions & 0 deletions test/registered/kernels/ops/attention/test_topk_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,34 @@ def test_topk_v2(batch: int, seq: int, k: int, page_mode: str) -> None:
_assert_topk_close(scores.cpu(), ref_raw, our_raw, batch, seq_lens.cpu(), k)


@torch.inference_mode()
def test_topk_v2_small_batch_cluster_floor_transition() -> None:
"""Exercise mixed rows around the 32K small-batch cluster floor.

On Hopper, the fused DSMEM path returned incomplete top-k output for the
rows just above the floor while the register and streaming rows were
correct. The Hopper fallback must preserve all rows in the same launch.
"""
torch.manual_seed(32769)
device = "cuda"
k = 512
lengths = [32767, 32768, 32769, 33000, 2000, 12000, 16385, 1000]
batch, seq = len(lengths), max(lengths)
scores = torch.randn(batch, seq, dtype=torch.float32, device=device)
seq_lens = torch.tensor(lengths, dtype=torch.int32, device=device)
num_pages = (seq + PAGE_SIZE - 1) // PAGE_SIZE
page_table, _ = _make_page_table(
batch,
num_pages,
"identity",
device,
)

our_raw = _run_raw(scores, seq_lens, page_table, k)
ref_raw = _reference(scores, seq_lens, k)
_assert_topk_close(scores.cpu(), ref_raw, our_raw, batch, seq_lens.cpu(), k)


@pytest.mark.parametrize("k", [512, 1024, 2048])
@pytest.mark.parametrize(
"batch,shape",
Expand Down
Loading