[Bugfix][Qwen4Exp] fix state index strides in fused PLE conv (#55375)

Signed-off-by: huanghaoyan.hhy <huanghaoyan.hhy@alibaba-inc.com>
This commit is contained in:
Harry Huang
2026-09-05 14:02:20 +00:00
committed by GitHub
parent bc96d76aa9
commit 28e605fb33
2 changed files with 31 additions and 3 deletions
+23 -1
View File
@@ -1077,6 +1077,8 @@ class _ConvBatchCase:
dilation: int = 3
spec_query_len: int = 1
graph_padding: int = 0
state_index_stride: int = 1
include_null_state: bool = True
def _make_conv_metadata(
@@ -1123,9 +1125,18 @@ def _make_conv_metadata(
dtype=torch.int32,
device=device,
)
if case.state_index_stride > 1:
strided_indices = torch.full(
(non_spec_state_indices.numel(), case.state_index_stride),
NULL_BLOCK_ID,
dtype=torch.int32,
device=device,
)
strided_indices[:, 0] = non_spec_state_indices
non_spec_state_indices = strided_indices[:, 0]
if case.num_decodes > 1:
non_spec_state_indices[case.num_decodes - 1] = NULL_BLOCK_ID
if num_prefills > 1:
if num_prefills > 1 and case.include_null_state:
non_spec_state_indices[case.num_decodes + 1] = NULL_BLOCK_ID
spec_query_start_loc = torch.tensor(
@@ -1203,6 +1214,15 @@ def _make_conv_metadata(
_ConvBatchCase(prefill_query_lens=(37, 0, 5, 128)),
id="prefill",
),
pytest.param(
_ConvBatchCase(
prefill_query_lens=(16, 676),
channels=10240,
state_index_stride=4,
include_null_state=False,
),
id="prefill-strided-state-indices",
),
pytest.param(
_ConvBatchCase(
spec_query_lens=(1, 4, 4, 0),
@@ -1243,6 +1263,8 @@ def test_fused_conv_correctness(
) -> None:
device = torch.device("cuda")
metadata, num_real_tokens = _make_conv_metadata(case, device)
if case.state_index_stride > 1:
assert metadata.state_indices_tensor.stride(0) == case.state_index_stride
module = Qwen4ExpPLELayer.__new__(Qwen4ExpPLELayer)
nn.Module.__init__(module)
module.conv_state_len = (case.kernel_size - 1) * case.dilation
+8 -2
View File
@@ -330,6 +330,7 @@ def _ple_conv_kernel(
has_token_map,
num_reqs,
bs_iters,
state_idx_stride,
state_bs,
state_ws,
state_cs,
@@ -386,7 +387,7 @@ def _ple_conv_kernel(
else:
slot_off = tl.full([], 0, tl.int32)
sid = tl.load(state_idx_ptr + r).to(tl.int64)
sid = tl.load(state_idx_ptr + r * state_idx_stride).to(tl.int64)
state_ok = sid != NULL_STATE_ID
sid_safe = tl.where(state_ok, sid, 0)
if HAS_INIT:
@@ -484,6 +485,7 @@ def _ple_conv_writeback_kernel(
has_init_ptr,
token_idx_ptr,
has_token_map,
state_idx_stride,
state_bs,
state_ws,
state_cs,
@@ -501,7 +503,7 @@ def _ple_conv_writeback_kernel(
c_offs = pid_c * BLOCK_C + tl.arange(0, BLOCK_C)
c_mask = c_offs < C
sid = tl.load(state_idx_ptr + r).to(tl.int64)
sid = tl.load(state_idx_ptr + r * state_idx_stride).to(tl.int64)
state_ok = sid != NULL_STATE_ID
if not state_ok:
return
@@ -612,6 +614,8 @@ def ple_conv(
num_warps = 4 if mode == "prefill" else 8
launch_pdl = current_platform.is_arch_support_pdl()
# Pure-prefill indices can be a strided block-table column view.
state_idx_stride = state_indices.stride(0)
# Constexpr flags eliminate accesses to optional None arguments. Without a
# token map, state_indices is an unused but device-resident placeholder.
@@ -628,6 +632,7 @@ def ple_conv(
token_indices is not None,
num_reqs,
binary_search_iters,
state_idx_stride,
state_bs,
state_ws,
state_cs,
@@ -654,6 +659,7 @@ def ple_conv(
has_initial_states,
token_indices if token_indices is not None else state_indices,
token_indices is not None,
state_idx_stride,
state_bs,
state_ws,
state_cs,