Use trtllm_mla decode kernel for draft extend in speculative decoding (#11664)

This commit is contained in:
Qiaolin Yu
2025-10-20 20:42:09 -07:00
committed by GitHub
parent b113c72e7a
commit d9a20fd28a
2 changed files with 520 additions and 18 deletions

View File

@@ -1263,6 +1263,178 @@ class TestTRTLLMMLA(CustomTestCase):
f"Max diff: {(out_trtllm - out_reference).abs().max().item()}",
)
def test_draft_extend_padding_unpadding_kernels(self):
"""Test TRTLLM MLA Triton kernels: pad_draft_extend_query_kernel and unpad_draft_extend_output_kernel."""
# Import the kernels
from sglang.srt.layers.attention.trtllm_mla_backend import (
pad_draft_extend_query_kernel,
unpad_draft_extend_output_kernel,
)
def _create_test_data(
self, batch_size, max_seq_len, num_heads, head_dim, dtype=torch.float32
):
"""Create test data for kernel testing."""
device = torch.device("cuda")
# Create sequence lengths (varying lengths for each batch)
seq_lens = torch.randint(
1, max_seq_len + 1, (batch_size,), device=device, dtype=torch.int32
)
# Create cumulative sequence lengths
cum_seq_lens = torch.zeros(batch_size + 1, device=device, dtype=torch.int32)
cum_seq_lens[1:] = torch.cumsum(seq_lens, dim=0)
# Create input query tensor (flattened format)
total_tokens = cum_seq_lens[-1].item()
q_input = torch.randn(
total_tokens, num_heads, head_dim, device=device, dtype=dtype
)
# Create padded query tensor (batch format)
padded_q = torch.zeros(
batch_size, max_seq_len, num_heads, head_dim, device=device, dtype=dtype
)
return q_input, padded_q, seq_lens, cum_seq_lens
def _create_test_output_data(
self,
batch_size,
token_per_batch,
tp_q_head_num,
v_head_dim,
dtype=torch.float32,
):
"""Create test data for unpad kernel testing."""
device = torch.device("cuda")
# Create accept lengths (varying lengths for each batch)
accept_lengths = torch.randint(
1, token_per_batch + 1, (batch_size,), device=device, dtype=torch.int32
)
# Create cumulative accept lengths
cum_accept_lengths = torch.zeros(
batch_size + 1, device=device, dtype=torch.int32
)
cum_accept_lengths[1:] = torch.cumsum(accept_lengths, dim=0)
# Create raw output tensor (batch format)
raw_out = torch.randn(
batch_size,
token_per_batch,
tp_q_head_num,
v_head_dim,
device=device,
dtype=dtype,
)
# Create output tensor (flattened format)
total_tokens = cum_accept_lengths[-1].item()
output = torch.empty(
total_tokens, tp_q_head_num, v_head_dim, device=device, dtype=dtype
)
return raw_out, output, accept_lengths, cum_accept_lengths
# Test 1: pad_draft_extend_query_kernel basic functionality
with self.subTest(test="pad_kernel_basic"):
batch_size = 4
max_seq_len = 8
num_heads = 16
head_dim = 64
q_input, padded_q, seq_lens, cum_seq_lens = _create_test_data(
self, batch_size, max_seq_len, num_heads, head_dim
)
# Launch kernel
BLOCK_SIZE = 64
grid = (batch_size * max_seq_len,)
pad_draft_extend_query_kernel[grid](
q_ptr=q_input,
padded_q_ptr=padded_q,
seq_lens_q_ptr=seq_lens,
cumsum_ptr=cum_seq_lens,
batch_size=batch_size,
max_seq_len=max_seq_len,
num_heads=num_heads,
head_dim=head_dim,
BLOCK_SIZE=BLOCK_SIZE,
)
# Verify the padding worked correctly
for i in range(batch_size):
seq_len = seq_lens[i].item()
# Check that valid positions are copied correctly
for pos in range(seq_len):
input_start = cum_seq_lens[i].item()
input_pos = input_start + pos
# Compare input and output for valid positions
input_data = q_input[input_pos]
output_data = padded_q[i, pos]
torch.testing.assert_close(
input_data, output_data, rtol=1e-5, atol=1e-6
)
# Check that invalid positions are zero
for pos in range(seq_len, max_seq_len):
output_data = padded_q[i, pos]
self.assertTrue(
torch.allclose(output_data, torch.zeros_like(output_data)),
f"Position {pos} in batch {i} should be zero",
)
# Test 2: unpad_draft_extend_output_kernel basic functionality
with self.subTest(test="unpad_kernel_basic"):
batch_size = 4
token_per_batch = 8
tp_q_head_num = 16
v_head_dim = 64
raw_out, output, accept_lengths, cum_accept_lengths = (
_create_test_output_data(
self, batch_size, token_per_batch, tp_q_head_num, v_head_dim
)
)
# Launch kernel
BLOCK_SIZE = 64
grid = (batch_size * token_per_batch,)
unpad_draft_extend_output_kernel[grid](
raw_out_ptr=raw_out,
output_ptr=output,
accept_length_ptr=accept_lengths,
cumsum_ptr=cum_accept_lengths,
batch_size=batch_size,
token_per_batch=token_per_batch,
tp_q_head_num=tp_q_head_num,
v_head_dim=v_head_dim,
BLOCK_SIZE=BLOCK_SIZE,
)
# Verify the unpadding worked correctly
for i in range(batch_size):
accept_len = accept_lengths[i].item()
output_start = cum_accept_lengths[i].item()
# Check that valid positions are copied correctly
for pos in range(accept_len):
input_data = raw_out[i, pos]
output_data = output[output_start + pos]
torch.testing.assert_close(
input_data, output_data, rtol=1e-5, atol=1e-6
)
if __name__ == "__main__":
unittest.main()