gdn_attn.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. # SPDX-License-Identifier: Apache-2.0
  2. # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
  3. """Backend for GatedDeltaNet attention."""
  4. from dataclasses import dataclass
  5. from typing import Literal
  6. import os
  7. import torch
  8. from vllm.config import VllmConfig
  9. from vllm.utils.torch_utils import async_tensor_h2d
  10. from vllm.v1.attention.backend import (
  11. AttentionBackend,
  12. AttentionCGSupport,
  13. AttentionMetadataBuilder,
  14. CommonAttentionMetadata,
  15. )
  16. from vllm.v1.attention.backends.utils import (
  17. NULL_BLOCK_ID,
  18. compute_causal_conv1d_metadata,
  19. mamba_get_block_table_tensor,
  20. split_decodes_and_prefills,
  21. )
  22. from vllm.v1.kv_cache_interface import MambaSpec
  23. class GDNAttentionBackend(AttentionBackend):
  24. @staticmethod
  25. def get_name() -> str:
  26. return "GDN_ATTN"
  27. @staticmethod
  28. def get_builder_cls() -> type["GDNAttentionMetadataBuilder"]:
  29. return GDNAttentionMetadataBuilder
  30. @classmethod
  31. def is_ssm(cls) -> bool:
  32. return True
  33. @dataclass
  34. class GDNAttentionMetadata:
  35. num_prefills: int
  36. num_prefill_tokens: int
  37. num_decodes: int
  38. num_decode_tokens: int
  39. num_spec_decodes: int
  40. num_spec_decode_tokens: int
  41. num_actual_tokens: int
  42. has_initial_state: torch.Tensor | None = None
  43. spec_query_start_loc: torch.Tensor | None = None # shape: [num_spec_decodes + 1,]
  44. non_spec_query_start_loc: torch.Tensor | None = (
  45. None # shape: [batch - num_spec_decodes + 1,]
  46. )
  47. spec_state_indices_tensor: torch.Tensor | None = None # shape: [batch, num_spec]
  48. non_spec_state_indices_tensor: torch.Tensor | None = (
  49. None # shape: [batch - num_spec_decodes,]
  50. )
  51. spec_sequence_masks: torch.Tensor | None = None # shape: [batch,]
  52. spec_token_indx: torch.Tensor | None = None
  53. non_spec_token_indx: torch.Tensor | None = None
  54. num_accepted_tokens: torch.Tensor | None = None # shape: [batch,]
  55. uniform_spec_sequence_length: int | None = None # None for ragged batches
  56. # Pre-computed FLA chunk metadata (avoids GPU->CPU sync in prepare_chunk_indices)
  57. chunk_indices: torch.Tensor | None = None
  58. chunk_offsets: torch.Tensor | None = None
  59. # Chunk-kernel inputs for prefill
  60. prefill_query_start_loc: torch.Tensor | None = None
  61. prefill_state_indices: torch.Tensor | None = None
  62. prefill_has_initial_state: torch.Tensor | None = None
  63. # The following attributes are for triton implementation of causal_conv1d
  64. nums_dict: dict | None = None
  65. batch_ptr: torch.Tensor | None = None
  66. token_chunk_offset_ptr: torch.Tensor | None = None
  67. # GB10: a decode step builds this metadata once per KDA KV-cache group (four
  68. # on GLM-5.3-Flash) from the same step inputs. The pure speculative-decode case
  69. # computes the group-independent parts once per step; each builder still reads
  70. # its own block table and copies into its own CUDA-graph buffers.
  71. _spec_step_cache: dict = {}
  72. # Diagnostic: while this file exists, every builder computes everything itself.
  73. _CACHE_OFF_FLAG = os.environ.get("VLLM_GDN_STEP_CACHE_OFF_FLAG", "/root/.cache/gdn-step-cache-off")
  74. class GDNAttentionMetadataBuilder(AttentionMetadataBuilder[GDNAttentionMetadata]):
  75. kv_cache_spec: MambaSpec
  76. _cudagraph_support = AttentionCGSupport.UNIFORM_BATCH
  77. reorder_batch_threshold: int = 1
  78. def __init__(
  79. self,
  80. kv_cache_spec: MambaSpec,
  81. layer_names: list[str],
  82. vllm_config: VllmConfig,
  83. device: torch.device,
  84. ):
  85. super().__init__(kv_cache_spec, layer_names, vllm_config, device)
  86. self.compilation_config = vllm_config.compilation_config
  87. self.speculative_config = vllm_config.speculative_config
  88. from vllm.model_executor.layers.mamba.gdn.qwen_gdn_linear_attn import (
  89. _resolve_gdn_prefill_backend,
  90. )
  91. self.gdn_prefill_backend: Literal["triton", "flashinfer", "cutedsl"]
  92. _, self.gdn_prefill_backend = _resolve_gdn_prefill_backend(vllm_config)
  93. if self.speculative_config:
  94. assert self.speculative_config.num_speculative_tokens is not None
  95. self.num_spec: int = self.speculative_config.num_speculative_tokens
  96. else:
  97. self.num_spec = 0
  98. self.use_spec_decode: bool = self.num_spec > 0
  99. self._init_reorder_batch_threshold(1, self.use_spec_decode)
  100. self.use_full_cuda_graph: bool = (
  101. self.compilation_config.cudagraph_mode.has_full_cudagraphs()
  102. )
  103. self.decode_cudagraph_max_bs: int = (
  104. self.vllm_config.scheduler_config.max_num_seqs * (self.num_spec + 1)
  105. )
  106. if self.compilation_config.max_cudagraph_capture_size is not None:
  107. self.decode_cudagraph_max_bs = min(
  108. self.decode_cudagraph_max_bs,
  109. self.compilation_config.max_cudagraph_capture_size,
  110. )
  111. self.spec_state_indices_tensor: torch.Tensor = torch.empty(
  112. (self.decode_cudagraph_max_bs, self.num_spec + 1),
  113. dtype=torch.int32,
  114. device=device,
  115. )
  116. self.non_spec_state_indices_tensor: torch.Tensor = torch.empty(
  117. (self.decode_cudagraph_max_bs,),
  118. dtype=torch.int32,
  119. device=device,
  120. )
  121. self.spec_sequence_masks: torch.Tensor = torch.empty(
  122. (self.decode_cudagraph_max_bs,),
  123. dtype=torch.bool,
  124. device=device,
  125. )
  126. self.spec_token_indx: torch.Tensor = torch.empty(
  127. (self.decode_cudagraph_max_bs * (self.num_spec + 1),),
  128. dtype=torch.int32,
  129. device=device,
  130. )
  131. self.non_spec_token_indx: torch.Tensor = torch.empty(
  132. (self.decode_cudagraph_max_bs * (self.num_spec + 1),),
  133. dtype=torch.int32,
  134. device=device,
  135. )
  136. self.spec_query_start_loc: torch.Tensor = torch.empty(
  137. (self.decode_cudagraph_max_bs + 1,),
  138. dtype=torch.int32,
  139. device=device,
  140. )
  141. self.non_spec_query_start_loc: torch.Tensor = torch.empty(
  142. (self.decode_cudagraph_max_bs + 1,),
  143. dtype=torch.int32,
  144. device=device,
  145. )
  146. self.num_accepted_tokens: torch.Tensor = torch.empty(
  147. (self.decode_cudagraph_max_bs,),
  148. dtype=torch.int32,
  149. device=device,
  150. )
  151. def _build_chunk_metadata(
  152. self,
  153. prefill_query_start_loc: torch.Tensor,
  154. prefill_query_start_loc_cpu: torch.Tensor,
  155. device: torch.device,
  156. ) -> tuple[torch.Tensor, torch.Tensor]:
  157. from vllm.third_party.flash_linear_attention.ops.utils import FLA_CHUNK_SIZE
  158. if self.gdn_prefill_backend == "cutedsl":
  159. from vllm.model_executor.layers.mamba.ops.gdn_chunk_cutedsl import (
  160. prepare_metadata_cutedsl,
  161. )
  162. assert prefill_query_start_loc is not None
  163. assert prefill_query_start_loc_cpu is not None
  164. total_tokens = int(prefill_query_start_loc_cpu[-1].item())
  165. return prepare_metadata_cutedsl(
  166. prefill_query_start_loc,
  167. total_tokens,
  168. FLA_CHUNK_SIZE,
  169. )
  170. # Only prefill batches use FLA chunk ops.
  171. # Pre-compute on CPU and async-copy to GPU to avoid
  172. # GPU→CPU sync (.tolist()) in prepare_chunk_indices.
  173. from vllm.third_party.flash_linear_attention.ops.index import (
  174. prepare_chunk_indices,
  175. prepare_chunk_offsets,
  176. )
  177. assert prefill_query_start_loc_cpu is not None
  178. return (
  179. async_tensor_h2d(
  180. prepare_chunk_indices(prefill_query_start_loc_cpu, FLA_CHUNK_SIZE),
  181. device=device,
  182. ),
  183. async_tensor_h2d(
  184. prepare_chunk_offsets(prefill_query_start_loc_cpu, FLA_CHUNK_SIZE),
  185. device=device,
  186. ),
  187. )
  188. def build( # type: ignore[override]
  189. self,
  190. common_prefix_len: int,
  191. common_attn_metadata: CommonAttentionMetadata,
  192. num_accepted_tokens: torch.Tensor | None = None,
  193. num_decode_draft_tokens_cpu: torch.Tensor | None = None,
  194. fast_build: bool = False,
  195. ) -> GDNAttentionMetadata:
  196. m = common_attn_metadata
  197. num_accepted_tokens_in = num_accepted_tokens
  198. c = _spec_step_cache
  199. # A builder that already used this entry is on the next step: recompute.
  200. if (
  201. c
  202. and not os.path.exists(_CACHE_OFF_FLAG)
  203. and id(self) not in c["served"]
  204. and c["num_spec"] == self.num_spec
  205. and num_decode_draft_tokens_cpu is not None
  206. and num_accepted_tokens is not None
  207. and c["accepted"] == (num_accepted_tokens.data_ptr(), tuple(num_accepted_tokens.shape))
  208. and torch.equal(c["qsl_cpu"], m.query_start_loc_cpu)
  209. and torch.equal(c["drafts"], num_decode_draft_tokens_cpu)
  210. ):
  211. c["served"].add(id(self))
  212. return self._build_spec_from_cache(m, c)
  213. _spec_step_cache.clear()
  214. query_start_loc = m.query_start_loc
  215. query_start_loc_cpu = m.query_start_loc_cpu
  216. nums_dict, batch_ptr, token_chunk_offset_ptr = None, None, None
  217. block_table_tensor = mamba_get_block_table_tensor(
  218. m.block_table_tensor,
  219. m.seq_lens,
  220. self.kv_cache_spec,
  221. self.vllm_config.cache_config.mamba_cache_mode,
  222. )
  223. uniform_spec_sequence_length = None
  224. spec_sequence_masks_cpu: torch.Tensor | None = None
  225. if not self.use_spec_decode or num_decode_draft_tokens_cpu is None:
  226. spec_sequence_masks = None
  227. num_spec_decodes = 0
  228. else:
  229. spec_sequence_masks_cpu = num_decode_draft_tokens_cpu >= 0
  230. num_spec_decodes = spec_sequence_masks_cpu.sum().item()
  231. if (
  232. num_spec_decodes == 0
  233. or num_decode_draft_tokens_cpu[spec_sequence_masks_cpu].sum().item()
  234. == 0
  235. ):
  236. num_spec_decodes = 0
  237. spec_sequence_masks = None
  238. spec_sequence_masks_cpu = None
  239. else:
  240. spec_sequence_masks = async_tensor_h2d(
  241. spec_sequence_masks_cpu, device=query_start_loc.device
  242. )
  243. if spec_sequence_masks is None:
  244. # V2 already excludes prefills from full decode graphs via
  245. # has_prefill. Classify first chunks as prefills to mask recycled
  246. # state; resumed one-token chunks can still use the decode kernels.
  247. assert m.seq_lens_cpu_upper_bound is not None
  248. query_lens_cpu = query_start_loc_cpu.diff()
  249. no_prior_state = (query_lens_cpu > 0) & (
  250. m.seq_lens_cpu_upper_bound <= query_lens_cpu
  251. )
  252. # Capture batches also have seq_len == query_len, but are not
  253. # prefills.
  254. if m.is_prefilling is not None:
  255. no_prior_state &= m.is_prefilling
  256. else:
  257. no_prior_state = torch.zeros_like(no_prior_state)
  258. num_decodes, num_prefills, num_decode_tokens, num_prefill_tokens = (
  259. split_decodes_and_prefills(
  260. m.replace(is_prefilling=no_prior_state),
  261. decode_threshold=1,
  262. treat_short_extends_as_decodes=False,
  263. )
  264. )
  265. # Exclude trailing padding from both prefill counts.
  266. if num_prefills:
  267. num_prefills -= int((query_lens_cpu[num_decodes:] == 0).sum())
  268. num_prefill_tokens = (
  269. int(query_start_loc_cpu[num_decodes + num_prefills])
  270. - num_decode_tokens
  271. )
  272. num_spec_decode_tokens = 0
  273. spec_token_indx = None
  274. non_spec_token_indx = None
  275. spec_state_indices_tensor = None
  276. non_spec_state_indices_tensor = block_table_tensor[:, 0]
  277. spec_query_start_loc = None
  278. non_spec_query_start_loc = query_start_loc
  279. non_spec_query_start_loc_cpu = query_start_loc_cpu
  280. num_accepted_tokens = None
  281. else:
  282. query_lens = query_start_loc[1:] - query_start_loc[:-1]
  283. assert spec_sequence_masks_cpu is not None
  284. non_spec_sequence_masks_cpu = ~spec_sequence_masks_cpu
  285. query_lens_cpu = query_start_loc_cpu[1:] - query_start_loc_cpu[:-1]
  286. spec_query_lens_cpu = query_lens_cpu[spec_sequence_masks_cpu]
  287. if spec_query_lens_cpu.numel() > 0:
  288. first_spec_sequence_length = int(spec_query_lens_cpu[0])
  289. if first_spec_sequence_length > 0 and bool(
  290. torch.all(spec_query_lens_cpu == first_spec_sequence_length)
  291. ):
  292. uniform_spec_sequence_length = first_spec_sequence_length
  293. # Use CPU tensors to avoid CPU-GPU sync
  294. non_spec_query_lens_cpu = query_lens_cpu[non_spec_sequence_masks_cpu]
  295. num_decodes = (non_spec_query_lens_cpu == 1).sum().item()
  296. # Exclude zero-length padded sequences from prefill count.
  297. num_zero_len = (non_spec_query_lens_cpu == 0).sum().item()
  298. num_prefills = non_spec_query_lens_cpu.size(0) - num_decodes - num_zero_len
  299. num_decode_tokens = num_decodes
  300. num_prefill_tokens = (
  301. non_spec_query_lens_cpu.sum().item() - num_decode_tokens
  302. )
  303. num_spec_decode_tokens = (
  304. query_lens_cpu.sum().item() - num_prefill_tokens - num_decode_tokens
  305. )
  306. # num_decodes and num_spec_decodes are mutually exclusive.
  307. # Reclassify non-spec decodes as prefills when spec decodes
  308. # exist — the prefill kernel handles 1-token sequences with
  309. # initial state correctly, producing identical results.
  310. if num_decodes > 0 and num_spec_decodes > 0:
  311. num_prefills += num_decodes
  312. num_prefill_tokens += num_decode_tokens
  313. num_decodes = 0
  314. num_decode_tokens = 0
  315. if num_prefills == 0 and num_decodes == 0:
  316. spec_token_size = min(
  317. num_spec_decodes * (self.num_spec + 1),
  318. query_start_loc_cpu[-1].item(),
  319. )
  320. spec_token_indx = torch.arange(
  321. spec_token_size,
  322. dtype=torch.int32,
  323. device=query_start_loc.device,
  324. )
  325. non_spec_token_indx = torch.empty(
  326. 0, dtype=torch.int32, device=query_start_loc.device
  327. )
  328. # Filter by spec_sequence_masks to exclude padded sequences
  329. spec_state_indices_tensor = block_table_tensor[
  330. spec_sequence_masks_cpu, : self.num_spec + 1
  331. ]
  332. non_spec_state_indices_tensor = None
  333. # Padded sequences are always at the back, so the first
  334. # num_spec_decodes + 1 entries of query_start_loc already
  335. # contain the correct cumulative token counts.
  336. spec_query_start_loc = query_start_loc[: num_spec_decodes + 1]
  337. non_spec_query_start_loc = None
  338. non_spec_query_start_loc_cpu = None
  339. else:
  340. spec_token_masks = torch.repeat_interleave(
  341. spec_sequence_masks,
  342. query_lens,
  343. output_size=query_start_loc_cpu[-1].item(),
  344. )
  345. index = torch.argsort(spec_token_masks, stable=True)
  346. num_non_spec_tokens = num_prefill_tokens + num_decode_tokens
  347. non_spec_token_indx = index[:num_non_spec_tokens]
  348. spec_token_indx = index[num_non_spec_tokens:]
  349. spec_state_indices_tensor = block_table_tensor[
  350. spec_sequence_masks_cpu, : self.num_spec + 1
  351. ]
  352. non_spec_state_indices_tensor = block_table_tensor[
  353. non_spec_sequence_masks_cpu, 0
  354. ]
  355. spec_query_start_loc = torch.zeros(
  356. num_spec_decodes + 1,
  357. dtype=torch.int32,
  358. device=query_start_loc.device,
  359. )
  360. torch.cumsum(
  361. query_lens[spec_sequence_masks_cpu],
  362. dim=0,
  363. out=spec_query_start_loc[1:],
  364. )
  365. non_spec_query_start_loc = torch.zeros(
  366. query_lens.size(0) - num_spec_decodes + 1,
  367. dtype=torch.int32,
  368. device=query_start_loc.device,
  369. )
  370. torch.cumsum(
  371. query_lens[non_spec_sequence_masks_cpu],
  372. dim=0,
  373. out=non_spec_query_start_loc[1:],
  374. )
  375. non_spec_query_start_loc_cpu = torch.zeros(
  376. query_lens_cpu.size(0) - num_spec_decodes + 1,
  377. dtype=torch.int32,
  378. )
  379. torch.cumsum(
  380. query_lens_cpu[non_spec_sequence_masks_cpu],
  381. dim=0,
  382. out=non_spec_query_start_loc_cpu[1:],
  383. )
  384. assert num_accepted_tokens is not None
  385. num_accepted_tokens = num_accepted_tokens[spec_sequence_masks_cpu]
  386. chunk_indices: torch.Tensor | None = None
  387. chunk_offsets: torch.Tensor | None = None
  388. prefill_query_start_loc: torch.Tensor | None = None
  389. prefill_state_indices: torch.Tensor | None = None
  390. prefill_has_initial_state: torch.Tensor | None = None
  391. if num_prefills > 0:
  392. # In a mixed non-spec batch, decodes are peeled off to the recurrent
  393. # kernel (decode-first front slice), so build chunk metadata from the
  394. # rebased prefill-only cu_seqlens; otherwise use the full non-spec one.
  395. # _forward_core keys off the same condition, so they agree.
  396. if spec_sequence_masks is None and num_decodes > 0:
  397. assert non_spec_query_start_loc is not None
  398. assert non_spec_query_start_loc_cpu is not None
  399. assert non_spec_state_indices_tensor is not None
  400. prefill_query_start_loc = (
  401. non_spec_query_start_loc[num_decodes:] - num_decode_tokens
  402. )
  403. prefill_query_start_loc_cpu = (
  404. non_spec_query_start_loc_cpu[num_decodes:] - num_decode_tokens
  405. )
  406. prefill_state_indices = non_spec_state_indices_tensor[num_decodes:]
  407. else:
  408. prefill_query_start_loc = non_spec_query_start_loc
  409. prefill_query_start_loc_cpu = non_spec_query_start_loc_cpu
  410. prefill_state_indices = non_spec_state_indices_tensor
  411. chunk_indices, chunk_offsets = self._build_chunk_metadata(
  412. prefill_query_start_loc,
  413. prefill_query_start_loc_cpu,
  414. query_start_loc.device,
  415. )
  416. if num_prefills > 0:
  417. context_lens_tensor = m.compute_num_computed_tokens()
  418. has_initial_state = context_lens_tensor > 0
  419. if spec_sequence_masks_cpu is not None:
  420. has_initial_state = has_initial_state[~spec_sequence_masks_cpu]
  421. assert non_spec_query_start_loc_cpu is not None
  422. nums_dict, batch_ptr, token_chunk_offset_ptr = (
  423. compute_causal_conv1d_metadata(
  424. non_spec_query_start_loc_cpu,
  425. device=query_start_loc.device,
  426. )
  427. )
  428. if spec_sequence_masks is None and num_decodes > 0:
  429. prefill_has_initial_state = has_initial_state[num_decodes:]
  430. else:
  431. prefill_has_initial_state = has_initial_state
  432. else:
  433. has_initial_state = None
  434. # Function code counted on either presency non-spec decode or spec decode,
  435. # but not both.
  436. assert not (num_decodes > 0 and num_spec_decodes > 0), (
  437. f"num_decodes: {num_decodes}, num_spec_decodes: {num_spec_decodes}"
  438. )
  439. # Prepare per-request tensors for cudagraph. m.num_actual_tokens is
  440. # token-padded for FULL graph replay, but the GDN state/query/accepted
  441. # metadata below is indexed by request.
  442. batch_size = m.num_reqs
  443. if (
  444. self.use_full_cuda_graph
  445. and num_prefills == 0
  446. and num_decodes == 0
  447. and num_spec_decodes <= self.decode_cudagraph_max_bs
  448. and num_spec_decode_tokens <= self.decode_cudagraph_max_bs
  449. ):
  450. assert spec_sequence_masks is not None
  451. _spec_step_cache.clear()
  452. _spec_step_cache.update(
  453. qsl_cpu=m.query_start_loc_cpu.clone(), drafts=num_decode_draft_tokens_cpu.clone(),
  454. accepted=(num_accepted_tokens_in.data_ptr(), tuple(num_accepted_tokens_in.shape)),
  455. num_spec=self.num_spec,
  456. masks_cpu=spec_sequence_masks_cpu, masks=spec_sequence_masks,
  457. spec_token_indx=spec_token_indx, non_spec_token_indx=non_spec_token_indx,
  458. spec_query_start_loc=spec_query_start_loc, num_accepted_tokens=num_accepted_tokens,
  459. num_spec_decodes=num_spec_decodes, num_spec_decode_tokens=num_spec_decode_tokens,
  460. num_prefill_tokens=num_prefill_tokens, num_decode_tokens=num_decode_tokens,
  461. uniform=uniform_spec_sequence_length, served={id(self)})
  462. self.spec_state_indices_tensor[:num_spec_decodes].copy_(
  463. spec_state_indices_tensor, non_blocking=True
  464. )
  465. spec_state_indices_tensor = self.spec_state_indices_tensor[:batch_size]
  466. spec_state_indices_tensor[num_spec_decodes:].fill_(NULL_BLOCK_ID)
  467. self.spec_sequence_masks[:num_spec_decodes].copy_(
  468. spec_sequence_masks[:num_spec_decodes], non_blocking=True
  469. )
  470. spec_sequence_masks = self.spec_sequence_masks[:batch_size]
  471. spec_sequence_masks[num_spec_decodes:].fill_(False)
  472. assert non_spec_token_indx is not None and spec_token_indx is not None
  473. self.non_spec_token_indx[: non_spec_token_indx.size(0)].copy_(
  474. non_spec_token_indx, non_blocking=True
  475. )
  476. non_spec_token_indx = self.non_spec_token_indx[
  477. : non_spec_token_indx.size(0)
  478. ]
  479. self.spec_token_indx[: spec_token_indx.size(0)].copy_(
  480. spec_token_indx, non_blocking=True
  481. )
  482. spec_token_indx = self.spec_token_indx[: spec_token_indx.size(0)]
  483. self.spec_query_start_loc[: num_spec_decodes + 1].copy_(
  484. spec_query_start_loc, non_blocking=True
  485. )
  486. spec_num_query_tokens = spec_query_start_loc[-1] # type: ignore[index]
  487. spec_query_start_loc = self.spec_query_start_loc[: batch_size + 1]
  488. spec_query_start_loc[num_spec_decodes + 1 :].fill_(spec_num_query_tokens)
  489. self.num_accepted_tokens[:num_spec_decodes].copy_(
  490. num_accepted_tokens, non_blocking=True
  491. )
  492. num_accepted_tokens = self.num_accepted_tokens[:batch_size]
  493. num_accepted_tokens[num_spec_decodes:].fill_(1)
  494. if (
  495. self.use_full_cuda_graph
  496. and num_prefills == 0
  497. and num_spec_decodes == 0
  498. and num_decodes <= self.decode_cudagraph_max_bs
  499. ):
  500. self.non_spec_state_indices_tensor[:num_decodes].copy_(
  501. non_spec_state_indices_tensor, non_blocking=True
  502. )
  503. non_spec_state_indices_tensor = self.non_spec_state_indices_tensor[
  504. :batch_size
  505. ]
  506. non_spec_state_indices_tensor[num_decodes:].fill_(NULL_BLOCK_ID)
  507. self.non_spec_query_start_loc[: num_decodes + 1].copy_(
  508. non_spec_query_start_loc, non_blocking=True
  509. )
  510. non_spec_num_query_tokens = non_spec_query_start_loc[-1] # type: ignore[index]
  511. non_spec_query_start_loc = self.non_spec_query_start_loc[: batch_size + 1]
  512. non_spec_query_start_loc[num_decodes + 1 :].fill_(non_spec_num_query_tokens)
  513. attn_metadata = GDNAttentionMetadata(
  514. num_prefills=num_prefills,
  515. num_prefill_tokens=num_prefill_tokens,
  516. num_decodes=num_decodes,
  517. num_decode_tokens=num_decode_tokens,
  518. num_spec_decodes=num_spec_decodes,
  519. num_spec_decode_tokens=num_spec_decode_tokens,
  520. num_actual_tokens=m.num_actual_tokens,
  521. has_initial_state=has_initial_state,
  522. chunk_indices=chunk_indices,
  523. chunk_offsets=chunk_offsets,
  524. prefill_query_start_loc=prefill_query_start_loc,
  525. prefill_state_indices=prefill_state_indices,
  526. prefill_has_initial_state=prefill_has_initial_state,
  527. spec_query_start_loc=spec_query_start_loc,
  528. non_spec_query_start_loc=non_spec_query_start_loc,
  529. spec_state_indices_tensor=spec_state_indices_tensor,
  530. non_spec_state_indices_tensor=non_spec_state_indices_tensor,
  531. spec_sequence_masks=spec_sequence_masks,
  532. spec_token_indx=spec_token_indx,
  533. non_spec_token_indx=non_spec_token_indx,
  534. num_accepted_tokens=num_accepted_tokens,
  535. uniform_spec_sequence_length=uniform_spec_sequence_length,
  536. nums_dict=nums_dict,
  537. batch_ptr=batch_ptr,
  538. token_chunk_offset_ptr=token_chunk_offset_ptr,
  539. )
  540. return attn_metadata
  541. def _build_spec_from_cache(self, m: CommonAttentionMetadata, c: dict) -> GDNAttentionMetadata:
  542. """The pure spec-decode full-graph case of build(), from this step's cache."""
  543. block_table_tensor = mamba_get_block_table_tensor(
  544. m.block_table_tensor,
  545. m.seq_lens,
  546. self.kv_cache_spec,
  547. self.vllm_config.cache_config.mamba_cache_mode,
  548. )
  549. n, batch_size = c["num_spec_decodes"], m.num_reqs
  550. self.spec_state_indices_tensor[:n].copy_(
  551. block_table_tensor[c["masks_cpu"], : self.num_spec + 1], non_blocking=True
  552. )
  553. spec_state_indices_tensor = self.spec_state_indices_tensor[:batch_size]
  554. spec_state_indices_tensor[n:].fill_(NULL_BLOCK_ID)
  555. self.spec_sequence_masks[:n].copy_(c["masks"][:n], non_blocking=True)
  556. spec_sequence_masks = self.spec_sequence_masks[:batch_size]
  557. spec_sequence_masks[n:].fill_(False)
  558. nsi, si = c["non_spec_token_indx"], c["spec_token_indx"]
  559. self.non_spec_token_indx[: nsi.size(0)].copy_(nsi, non_blocking=True)
  560. self.spec_token_indx[: si.size(0)].copy_(si, non_blocking=True)
  561. self.spec_query_start_loc[: n + 1].copy_(c["spec_query_start_loc"], non_blocking=True)
  562. spec_query_start_loc = self.spec_query_start_loc[: batch_size + 1]
  563. spec_query_start_loc[n + 1 :].fill_(c["spec_query_start_loc"][-1])
  564. self.num_accepted_tokens[:n].copy_(c["num_accepted_tokens"], non_blocking=True)
  565. num_accepted_tokens = self.num_accepted_tokens[:batch_size]
  566. num_accepted_tokens[n:].fill_(1)
  567. return GDNAttentionMetadata(
  568. num_prefills=0,
  569. num_prefill_tokens=c["num_prefill_tokens"],
  570. num_decodes=0,
  571. num_decode_tokens=c["num_decode_tokens"],
  572. num_spec_decodes=n,
  573. num_spec_decode_tokens=c["num_spec_decode_tokens"],
  574. num_actual_tokens=m.num_actual_tokens,
  575. has_initial_state=None,
  576. chunk_indices=None,
  577. chunk_offsets=None,
  578. prefill_query_start_loc=None,
  579. prefill_state_indices=None,
  580. prefill_has_initial_state=None,
  581. spec_query_start_loc=spec_query_start_loc,
  582. non_spec_query_start_loc=None,
  583. spec_state_indices_tensor=spec_state_indices_tensor,
  584. non_spec_state_indices_tensor=None,
  585. spec_sequence_masks=spec_sequence_masks,
  586. spec_token_indx=self.spec_token_indx[: si.size(0)],
  587. non_spec_token_indx=self.non_spec_token_indx[: nsi.size(0)],
  588. num_accepted_tokens=num_accepted_tokens,
  589. uniform_spec_sequence_length=c["uniform"],
  590. nums_dict=None,
  591. batch_ptr=None,
  592. token_chunk_offset_ptr=None,
  593. )
  594. def build_for_cudagraph_capture(
  595. self, common_attn_metadata: CommonAttentionMetadata
  596. ):
  597. """This method builds the metadata for full cudagraph capture.
  598. Currently, only decode is supported for full cudagraphs with Mamba.
  599. """
  600. m = common_attn_metadata
  601. assert (
  602. m.num_reqs <= self.decode_cudagraph_max_bs
  603. and m.num_actual_tokens <= self.decode_cudagraph_max_bs
  604. ), (
  605. f"GDN only supports decode-only full CUDAGraph capture. "
  606. f"Make sure batch size ({m.num_reqs}) <= "
  607. f"cudagraph capture sizes ({self.decode_cudagraph_max_bs}), "
  608. f"and number of tokens ({m.num_actual_tokens}) <= "
  609. f"cudagraph capture sizes ({self.decode_cudagraph_max_bs})."
  610. )
  611. num_accepted_tokens = torch.diff(m.query_start_loc)
  612. num_decode_draft_tokens_cpu = torch.diff(m.query_start_loc_cpu).sub_(1)
  613. assert num_decode_draft_tokens_cpu.shape == num_accepted_tokens.shape
  614. return self.build(0, m, num_accepted_tokens, num_decode_draft_tokens_cpu)