spark_storage/high_speed_swap/
impl_more.rs1use anyhow::Result;
6use std::ffi::c_void;
7
8use super::HighSpeedSwap;
9use crate::backend::{ReadRequest, StorageBackend};
10use crate::config::HighSpeedSwapConfig;
11use crate::cuda_min::{CudaCtx, copy_d_to_h_async, copy_h_to_d_async, stream_sync};
12use crate::group::{GroupKey, KvKind};
13use crate::predictor::Predictor;
14use crate::scratch_pool::{ResidentKey, ScratchPool};
15
16impl HighSpeedSwap {
17 pub fn offload_block(
22 &mut self,
23 ctx: &CudaCtx,
24 layer: u32,
25 block: u32,
26 k_block_dev: u64,
27 k_block_host: &[half::bf16],
28 v_block_host: &[half::bf16],
29 ) -> Result<()> {
30 self.offload_block_on_stream(
31 ctx.stream,
32 layer,
33 block,
34 k_block_dev,
35 k_block_host,
36 v_block_host,
37 )
38 }
39
40 pub fn offload_block_on_stream(
43 &mut self,
44 stream: u64,
45 layer: u32,
46 block: u32,
47 k_block_dev: u64,
48 k_block_host: &[half::bf16],
49 v_block_host: &[half::bf16],
50 ) -> Result<()> {
51 self.offload_block_inner_on_stream(
55 stream,
56 layer,
57 block,
58 k_block_dev,
59 k_block_host,
60 v_block_host,
61 true,
62 )
63 }
64
65 pub fn offload_block_no_predict_on_stream(
71 &mut self,
72 stream: u64,
73 layer: u32,
74 block: u32,
75 k_block_host: &[half::bf16],
76 v_block_host: &[half::bf16],
77 ) -> Result<()> {
78 self.offload_block_inner_on_stream(
79 stream,
80 layer,
81 block,
82 0,
83 k_block_host,
84 v_block_host,
85 false,
86 )
87 }
88
89 #[allow(clippy::too_many_arguments)]
90 fn offload_block_inner_on_stream(
91 &mut self,
92 stream: u64,
93 layer: u32,
94 block: u32,
95 k_block_dev: u64,
96 k_block_host: &[half::bf16],
97 v_block_host: &[half::bf16],
98 do_predict: bool,
99 ) -> Result<()> {
100 if do_predict {
101 self.predictor.project_kv_block_on_stream(
102 stream,
103 layer as usize,
104 block as usize,
105 k_block_dev,
106 )?;
107 }
108 let bs = self.model.block_size as usize;
109 let nkv = self.model.num_kv_heads as usize;
110 let hd = self.model.head_dim as usize;
111 if k_block_host.len() != bs * nkv * hd || v_block_host.len() != bs * nkv * hd {
112 anyhow::bail!(
113 "offload_block: host buffers must be {} BF16 elements",
114 bs * nkv * hd
115 );
116 }
117 for kh in 0..nkv {
118 let mut k_stripe = Vec::with_capacity(bs * hd * 2);
119 let mut v_stripe = Vec::with_capacity(bs * hd * 2);
120 for tok in 0..bs {
121 let base = (tok * nkv + kh) * hd;
122 for x in &k_block_host[base..base + hd] {
123 k_stripe.extend_from_slice(&x.to_le_bytes());
124 }
125 for x in &v_block_host[base..base + hd] {
126 v_stripe.extend_from_slice(&x.to_le_bytes());
127 }
128 }
129 self.backend
130 .write_from_host(GroupKey::new(layer, block, kh as u16, KvKind::K), &k_stripe)?;
131 self.backend
132 .write_from_host(GroupKey::new(layer, block, kh as u16, KvKind::V), &v_stripe)?;
133 }
134 self.pool.invalidate(ResidentKey { layer, block });
139 Ok(())
140 }
141
142 pub fn attend_layer(
147 &mut self,
148 ctx: &CudaCtx,
149 layer: u32,
150 seq_block_ids: &[u32],
151 q_dev: u64,
152 output_dev: u64,
153 ) -> Result<()> {
154 self.attend_layer_on_stream(ctx.stream, layer, seq_block_ids, q_dev, output_dev)
155 }
156
157 pub fn attend_layer_on_stream(
167 &mut self,
168 stream: u64,
169 layer: u32,
170 seq_block_ids: &[u32],
171 q_dev: u64,
172 output_dev: u64,
173 ) -> Result<()> {
174 let bs = self.model.block_size as i32;
175 self.attend_layer_on_stream_with_q_pos(stream, layer, seq_block_ids, q_dev, output_dev, bs)
176 }
177
178 pub fn attend_layer_on_stream_with_q_pos(
184 &mut self,
185 stream: u64,
186 layer: u32,
187 seq_block_ids: &[u32],
188 q_dev: u64,
189 output_dev: u64,
190 last_block_valid_slots: i32,
191 ) -> Result<()> {
192 self.predictor
195 .project_q_on_stream(stream, q_dev, self.q_proj.ptr)?;
196 let m = &self.model;
197 let layer_a_g = self.predictor.a_g_dev_ptr()
198 + (layer as u64)
199 * (m.max_blocks_per_layer as u64)
200 * (m.num_kv_heads as u64)
201 * (m.block_size as u64)
202 * (self.cfg.rank as u64)
203 * 2;
204 self.predictor.score_blocks_on_stream(
205 stream,
206 self.q_proj.ptr,
207 layer_a_g,
208 self.block_scores_dev.ptr,
209 m.max_blocks_per_layer as usize,
210 )?;
211 copy_d_to_h_async(
212 self.score_host_buf.as_mut_ptr() as *mut c_void,
213 self.block_scores_dev.ptr,
214 self.score_host_buf.len() * 4,
215 stream,
216 )?;
217 stream_sync(stream)?;
218
219 self.attn.begin_step_on_stream(stream, 1)?;
221 let tile_cap = self.cfg.resident_blocks as usize;
222 let mut tile_idx = 0;
223 while tile_idx < seq_block_ids.len() {
224 let tile_end = (tile_idx + tile_cap).min(seq_block_ids.len());
225 let tile = &seq_block_ids[tile_idx..tile_end];
226
227 let mut block_table = vec![0_i32; tile_cap];
229 let mut pinned: Vec<u32> = Vec::new();
230 let mut missing: Vec<u32> = Vec::new();
232 for (i, &blk) in tile.iter().enumerate() {
233 let key = ResidentKey { layer, block: blk };
234 if let Some(slot) = self.pool.lookup(key) {
235 block_table[i] = slot as i32;
236 pinned.push(slot);
237 self.eviction.touch(slot);
238 } else {
239 missing.push(blk);
240 }
241 }
242 let mut reqs: Vec<ReadRequest> = Vec::new();
244 for &blk in &missing {
245 let key = ResidentKey { layer, block: blk };
246 let candidates = self.eviction.rank(&pinned);
247 let slot = self.pool.assign(key, &candidates)?;
248 pinned.push(slot);
249 self.eviction.touch(slot);
250 self.eviction
251 .record_score(slot, self.score_host_buf[blk as usize]);
252 let idx = tile.iter().position(|&x| x == blk).unwrap();
254 block_table[idx] = slot as i32;
255 for kh in 0..self.model.num_kv_heads {
256 reqs.push(ReadRequest {
257 group: GroupKey::new(layer, blk, kh, KvKind::K),
258 dst_dev_ptr: self.pool.slot_k_ptr(slot, kh),
259 });
260 reqs.push(ReadRequest {
261 group: GroupKey::new(layer, blk, kh, KvKind::V),
262 dst_dev_ptr: self.pool.slot_v_ptr(slot, kh),
263 });
264 }
265 }
266 self.backend.read(&reqs, stream)?;
267
268 let counts = [(tile.len()) as i32];
270 copy_h_to_d_async(
271 self.block_table_dev.ptr,
272 block_table.as_ptr() as *const c_void,
273 tile_cap * 4,
274 stream,
275 )?;
276 copy_h_to_d_async(
277 self.counts_dev.ptr,
278 counts.as_ptr() as *const c_void,
279 4,
280 stream,
281 )?;
282 let (s_blk, s_tok, s_kvh) = self.attn.scratch_pool_strides();
283 let v_off = (self.model.num_kv_heads as u64)
284 * (self.model.block_size as u64)
285 * (self.model.head_dim as u64)
286 * 2;
287 let lbvs = if tile_end == seq_block_ids.len() {
290 last_block_valid_slots
291 } else {
292 self.model.block_size as i32
293 };
294 self.attn.step_tile_on_stream(
295 stream,
296 q_dev,
297 self.pool.pool_dev_ptr(),
298 self.pool.pool_dev_ptr() + v_off,
299 self.block_table_dev.ptr,
300 self.counts_dev.ptr,
301 1,
302 s_blk,
303 s_tok,
304 s_kvh,
305 lbvs,
306 )?;
307 tile_idx = tile_end;
308 }
309 self.attn.finalize_on_stream(stream, output_dev, 1)?;
310 Ok(())
311 }
312
313 pub fn pool(&self) -> &ScratchPool {
315 &self.pool
316 }
317 pub fn predictor(&self) -> &Predictor {
318 &self.predictor
319 }
320 pub fn config(&self) -> &HighSpeedSwapConfig {
321 &self.cfg
322 }
323}