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