@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / a16133a0f1
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 6, 05:45 UTC

[Perf] [Bugfix] Fix Triton autotuning in inference for Qwen3.5 (#37338)

Signed-off-by: Artem Perevedentsev <aperevedents@nvidia.com>

Artem Perevedentsev committed Mar 23, 2026 at 09:37 UTC a16133a0f12e8e406206be1bc8041727d9f1ea7d
10 files changed +40 -39
vllm/model_executor/layers/fla/ops/chunk.py
+5 -5
@@ -30,7 +30,7 @@ def chunk_gated_delta_rule_fwd(
30 scale: float,
31 initial_state: torch.Tensor,
32 output_final_state: bool,
33 - cu_seqlens: torch.LongTensor | None = None,
33 + cu_seqlens: torch.Tensor | None = None,
34 ):
35 g = chunk_local_cumsum(g, chunk_size=64, cu_seqlens=cu_seqlens)
36 # obtain WY representation. u is actually the new v.
@@ -84,7 +84,7 @@ class ChunkGatedDeltaRuleFunction(torch.autograd.Function):
84 scale: float,
85 initial_state: torch.Tensor,
86 output_final_state: bool,
87 - cu_seqlens: torch.LongTensor | None = None,
87 + cu_seqlens: torch.Tensor | None = None,
88 use_qk_l2norm_in_kernel: bool = False,
89 ):
90 if use_qk_l2norm_in_kernel:
@@ -117,7 +117,7 @@ def chunk_gated_delta_rule(
117 scale: float = None,
118 initial_state: torch.Tensor = None,
119 output_final_state: bool = False,
120 - cu_seqlens: torch.LongTensor | None = None,
120 + cu_seqlens: torch.Tensor | None = None,
121 use_qk_l2norm_in_kernel: bool = False,
122 ):
123 r"""
@@ -141,7 +141,7 @@ def chunk_gated_delta_rule(
141 Default: `None`.
142 output_final_state (Optional[bool]):
143 Whether to output the final state of shape `[N, H, V, K]`. Default: `False`.
144 - cu_seqlens (torch.LongTensor):
144 + cu_seqlens (torch.Tensor):
145 Cumulative sequence lengths of shape `[N+1]` used for variable-length training,
146 consistent with the FlashAttention API.
147 Returns:
@@ -171,7 +171,7 @@ def chunk_gated_delta_rule(
171 # for variable-length inputs, the batch size `B` is expected to be 1 and `cu_seqlens` is required
172 >>> q, k, v, beta, g = map(lambda x: rearrange(x, 'b t ... -> 1 (b t) ...'), (q, k, v, beta, g))
173 # for a batch with 4 sequences, `cu_seqlens` with 5 start/end positions are expected
174 - >>> cu_seqlens = q.new_tensor([0, 2048, 4096, 6144, 8192], dtype=torch.long)
174 + >>> cu_seqlens = q.new_tensor([0, 2048, 4096, 6144, 8192], dtype=torch.int32)
175 >>> o_var, ht_var = chunk_gated_delta_rule(
176 q, k, v, g, beta,
177 initial_state=h0,
vllm/model_executor/layers/fla/ops/chunk_delta_h.py
+1 -1
@@ -288,7 +288,7 @@ def chunk_gated_delta_rule_fwd_h(
288 output_final_state: bool = False,
289 chunk_size: int = 64, # SY: remove this argument and force chunk size 64?
290 save_new_value: bool = True,
291 - cu_seqlens: torch.LongTensor | None = None,
291 + cu_seqlens: torch.Tensor | None = None,
292 ) -> tuple[torch.Tensor, torch.Tensor]:
293 # This kernel is slightly different from fla to support Q/K with different head numbers.
294 # In fla, Q/K always have the same head number, so Hg is always equal to H.
vllm/model_executor/layers/fla/ops/chunk_o.py
+1 -1
@@ -145,7 +145,7 @@ def chunk_fwd_o(
145 h: torch.Tensor,
146 g: torch.Tensor | None = None, # cumsum of log decay
147 scale: float | None = None,
148 - cu_seqlens: torch.LongTensor | None = None,
148 + cu_seqlens: torch.Tensor | None = None,
149 chunk_size: int = 64,
150 ) -> torch.Tensor:
151 B, T, Hg, K, V = *q.shape, v.shape[-1]
vllm/model_executor/layers/fla/ops/chunk_scaled_dot_kkt.py
+2 -2
@@ -102,7 +102,7 @@ def chunk_scaled_dot_kkt_fwd(
102 k: torch.Tensor,
103 g: torch.Tensor | None = None,
104 beta: torch.Tensor | None = None,
105 - cu_seqlens: torch.LongTensor | None = None,
105 + cu_seqlens: torch.Tensor | None = None,
106 chunk_size: int = 64,
107 output_dtype: torch.dtype = torch.float32,
108 ) -> torch.Tensor:
@@ -116,7 +116,7 @@ def chunk_scaled_dot_kkt_fwd(
116 The beta tensor of shape `[B, T, H]`.
117 g (torch.Tensor):
118 The cumulative sum of the gate tensor of shape `[B, T, H]`. Default: `None`.
119 - cu_seqlens (torch.LongTensor):
119 + cu_seqlens (torch.Tensor):
120 The cumulative sequence lengths of the input tensor.
121 Default: None
122 chunk_size (int):
vllm/model_executor/layers/fla/ops/fused_recurrent.py
+5 -5
@@ -184,7 +184,7 @@ def fused_recurrent_gated_delta_rule_fwd(
184 scale: float,
185 initial_state: torch.Tensor,
186 inplace_final_state: bool = True,
187 - cu_seqlens: torch.LongTensor | None = None,
187 + cu_seqlens: torch.Tensor | None = None,
188 ssm_state_indices: torch.Tensor | None = None,
189 num_accepted_tokens: torch.Tensor | None = None,
190 use_qk_l2norm_in_kernel: bool = False,
@@ -489,7 +489,7 @@ class FusedRecurrentFunction(torch.autograd.Function):
489 scale: float,
490 initial_state: torch.Tensor,
491 inplace_final_state: bool = True,
492 - cu_seqlens: torch.LongTensor | None = None,
492 + cu_seqlens: torch.Tensor | None = None,
493 ssm_state_indices: torch.Tensor | None = None,
494 num_accepted_tokens: torch.Tensor | None = None,
495 use_qk_l2norm_in_kernel: bool = False,
@@ -521,7 +521,7 @@ def fused_recurrent_gated_delta_rule(
521 scale: float = None,
522 initial_state: torch.Tensor = None,
523 inplace_final_state: bool = True,
524 - cu_seqlens: torch.LongTensor | None = None,
524 + cu_seqlens: torch.Tensor | None = None,
525 ssm_state_indices: torch.Tensor | None = None,
526 num_accepted_tokens: torch.Tensor | None = None,
527 use_qk_l2norm_in_kernel: bool = False,
@@ -549,7 +549,7 @@ def fused_recurrent_gated_delta_rule(
549 inplace_final_state: bool:
550 Whether to store the final state in-place to save memory.
551 Default: `True`.
552 - cu_seqlens (torch.LongTensor):
552 + cu_seqlens (torch.Tensor):
553 Cumulative sequence lengths of shape `[N+1]` used for variable-length training,
554 consistent with the FlashAttention API.
555 ssm_state_indices (Optional[torch.Tensor]):
@@ -583,7 +583,7 @@ def fused_recurrent_gated_delta_rule(
583 # for variable-length inputs, the batch size `B` is expected to be 1 and `cu_seqlens` is required
584 >>> q, k, v, g, beta = map(lambda x: rearrange(x, 'b t ... -> 1 (b t) ...'), (q, k, v, g, beta))
585 # for a batch with 4 sequences, `cu_seqlens` with 5 start/end positions are expected
586 - >>> cu_seqlens = q.new_tensor([0, 2048, 4096, 6144, 8192], dtype=torch.long)
586 + >>> cu_seqlens = q.new_tensor([0, 2048, 4096, 6144, 8192], dtype=torch.int32)
587 >>> o_var, ht_var = fused_gated_recurrent_delta_rule(
588 q, k, v, g, beta,
589 initial_state=h0,
vllm/model_executor/layers/fla/ops/fused_sigmoid_gating.py
+1 -1
@@ -191,7 +191,7 @@ def fused_sigmoid_gating_delta_rule_update(
191 scale: float = None,
192 initial_state: torch.Tensor = None,
193 inplace_final_state: bool = True,
194 - cu_seqlens: torch.LongTensor | None = None,
194 + cu_seqlens: torch.Tensor | None = None,
195 ssm_state_indices: torch.Tensor | None = None,
196 num_accepted_tokens: torch.Tensor | None = None,
197 use_qk_l2norm_in_kernel: bool = False,
vllm/model_executor/layers/fla/ops/index.py
+3 -7
@@ -15,14 +15,12 @@ from .utils import tensor_cache
15
16
17 @tensor_cache
18 -def prepare_lens(cu_seqlens: torch.LongTensor) -> torch.LongTensor:
18 +def prepare_lens(cu_seqlens: torch.Tensor) -> torch.Tensor:
19 return cu_seqlens[1:] - cu_seqlens[:-1]
20
21
22 @tensor_cache
23 -def prepare_chunk_indices(
24 - cu_seqlens: torch.LongTensor, chunk_size: int
25 -) -> torch.LongTensor:
23 +def prepare_chunk_indices(cu_seqlens: torch.Tensor, chunk_size: int) -> torch.Tensor:
24 indices = torch.cat(
25 [
26 torch.arange(n)
@@ -33,9 +31,7 @@ def prepare_chunk_indices(
31
32
33 @tensor_cache
36 -def prepare_chunk_offsets(
37 - cu_seqlens: torch.LongTensor, chunk_size: int
38 -) -> torch.LongTensor:
34 +def prepare_chunk_offsets(cu_seqlens: torch.Tensor, chunk_size: int) -> torch.Tensor:
35 return torch.cat(
36 [cu_seqlens.new_tensor([0]), triton.cdiv(prepare_lens(cu_seqlens), chunk_size)]
37 ).cumsum(-1)
vllm/model_executor/layers/fla/ops/kda.py
+8 -8
@@ -38,7 +38,7 @@ def fused_recurrent_kda_fwd(
38 scale: float,
39 initial_state: torch.Tensor,
40 inplace_final_state: bool = True,
41 - cu_seqlens: torch.LongTensor | None = None,
41 + cu_seqlens: torch.Tensor | None = None,
42 ssm_state_indices: torch.Tensor | None = None,
43 num_accepted_tokens: torch.Tensor | None = None,
44 use_qk_l2norm_in_kernel: bool = False,
@@ -116,7 +116,7 @@ def fused_recurrent_kda(
116 initial_state: torch.Tensor = None,
117 inplace_final_state: bool = True,
118 use_qk_l2norm_in_kernel: bool = True,
119 - cu_seqlens: torch.LongTensor | None = None,
119 + cu_seqlens: torch.Tensor | None = None,
120 ssm_state_indices: torch.LongTensor | None = None,
121 **kwargs,
122 ) -> tuple[torch.Tensor, torch.Tensor]:
@@ -720,7 +720,7 @@ def chunk_kda_scaled_dot_kkt_fwd(
720 gk: torch.Tensor | None = None,
721 beta: torch.Tensor | None = None,
722 scale: float | None = None,
723 - cu_seqlens: torch.LongTensor | None = None,
723 + cu_seqlens: torch.Tensor | None = None,
724 chunk_size: int = 64,
725 output_dtype: torch.dtype = torch.float32,
726 ) -> tuple[torch.Tensor, torch.Tensor]:
@@ -734,7 +734,7 @@ def chunk_kda_scaled_dot_kkt_fwd(
734 The beta tensor of shape `[B, T, H]`.
735 gk (torch.Tensor):
736 The cumulative sum of the gate tensor of shape `[B, T, H, K]` applied to the key tensor. Default: `None`.
737 - cu_seqlens (torch.LongTensor):
737 + cu_seqlens (torch.Tensor):
738 The cumulative sequence lengths of the input tensor.
739 Default: None
740 chunk_size (int):
@@ -964,7 +964,7 @@ def recompute_w_u_fwd(
964 A: torch.Tensor,
965 q: torch.Tensor | None = None,
966 gk: torch.Tensor | None = None,
967 - cu_seqlens: torch.LongTensor | None = None,
967 + cu_seqlens: torch.Tensor | None = None,
968 ) -> tuple[torch.Tensor, torch.Tensor]:
969 B, T, H, K, V = *k.shape, v.shape[-1]
970 BT = A.shape[-1]
@@ -1132,7 +1132,7 @@ def chunk_gla_fwd_o_gk(
1132 h: torch.Tensor,
1133 o: torch.Tensor,
1134 scale: float,
1135 - cu_seqlens: torch.LongTensor | None = None,
1135 + cu_seqlens: torch.Tensor | None = None,
1136 chunk_size: int = 64,
1137 ):
1138 B, T, H, K, V = *q.shape, v.shape[-1]
@@ -1176,7 +1176,7 @@ def chunk_kda_fwd(
1176 scale: float,
1177 initial_state: torch.Tensor,
1178 output_final_state: bool,
1179 - cu_seqlens: torch.LongTensor | None = None,
1179 + cu_seqlens: torch.Tensor | None = None,
1180 ):
1181 chunk_size = 64
1182 g = chunk_local_cumsum(g, chunk_size=chunk_size, cu_seqlens=cu_seqlens)
@@ -1236,7 +1236,7 @@ def chunk_kda(
1236 initial_state: torch.Tensor = None,
1237 output_final_state: bool = False,
1238 use_qk_l2norm_in_kernel: bool = False,
1239 - cu_seqlens: torch.LongTensor | None = None,
1239 + cu_seqlens: torch.Tensor | None = None,
1240 **kwargs,
1241 ):
1242 if scale is None:
vllm/model_executor/layers/fla/ops/wy_fast.py
+1 -1
@@ -122,7 +122,7 @@ def recompute_w_u_fwd(
122 beta: torch.Tensor,
123 g_cumsum: torch.Tensor,
124 A: torch.Tensor,
125 - cu_seqlens: torch.LongTensor | None,
125 + cu_seqlens: torch.Tensor | None,
126 ) -> tuple[torch.Tensor, torch.Tensor]:
127 B, T, Hg, K, V = *k.shape, v.shape[-1]
128 H = v.shape[-2]
vllm/model_executor/models/qwen3_next.py
+13 -8
@@ -119,7 +119,7 @@ def fi_chunk_gated_delta_rule(
119 beta: torch.Tensor,
120 initial_state: torch.Tensor,
121 output_final_state: bool,
122 - cu_seqlens: torch.LongTensor | None = None,
122 + cu_seqlens: torch.Tensor | None = None,
123 use_qk_l2norm_in_kernel: bool = True,
124 ):
125 from flashinfer.gdn_prefill import (
@@ -214,7 +214,7 @@ class ChunkGatedDeltaRule(CustomOp):
214 beta: torch.Tensor,
215 initial_state: torch.Tensor,
216 output_final_state: bool,
217 - cu_seqlens: torch.LongTensor | None = None,
217 + cu_seqlens: torch.Tensor | None = None,
218 use_qk_l2norm_in_kernel: bool = True,
219 ):
220 return fi_chunk_gated_delta_rule(
@@ -238,7 +238,7 @@ class ChunkGatedDeltaRule(CustomOp):
238 beta: torch.Tensor,
239 initial_state: torch.Tensor,
240 output_final_state: bool,
241 - cu_seqlens: torch.LongTensor | None = None,
241 + cu_seqlens: torch.Tensor | None = None,
242 use_qk_l2norm_in_kernel: bool = True,
243 ):
244 return fla_chunk_gated_delta_rule(
@@ -755,8 +755,13 @@ class Qwen3NextGatedDeltaNet(nn.Module, MambaBase):
755 v = torch.randn(
756 1, T, num_v_heads, self.head_v_dim, device=device, dtype=dtype
757 )
758 - g = torch.randn(1, T, num_v_heads, device=device, dtype=dtype)
759 - beta = torch.randn(1, T, num_v_heads, device=device, dtype=dtype)
758 + # NOTE: g and beta must have the same dtypes as during
759 + # inference, so we construct them with the same function
760 + # (fused_gdn_gating). dummy_a and dummy_b are throwaway
761 + # inputs required by that function.
762 + dummy_a = torch.randn(T, num_v_heads, device=device, dtype=dtype)
763 + dummy_b = torch.randn(T, num_v_heads, device=device, dtype=dtype)
764 + g, beta = fused_gdn_gating(self.A_log, dummy_a, dummy_b, self.dt_bias)
765 state = torch.zeros(
766 1,
767 num_v_heads,
@@ -765,7 +770,7 @@ class Qwen3NextGatedDeltaNet(nn.Module, MambaBase):
770 device=device,
771 dtype=state_dtype,
772 )
768 - cu_seqlens = torch.tensor([0, T], device=device, dtype=torch.long)
773 + cu_seqlens = torch.tensor([0, T], device=device, dtype=torch.int32)
774
775 try:
776 self.chunk_gated_delta_rule(
@@ -775,7 +780,7 @@ class Qwen3NextGatedDeltaNet(nn.Module, MambaBase):
780 g=g,
781 beta=beta,
782 initial_state=state,
778 - output_final_state=False,
783 + output_final_state=True,
784 cu_seqlens=cu_seqlens,
785 use_qk_l2norm_in_kernel=True,
786 )
@@ -795,7 +800,7 @@ class Qwen3NextGatedDeltaNet(nn.Module, MambaBase):
800 self.prefix,
801 )
802 finally:
798 - del q, k, v, g, beta, state, cu_seqlens
803 + del q, k, v, dummy_a, dummy_b, g, beta, state, cu_seqlens
804
805 torch.accelerator.empty_cache()
806