1#![allow(unused_imports, dead_code, clippy::too_many_arguments)]
9
10use anyhow::Result;
11use spark_runtime::gpu::DevicePtr;
12use spark_runtime::kv_cache::PagedKvCache;
13
14use super::types::{PinnedMetaStaging, TransformerModel};
15use crate::layer::{AttnMetadataDev, LayerState};
16use crate::speculative::DraftProposer;
17use crate::traits::{ChunkedPrefillPageMetadata, Model, PrefillSlice, SequenceState};
18use crate::weight_map::{DenseWeight, MtpWeights};
19
20mod async_chkpt;
21mod decode_a;
22mod decode_a2;
23mod decode_a3;
24mod decode_a_diag;
25mod decode_b;
26mod decode_b2;
27pub(in crate::model) mod decode_checkpoint;
28mod decode_graph_key;
29mod decode_multi_seq_gate;
30mod drafter_prefill;
31mod ep_misc;
32mod gdn_woa;
33mod graph_borrow;
34mod lm_head_batched;
35mod meta;
36mod prefill_a;
37mod prefill_b;
38mod prefill_c;
39mod prefill_d;
40mod prefix_reuse;
41mod sequence;
42mod speculative;
43pub(in crate::model) mod ssm_fault_in;
44mod verify_a;
45mod verify_a_ssm;
46mod verify_b;
47mod verify_c;
48mod verify_c2;
49mod verify_d;
50mod verify_e;
51pub(in crate::model) mod verify_e2;
52mod verify_fused;
53
54impl Model for TransformerModel {
55 fn teardown(&mut self) -> Result<()> {
56 self.release_pools()
57 }
58
59 #[cfg(feature = "cuda")]
62 fn poll_innerq(&self) {
63 if let Some(driver) = self.innerq.as_ref()
64 && let Err(e) = driver.maybe_finalize(128)
65 {
66 tracing::warn!("InnerQ maybe_finalize failed: {e:#}");
67 }
68 }
69
70 fn prepare_vision_embed(&self, images: &[crate::VisionItem]) -> Result<()> {
71 self.prepare_vision_embed_dispatch(images)
72 }
73 fn prepare_vision_embed_batched(
74 &self,
75 per_request: &[Vec<crate::VisionItem>],
76 ) -> Result<Vec<(usize, usize, usize, usize)>> {
77 self.prepare_vision_embed_batched_dispatch(per_request)
78 }
79 fn ep_sync_vision_embeds(&self, tokens: &[u32]) -> Result<()> {
80 TransformerModel::ep_sync_vision_embeds(self, tokens)
81 }
82 fn set_vision_slice_base(&self, row_base: usize, grid_base: usize, owned_images: usize) {
83 *self.vision_row_base.lock() = row_base;
84 *self.vision_grid_base.lock() = grid_base;
85 *self.vision_owned_images.lock() = owned_images;
86 }
87 fn tokens_contain_vision_pad(&self, tokens: &[u32]) -> bool {
94 self.tokens_have_vision_pad(tokens)
95 }
96 fn prefill(&self, tokens: &[u32], seq: &mut SequenceState, stream: u64) -> Result<DevicePtr> {
97 self.stamp_overlay_route(seq.adapter_slot);
98 let logits = self.prefill_dispatch(tokens, seq, stream)?;
99 self.try_eager_drafter_prefill(seq, true, stream);
100 Ok(logits)
101 }
102 fn prefill_chunk(
103 &self,
104 tokens: &[u32],
105 seq: &mut SequenceState,
106 chunk_start: usize,
107 chunk_len: usize,
108 is_last_chunk: bool,
109 stream: u64,
110 ) -> Result<DevicePtr> {
111 self.stamp_overlay_route(seq.adapter_slot);
112 let logits = self.prefill_chunk_dispatch(
113 tokens,
114 seq,
115 chunk_start,
116 chunk_len,
117 is_last_chunk,
118 stream,
119 )?;
120 self.try_eager_drafter_prefill(seq, is_last_chunk, stream);
121 Ok(logits)
122 }
123 fn prefill_twophase(
124 &self,
125 tokens: &[u32],
126 seq: &mut SequenceState,
127 chunk_size: usize,
128 stream: u64,
129 ) -> Result<DevicePtr> {
130 self.stamp_overlay_route(seq.adapter_slot);
131 let logits = self.prefill_twophase_dispatch(tokens, seq, chunk_size, stream)?;
132 self.try_eager_drafter_prefill(seq, true, stream);
133 Ok(logits)
134 }
135 fn decode(&self, token: u32, seq: &mut SequenceState, _stream: u64) -> Result<DevicePtr> {
136 self.stamp_overlay_route(seq.adapter_slot);
137 self.stamp_decode_moe_single(seq.adapter_slot);
138 self.decode_dispatch(token, seq, _stream)
139 }
140 fn decode_batch(
141 &self,
142 tokens: &[u32],
143 seqs: &mut [&mut SequenceState],
144 stream: u64,
145 ) -> Result<DevicePtr> {
146 self.stamp_overlay_route_batch(seqs);
147 self.stamp_decode_moe_batch(seqs);
148 let r = self.decode_batch_dispatch(tokens, seqs, stream);
149 if r.is_err() {
150 self.gpu.abort_capture_if_active(self.gpu.default_stream());
157 }
158 r
159 }
160 fn mixed_forward(
161 &self,
162 decode_tokens: &[u32],
163 decode_seqs: &mut [&mut SequenceState],
164 prefill_tokens: &[u32],
165 prefill_seq: &mut SequenceState,
166 prefill_chunk_start: usize,
167 prefill_chunk_len: usize,
168 prefill_is_last: bool,
169 stream: u64,
170 ) -> Result<crate::traits::MixedForwardResult> {
171 self.overlay_route_slot
174 .store(i32::MIN, std::sync::atomic::Ordering::Relaxed);
175 self.stamp_decode_moe_batch(decode_seqs);
177 let r = self.mixed_forward_dispatch(
178 decode_tokens,
179 decode_seqs,
180 prefill_tokens,
181 prefill_seq,
182 prefill_chunk_start,
183 prefill_chunk_len,
184 prefill_is_last,
185 stream,
186 );
187 if r.is_err() {
188 self.gpu.abort_capture_if_active(self.gpu.default_stream());
191 }
192 let out = r?;
193 self.try_eager_drafter_prefill(prefill_seq, prefill_is_last, stream);
194 Ok(out)
195 }
196
197 fn prefill_batch_chunk(
202 &self,
203 streams: &mut [PrefillSlice<'_>],
204 stream: u64,
205 ) -> Result<Vec<DevicePtr>> {
206 self.prefill_batch_chunk_rows(streams, stream, 0)
207 }
208 fn prefill_batch_chunk_rows(
211 &self,
212 streams: &mut [PrefillSlice<'_>],
213 stream: u64,
214 row_base: usize,
215 ) -> Result<Vec<DevicePtr>> {
216 self.prefill_batch_chunk_dispatch(streams, stream, row_base)
217 }
218 fn vocab_size(&self) -> usize {
219 self.vocab_size_dispatch()
220 }
221 fn set_active_lora(&mut self, name: &str) -> Result<()> {
222 self.rotate_lora_to(name)
223 }
224 fn adapter_id_for(&self, slot: i32) -> u64 {
225 self.adapter_id_for_slot(slot)
226 }
227 fn acquire_adapter_slot(&self, slot: i32) -> i32 {
228 TransformerModel::acquire_adapter_slot(self, slot)
229 }
230 fn release_adapter_slot(&self, resolved: i32) {
231 TransformerModel::release_adapter_slot(self, resolved)
232 }
233 fn swap_lora_from_disk(
234 &mut self,
235 dir: &std::path::Path,
236 name: &str,
237 slot: usize,
238 ) -> Result<()> {
239 #[cfg(feature = "cuda")]
242 {
243 self.swap_lora_slot_from_disk(dir, name, slot)
244 }
245 #[cfg(not(feature = "cuda"))]
246 {
247 let _ = (dir, name, slot);
248 anyhow::bail!("LoRA disk swap requires the cuda feature")
249 }
250 }
251 fn promote_lora_from_peer(
252 &mut self,
253 peer_addr: &str,
254 adapter_id: &str,
255 name: &str,
256 peft: avarok_core::config::PeftAdapterConfig,
257 ) -> Result<(usize, Option<String>)> {
258 #[cfg(all(feature = "cuda", unix))]
259 {
260 self.promote_lora_slot_from_peer(peer_addr, adapter_id, name, peft)
261 }
262 #[cfg(not(all(feature = "cuda", unix)))]
263 {
264 let _ = (peer_addr, adapter_id, name, peft);
265 anyhow::bail!("LoRA peer promotion stages over RDMA (rdma-core); unix-only")
266 }
267 }
268 fn promote_lora_from_disk(
269 &mut self,
270 dir: &std::path::Path,
271 name: &str,
272 ) -> Result<(usize, Option<String>)> {
273 #[cfg(feature = "cuda")]
274 {
275 self.promote_lora_slot_from_disk(dir, name)
276 }
277 #[cfg(not(feature = "cuda"))]
278 {
279 let _ = (dir, name);
280 anyhow::bail!("LoRA disk promotion requires the cuda feature")
281 }
282 }
283 fn high_speed_swap_dims(&self) -> Option<spark_storage::ModelDims> {
284 self.high_speed_swap_dims_dispatch()
285 }
286 fn normalize_ssm_states(&self, seq: &SequenceState, stream: u64) -> Result<()> {
287 self.normalize_ssm_states_dispatch(seq, stream)
288 }
289 fn bind_gpu_to_thread(&self) -> Result<()> {
290 self.bind_gpu_to_thread_dispatch()
291 }
292 fn alloc_sequence(&self) -> Result<SequenceState> {
293 self.alloc_sequence_dispatch(usize::MAX)
294 }
295
296 fn alloc_sequence_for(&self, budget_tokens: usize) -> Result<SequenceState> {
297 self.alloc_sequence_dispatch(budget_tokens)
298 }
299 fn copy_logits_to_host(&self, logits_ptr: DevicePtr, dst: &mut [u8]) -> Result<()> {
300 self.copy_logits_to_host_dispatch(logits_ptr, dst)
301 }
302 fn logits_ptr_is_fp32(&self, logits_ptr: DevicePtr) -> bool {
303 self.logits_ptr_is_fp32_dispatch(logits_ptr)
304 }
305 fn logits_buffer_ptr(&self) -> DevicePtr {
306 self.logits_buffer_ptr_dispatch()
307 }
308 fn argmax_on_device(&self, logits_ptr: DevicePtr, _stream: u64) -> Result<u32> {
309 self.argmax_on_device_dispatch(logits_ptr, _stream)
310 }
311 fn argmax_batch(&self, logits_ptr: DevicePtr, n: usize, _stream: u64) -> Result<Vec<u32>> {
312 self.argmax_batch_dispatch(logits_ptr, n, _stream)
313 }
314 fn hidden_after_norm(&self) -> DevicePtr {
315 self.hidden_after_norm_dispatch()
316 }
317 fn decode_verify(
318 &self,
319 tokens: &[u32],
320 seq: &mut SequenceState,
321 stream: u64,
322 ) -> Result<Vec<u32>> {
323 self.ssm_pool.require_verify_rollback_supported()?;
324 let r = self.decode_verify_dispatch(tokens, seq, stream);
325 if r.is_err() {
326 self.gpu.abort_capture_if_active(self.gpu.default_stream());
330 }
331 r
332 }
333 fn checkpoint_ssm_states(&self, seq: &mut SequenceState) -> Result<()> {
334 self.checkpoint_ssm_states_dispatch(seq)
335 }
336 fn rollback_ssm_states(&self, seq: &mut SequenceState, num_accepted: usize) -> Result<()> {
337 self.rollback_ssm_states_dispatch(seq, num_accepted)
338 }
339 fn has_ssm_layers(&self) -> bool {
340 self.ssm_pool.num_ssm_layers > 0
341 }
342 fn decode_rollback_unsupported(&self) -> bool {
343 self.layers.iter().any(|l| l.decode_rollback_unsupported())
344 }
345 fn mtp_slot_draft_capacity(&self, slot_idx: usize) -> usize {
346 self.ssm_pool.verify_draft_capacity(slot_idx)
347 }
348 fn decode_rollback_ring_slots(&self) -> usize {
349 if self.ssm_snapshots.decode_rollback_enabled() {
350 self.ssm_snapshots.decode_ring_slots
351 } else {
352 0
353 }
354 }
355 fn save_decode_ssm_snapshot(&self, seq: &SequenceState, ring_slot: usize) -> Result<()> {
356 self.save_decode_ssm_snapshot_dispatch(seq, ring_slot)
357 }
358 fn restore_decode_ssm_snapshot(&self, seq: &SequenceState, ring_slot: usize) -> Result<()> {
359 self.restore_decode_ssm_snapshot_dispatch(seq, ring_slot)
360 }
361 fn generate_speculative(
362 &self,
363 prompt_tokens: &[u32],
364 params: &spark_runtime::sampler::SamplingParams,
365 num_drafts: usize,
366 ) -> Result<crate::engine::GenerateResult> {
367 self.generate_speculative_dispatch(prompt_tokens, params, num_drafts)
368 }
369 fn has_proposer(&self) -> bool {
370 self.has_proposer_dispatch()
371 }
372 fn dflash_gamma(&self) -> Option<usize> {
373 self.proposer.as_ref().and_then(|p| p.block_gamma())
374 }
375 fn has_self_speculative(&self) -> bool {
376 self.has_self_speculative_dispatch()
377 }
378 fn decode_draft(&self, token: u32, seq: &mut SequenceState, stream: u64) -> Result<DevicePtr> {
379 self.decode_draft_dispatch(token, seq, stream)
380 }
381 fn cache_sequence(&self, seq: &SequenceState) {
382 self.cache_sequence_dispatch(seq)
383 }
384 fn decode_marconi_checkpoint(&self, seq: &mut SequenceState) {
385 self.decode_marconi_checkpoint_dispatch(seq)
386 }
387 fn free_sequence(&self, seq: &mut SequenceState) -> Result<()> {
388 self.free_sequence_dispatch(seq)
389 }
390 fn decode_verify_graphed(
391 &self,
392 tokens: &[u32; 2],
393 seq: &mut SequenceState,
394 _stream: u64,
395 ) -> Result<[u32; 2]> {
396 self.ssm_pool.require_verify_rollback_supported()?;
397 self.decode_verify_graphed_dispatch(tokens, seq, _stream)
398 }
399 fn decode_verify_graphed_k3(
400 &self,
401 tokens: &[u32; 3],
402 seq: &mut SequenceState,
403 _stream: u64,
404 ) -> Result<[u32; 3]> {
405 self.ssm_pool.require_verify_rollback_supported()?;
406 self.decode_verify_graphed_k3_dispatch(tokens, seq, _stream)
407 }
408 fn decode_verify_graphed_k4(
409 &self,
410 tokens: &[u32; 4],
411 seq: &mut SequenceState,
412 _stream: u64,
413 ) -> Result<[u32; 4]> {
414 self.ssm_pool.require_verify_rollback_supported()?;
415 self.decode_verify_graphed_k4_dispatch(tokens, seq, _stream)
416 }
417 fn can_batch_verify(&self, ks: &[usize]) -> bool {
418 self.can_batch_verify_dispatch(ks)
419 }
420 fn decode_verify_batched(
421 &self,
422 tokens: &[u32],
423 ks: &[usize],
424 seqs: &mut [&mut SequenceState],
425 _stream: u64,
426 opts: crate::traits::VerifyBatchedOpts,
427 ) -> Result<Vec<u32>> {
428 self.ssm_pool.require_verify_rollback_supported()?;
429 self.decode_verify_batched_dispatch(tokens, ks, seqs, _stream, opts)
430 }
431 fn stash_verify_hidden_rows(&self, rows: &[usize], _stream: u64) -> Result<()> {
432 self.stash_verify_hidden_rows_dispatch(rows, _stream)
433 }
434 fn save_hidden_for_mtp_from_stash(&self, idx: usize, _stream: u64) -> Result<()> {
435 self.save_hidden_for_mtp_from_stash_dispatch(idx, _stream)
436 }
437 fn run_mtp_propose_batched(
438 &self,
439 tokens: &[u32],
440 positions: &[usize],
441 stash_idx: &[usize],
442 num_drafts: usize,
443 seqs: &mut [&mut SequenceState],
444 _stream: u64,
445 out_conf: Option<&mut Vec<Vec<f32>>>,
446 ) -> Result<Option<Vec<Vec<u32>>>> {
447 self.run_mtp_propose_batched_dispatch(
448 tokens, positions, stash_idx, num_drafts, seqs, out_conf,
449 )
450 }
451 fn mtp_propose_batch_max(&self) -> usize {
452 match &self.proposer {
453 Some(p) => p.propose_batch_max(&self.buffers, &self.config),
454 None => 1,
455 }
456 }
457 fn decode_verify_graphed_kgamma(
458 &self,
459 tokens: &[u32],
460 seq: &mut SequenceState,
461 _stream: u64,
462 ) -> Result<Vec<u32>> {
463 self.ssm_pool.require_verify_rollback_supported()?;
464 self.decode_verify_graphed_kgamma_dispatch(tokens, seq, _stream)
465 }
466 fn decode_and_verify_fused(
467 &self,
468 tokens: &[u32],
469 seq: &mut SequenceState,
470 _stream: u64,
471 ) -> Result<Vec<u32>> {
472 self.ssm_pool.require_verify_rollback_supported()?;
473 self.decode_and_verify_fused_dispatch(tokens, seq, _stream)
474 }
475 fn save_hidden_for_catchup(&self, token_idx: usize, pos: usize) -> Result<()> {
476 self.save_hidden_for_catchup_dispatch(token_idx, pos)
477 }
478
479 fn save_hidden_for_mtp(&self, token_idx: usize, _stream: u64) -> Result<()> {
480 self.save_hidden_for_mtp_dispatch(token_idx, _stream)
481 }
482 fn save_dflash_hidden_for_propose(&self, token_idx: usize, _stream: u64) -> Result<()> {
483 self.save_dflash_hidden_dispatch(token_idx, _stream)
484 }
485
486 fn dflash_accept_append(&self, seq: &mut SequenceState) -> Result<()> {
487 let base = match self.dflash_hidden_save {
488 Some(p) => p,
489 None => return Ok(()),
490 };
491 let prop = match seq.proposer_state.as_mut() {
492 Some(p) => p.as_mut(),
493 None => return Ok(()),
494 };
495 let d = prop
496 .as_any_mut()
497 .downcast_mut::<crate::layers::DflashProposerState>()
498 .ok_or_else(|| anyhow::anyhow!("not DFlash proposer state"))?;
499 let n_layers = self.dflash_capture_layers.len();
500 if n_layers == 0 {
501 return Ok(());
502 }
503 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
504 let save_1 = base.offset(ctx_slot_bytes);
505 let dst = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
506 self.gpu
507 .copy_d2d_async(save_1, dst, ctx_slot_bytes, self.gpu.default_stream())?;
508 d.ctx_positions.push((seq.seq_len as i32).saturating_sub(1));
509 d.ctx_len += 1;
510 Ok(())
511 }
512
513 fn dflash_eagle_accept_append(&self, seq: &mut SequenceState) -> Result<()> {
514 let base = match self.dflash_hidden_save {
515 Some(p) => p,
516 None => return Ok(()),
517 };
518 let prop = match seq.proposer_state.as_mut() {
519 Some(p) => p.as_mut(),
520 None => return Ok(()),
521 };
522 let d = prop
523 .as_any_mut()
524 .downcast_mut::<crate::layers::DflashProposerState>()
525 .ok_or_else(|| anyhow::anyhow!("not DFlash proposer state"))?;
526 let n_layers = self.dflash_capture_layers.len();
527 if n_layers == 0 {
528 return Ok(());
529 }
530 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
531 let stream = self.gpu.default_stream();
532 let pos_row0 = (seq.seq_len as i32).saturating_sub(2);
533 let pos_row1 = (seq.seq_len as i32).saturating_sub(1);
534 let save_0 = base;
536 let dst_0 = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
537 self.gpu
538 .copy_d2d_async(save_0, dst_0, ctx_slot_bytes, stream)?;
539 d.ctx_positions.push(pos_row0);
540 d.ctx_len += 1;
541 let save_1 = base.offset(ctx_slot_bytes);
543 let dst_1 = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
544 self.gpu
545 .copy_d2d_async(save_1, dst_1, ctx_slot_bytes, stream)?;
546 d.ctx_positions.push(pos_row1);
547 d.ctx_len += 1;
548 d.skip_next_decode_append = true;
549 Ok(())
550 }
551
552 fn dflash_eagle_kgamma_append(
553 &self,
554 seq: &mut SequenceState,
555 num_accepted: usize,
556 base_pos: usize,
557 ) -> Result<()> {
558 let base = match self.dflash_hidden_save {
559 Some(p) => p,
560 None => return Ok(()),
561 };
562 let prop = match seq.proposer_state.as_mut() {
563 Some(p) => p.as_mut(),
564 None => return Ok(()),
565 };
566 let d = prop
567 .as_any_mut()
568 .downcast_mut::<crate::layers::DflashProposerState>()
569 .ok_or_else(|| anyhow::anyhow!("not DFlash proposer state"))?;
570 let n_layers = self.dflash_capture_layers.len();
571 if n_layers == 0 {
572 return Ok(());
573 }
574 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
575 let stream = self.gpu.default_stream();
576 for t in 0..=num_accepted {
577 let row = base.offset(t * ctx_slot_bytes);
578 let dst = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
579 self.gpu.copy_d2d_async(row, dst, ctx_slot_bytes, stream)?;
580 let pos = (base_pos + t) as i32;
581 d.ctx_positions.push(pos);
582 d.ctx_len += 1;
583 }
584 d.skip_next_decode_append = true;
585 Ok(())
586 }
587
588 fn dflash_capture_band(&self) -> usize {
589 self.dflash_kgamma
590 }
591
592 fn commit_ctx(
593 &self,
594 seq: &mut SequenceState,
595 num_committed: usize,
596 base_pos: usize,
597 scratch_row: usize,
598 ) -> Result<()> {
599 if num_committed == 0 {
600 return Ok(());
601 }
602 let base = match self.dflash_hidden_save {
603 Some(p) => p,
604 None => return Ok(()),
605 };
606 let prop = match seq.proposer_state.as_mut() {
607 Some(p) => p.as_mut(),
608 None => return Ok(()),
609 };
610 let d = match prop
612 .as_any_mut()
613 .downcast_mut::<crate::layers::DflashProposerState>()
614 {
615 Some(d) => d,
616 None => return Ok(()),
617 };
618 let n_layers = self.dflash_capture_layers.len();
619 if n_layers == 0 {
620 return Ok(());
621 }
622 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
623 let stream = self.gpu.default_stream();
624
625 if scratch_row + num_committed > self.dflash_hidden_save_rows {
631 tracing::warn!(
632 "commit_ctx: scratch rows {}..{} exceed capture capacity {} — skipping (ctx hole)",
633 scratch_row,
634 scratch_row + num_committed,
635 self.dflash_hidden_save_rows,
636 );
637 return Ok(());
638 }
639
640 if d.ctx_len + num_committed > d.max_ctx_len {
648 let keep = (d.max_ctx_len / 2).min(d.max_ctx_len.saturating_sub(num_committed));
649 let drop_n = d.ctx_len.saturating_sub(keep);
650 if drop_n > 0 {
651 let src = d.ctx_hidden_acc.offset(drop_n * ctx_slot_bytes);
652 let dst0 = d.ctx_hidden_acc.offset(0);
653 self.gpu
654 .copy_d2d_async(src, dst0, keep * ctx_slot_bytes, stream)?;
655 d.ctx_positions.drain(..drop_n);
656 d.ctx_len = keep;
657 d.ctx_committed = 0;
658 tracing::info!(
659 "DFlash UNIFIED_CTX watermark: slid ctx window (dropped {} oldest, keep {})",
660 drop_n,
661 keep,
662 );
663 }
664 }
665
666 debug_assert_eq!(d.ctx_positions.len(), d.ctx_len);
671 for t in 0..num_committed {
672 let row = base.offset((scratch_row + t) * ctx_slot_bytes);
673 let dst = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
674 self.gpu.copy_d2d_async(row, dst, ctx_slot_bytes, stream)?;
675 d.ctx_positions.push((base_pos + t) as i32);
676 d.ctx_len += 1;
677 }
678 d.skip_next_decode_append = true;
682
683 tracing::debug!(
686 "CTX_COMMIT slot={} rows={} base_pos={} ctx_len_after={}",
687 seq.slot_idx,
688 num_committed,
689 base_pos,
690 d.ctx_len,
691 );
692 if self.stats.once("log:dflash_unified_ctx") {
694 tracing::info!(
695 "DFlash UNIFIED_CTX ACTIVE: first commit_ctx rows={} base_pos={} ctx_len={}",
696 num_committed,
697 base_pos,
698 d.ctx_len,
699 );
700 }
701 Ok(())
702 }
703
704 fn dflash_serial_ctx_append(&self, seq: &mut SequenceState) -> Result<()> {
705 let base = match self.dflash_hidden_save {
710 Some(p) => p,
711 None => return Ok(()),
712 };
713 let prop = match seq.proposer_state.as_mut() {
714 Some(p) => p.as_mut(),
715 None => return Ok(()),
716 };
717 let d = match prop
720 .as_any_mut()
721 .downcast_mut::<crate::layers::DflashProposerState>()
722 {
723 Some(d) => d,
724 None => return Ok(()),
725 };
726 let n_layers = self.dflash_capture_layers.len();
727 if n_layers == 0 {
728 return Ok(());
729 }
730 let ctx_slot_bytes = n_layers * self.config.hidden_size * 2;
731 let stream = self.gpu.default_stream();
732 if d.ctx_len >= d.max_ctx_len {
743 let keep = d.max_ctx_len / 2;
744 let drop_n = d.ctx_len - keep;
745 let src = d.ctx_hidden_acc.offset(drop_n * ctx_slot_bytes);
746 let dst0 = d.ctx_hidden_acc.offset(0);
747 self.gpu
748 .copy_d2d_async(src, dst0, keep * ctx_slot_bytes, stream)?;
749 d.ctx_positions.drain(..drop_n);
750 d.ctx_len = keep;
751 d.ctx_committed = 0;
752 tracing::info!(
753 "DFlash SERIAL_APPEND watermark: slid ctx window (dropped {} oldest, keep {})",
754 drop_n,
755 keep,
756 );
757 }
758 let dst = d.ctx_hidden_acc.offset(d.ctx_len * ctx_slot_bytes);
759 self.gpu.copy_d2d_async(base, dst, ctx_slot_bytes, stream)?;
760 if self.stats.once("log:dflash_serial_append") {
762 tracing::info!(
763 "DFlash SERIAL_APPEND ACTIVE: first serial ctx append at ctx_len={} pos={}",
764 d.ctx_len,
765 seq.seq_len.saturating_sub(1),
766 );
767 }
768 debug_assert_eq!(d.ctx_positions.len(), d.ctx_len);
772 d.ctx_positions.push(seq.seq_len.saturating_sub(1) as i32);
773 d.ctx_len += 1;
774 d.skip_next_decode_append = true;
777 Ok(())
778 }
779 fn run_mtp_propose(
780 &self,
781 token: u32,
782 position: usize,
783 seq: &mut SequenceState,
784 _stream: u64,
785 ) -> Result<Option<u32>> {
786 self.run_mtp_propose_dispatch(token, position, seq, _stream)
787 }
788 fn run_mtp_propose_multi(
789 &self,
790 token: u32,
791 position: usize,
792 num_drafts: usize,
793 seq: &mut SequenceState,
794 _stream: u64,
795 grammar_bitmask: Option<&[i32]>,
796 ) -> Result<Vec<u32>> {
797 self.run_mtp_propose_multi_dispatch(
798 token,
799 position,
800 num_drafts,
801 seq,
802 _stream,
803 grammar_bitmask,
804 )
805 }
806 fn read_deferred_draft_token(&self) -> Result<u32> {
807 self.read_deferred_draft_token_dispatch()
808 }
809 fn trim_proposer_state(
810 &self,
811 seq: &mut SequenceState,
812 num_accepted: usize,
813 _stream: u64,
814 ) -> Result<()> {
815 self.trim_proposer_state_dispatch(seq, num_accepted, _stream)
816 }
817 fn compact_sequence(&self, seq: &mut SequenceState, new_slot: usize) -> Result<()> {
818 self.compact_sequence_dispatch(seq, new_slot)
819 }
820 fn detach_slot_for_reuse(&self, seq: &mut SequenceState) {
821 self.detach_slot_for_reuse_dispatch(seq)
822 }
823 fn save_sequence_state(
824 &self,
825 seq: &SequenceState,
826 writer: &mut dyn std::io::Write,
827 ) -> Result<()> {
828 self.save_sequence_state_dispatch(seq, writer)
829 }
830 fn restore_sequence_state(
831 &self,
832 seq: &mut SequenceState,
833 num_blocks: usize,
834 reader: &mut dyn std::io::Read,
835 ) -> Result<()> {
836 self.restore_sequence_state_dispatch(seq, num_blocks, reader)
837 }
838 fn num_free_blocks(&self) -> usize {
839 self.num_free_blocks_dispatch()
840 }
841 fn num_total_blocks(&self) -> usize {
842 self.num_total_blocks_dispatch()
843 }
844 fn ssm_snapshot_occupancy(&self) -> Option<(u32, u32)> {
845 let (used, total) = self.ssm_snapshots.occupancy();
846 (total > 0).then_some((used as u32, total as u32))
847 }
848 fn reclaim_prefix_blocks(&self, num_blocks: usize) -> usize {
849 self.reclaim_prefix_blocks_dispatch(num_blocks)
850 }
851 fn start_checkpoint_async(&self, seq: &mut SequenceState) -> Result<()> {
852 self.start_checkpoint_async_dispatch(seq)
853 }
854 fn start_rollback_and_checkpoint_async(
855 &self,
856 seq: &mut SequenceState,
857 num_accepted: usize,
858 ) -> Result<()> {
859 self.start_rollback_and_checkpoint_async_dispatch(seq, num_accepted)
860 }
861 fn sync_secondary(&self) -> Result<()> {
862 self.sync_secondary_dispatch()
863 }
864 fn gdn_fold_accepted(
865 &self,
866 slots: &[usize],
867 accepted_rows: &[u32],
868 k_rows: usize,
869 ) -> Result<bool> {
870 self.gdn_fold_accepted_dispatch(slots, accepted_rows, k_rows)
871 }
872
873 fn commit_accepted_prefix(
874 &self,
875 seq: &mut SequenceState,
876 num_accepted: usize,
877 k: usize,
878 ) -> Result<()> {
879 self.commit_accepted_prefix_dispatch(seq, num_accepted, k)
880 }
881 fn ep_worker_step(&self, slots: &mut [Option<SequenceState>]) -> Result<bool> {
882 self.ep_worker_step_dispatch(slots)
883 }
884 fn is_ep(&self) -> bool {
885 self.is_ep_dispatch()
886 }
887 fn hc_mult(&self) -> usize {
888 self.config.hc_mult
889 }
890
891 fn is_mla(&self) -> bool {
892 self.is_mla_dispatch()
893 }
894
895 fn kv_block_size(&self) -> Option<usize> {
896 Some(self.kv_cache.lock().block_size())
897 }
898 fn decode_logits_fp32(&self) -> bool {
899 self.decode_logits_fp32_dispatch()
900 }
901 fn decode_logits_ptr(&self) -> DevicePtr {
902 self.decode_logits_ptr_dispatch()
903 }
904 fn ep_broadcast_cmd(&self, cmd: u32) -> Result<()> {
905 self.ep_broadcast_cmd_dispatch(cmd)
906 }
907 fn ep_broadcast_cmd_for_seq(&self, seq_id: u32, cmd: u32) -> Result<()> {
908 self.ep_broadcast_seq_and_cmd(seq_id, cmd, self.ep_protocol_v2)
911 }
912 fn ep_protocol_v2(&self) -> bool {
913 self.ep_protocol_v2
914 }
915 fn ep_broadcast_tokens(&self, tokens: &[u32]) -> Result<Vec<u32>> {
916 self.ep_broadcast_tokens_dispatch(tokens)
917 }
918 fn default_stream(&self) -> u64 {
919 self.default_stream_dispatch()
920 }
921 fn create_stream(&self) -> Result<u64> {
922 self.create_stream_dispatch()
923 }
924 fn create_event(&self) -> Result<u64> {
925 self.create_event_dispatch()
926 }
927 fn record_event(&self, event: u64, stream: u64) -> Result<()> {
928 self.record_event_dispatch(event, stream)
929 }
930 fn stream_wait_event(&self, stream: u64, event: u64) -> Result<()> {
931 self.stream_wait_event_dispatch(stream, event)
932 }
933 fn synchronize(&self, stream: u64) -> Result<()> {
934 self.synchronize_dispatch(stream)
935 }
936}
937
938impl TransformerModel {
939 pub(in crate::model) fn collect_aux_states(
943 &self,
944 seq: &SequenceState,
945 stream: u64,
946 ) -> Result<Vec<(u32, Vec<u8>)>> {
947 let mut out = Vec::new();
948 for (i, l) in self.layers.iter().enumerate() {
949 if let Some(blob) =
950 l.snapshot_aux(seq.layer_states[i].as_ref(), self.gpu.as_ref(), stream)?
951 {
952 out.push((i as u32, blob));
953 }
954 }
955 Ok(out)
956 }
957
958 pub(in crate::model) fn requires_aux_state(&self) -> bool {
961 self.layers.iter().any(|l| l.has_aux_state())
962 }
963
964 pub(in crate::model) fn apply_aux_states(
966 &self,
967 seq: &mut SequenceState,
968 blobs: &[(u32, Vec<u8>)],
969 stream: u64,
970 ) -> Result<()> {
971 for (i, blob) in blobs {
972 self.layers[*i as usize].restore_aux(
973 seq.layer_states[*i as usize].as_mut(),
974 blob,
975 self.gpu.as_ref(),
976 stream,
977 )?;
978 }
979 Ok(())
980 }
981}
982
983#[cfg(test)]
984mod gdn_woa_folded_slots_tests {
985 fn record_old(list: &mut Vec<usize>, any: bool, slots: &[usize]) {
993 if any {
994 list.clear();
995 list.extend_from_slice(slots);
996 }
997 }
998 fn record_new(list: &mut Vec<usize>, any: bool, slots: &[usize]) {
999 list.clear();
1000 if any {
1001 list.extend_from_slice(slots);
1002 }
1003 }
1004 fn consume(list: &mut Vec<usize>, slot: usize) -> bool {
1006 match list.iter().position(|&s| s == slot) {
1007 Some(p) => {
1008 list.swap_remove(p);
1009 true
1010 }
1011 None => false,
1012 }
1013 }
1014
1015 #[test]
1019 fn a_no_op_fold_does_not_leave_a_stale_claim() {
1020 let mut new = vec![];
1021 record_new(&mut new, true, &[1, 2, 3, 4]); for s in [1, 2, 3, 4] {
1023 assert!(consume(&mut new, s), "batch A's own slots are folded");
1024 }
1025 record_new(&mut new, false, &[7]); assert!(
1027 !consume(&mut new, 7),
1028 "slot 7 was NOT folded and must restore h"
1029 );
1030
1031 let mut old = vec![];
1035 record_old(&mut old, true, &[1, 2, 3, 4]);
1036 record_old(&mut old, false, &[7]); assert!(
1038 consume(&mut old, 3),
1039 "the shipped behaviour leaves slot 3 claimed — this assertion \
1040 documents the bug, and its inverse is the fix above"
1041 );
1042 }
1043}