spark_runtime/weights/gguf/
expert_lru_pool.rs1use std::io::Write;
20use std::sync::Arc;
21
22use anyhow::{Context, Result, bail, ensure};
23
24use super::expert_lru::{ExpertLru, ExpertSlot};
25use super::expert_prefetch::{ReaderPool, Ticket};
26use super::expert_stream::ExpertSource;
27
28impl ExpertLru {
29 pub fn set_pool(&mut self, src: Arc<dyn ExpertSource + Send + Sync>, threads: usize) {
32 self.pool = Some((src, ReaderPool::new(threads)));
33 }
34
35 pub fn has_pool(&self) -> bool {
36 self.pool.is_some()
37 }
38
39 fn enqueue_read(&mut self, i: u32, key: (u32, u32), parts: usize, urgent: bool) -> Arc<Ticket> {
41 let (src, pool) = self.pool.as_ref().expect("reader pool");
42 let bytes = self.layout().bytes;
43 let parts = parts.clamp(1, (bytes / (1 << 20)).max(1));
44 let per = bytes.div_ceil(parts);
45 let ticket = Ticket::new(parts);
46 let p = self.slot_ptr(i);
47 for j in 0..parts {
48 let off = j * per;
49 let len = per.min(bytes - off);
50 let (src, ticket, p) = (src.clone(), ticket.clone(), p);
51 pool.submit(
52 Box::new(move || {
53 let base = p.get();
54 let dst = unsafe { std::slice::from_raw_parts_mut(base.add(off), len) };
57 ticket.finish(src.read_expert_range(key.0, key.1, off, dst));
58 }),
59 urgent,
60 );
61 }
62 self.meta[i as usize].ticket = Some(ticket.clone());
63 self.in_flight.push(i);
64 ticket
65 }
66
67 pub fn fetch_many_prefetching(
70 &mut self,
71 keys: &[(u32, u32)],
72 predicted: &[(u32, u32)],
73 ) -> Result<Vec<ExpertSlot>> {
74 ensure!(self.pool.is_some(), "fetch_many_prefetching without a pool");
75 ensure!(
76 self.staging.is_none(),
77 "device expert cache: no reader pool"
78 );
79 let threads = self.pool.as_ref().map(|p| p.1.threads()).unwrap_or(1);
80 let evictions0 = self.stats.evictions;
81 let mut out: Vec<u32> = Vec::with_capacity(keys.len());
82 let mut misses: Vec<(u32, (u32, u32))> = Vec::new();
83 let mut waits: Vec<(u32, Arc<Ticket>)> = Vec::new();
84 for &key in keys {
85 if let Some(&i) = self.map.get(&key) {
86 self.touch(i);
87 self.meta[i as usize].speculative = false;
88 if let Some(t) = self.meta[i as usize].ticket.clone()
89 && !t.is_done()
90 {
91 waits.push((i, t));
92 }
93 self.stats.hits += 1;
94 out.push(i);
95 continue;
96 }
97 let i = self.assign(key)?;
98 misses.push((i, key));
99 out.push(i);
100 }
101 let n_wait = waits.len();
102 let parts = if misses.is_empty() {
104 1
105 } else {
106 (threads / misses.len()).clamp(1, 8)
107 };
108 for &(i, key) in &misses {
109 let t = self.enqueue_read(i, key, parts, true);
110 waits.push((i, t));
111 }
112 let mut n_pred = 0usize;
114 for &key in predicted {
115 if self.map.contains_key(&key) {
116 continue;
117 }
118 let Ok(i) = self.assign(key) else { break };
119 self.meta[i as usize].speculative = true;
120 self.enqueue_read(i, key, 4, false);
121 n_pred += 1;
122 }
123 self.stats.prefetched += n_pred as u64;
124 let bytes = self.layout().bytes;
125 let mut failed: Vec<(u32, String)> = Vec::new();
126 for (i, t) in &waits {
127 if let Err(e) = t.wait() {
128 failed.push((*i, format!("{e:#}")));
129 }
130 }
131 let n_ok = misses.len()
132 - failed
133 .iter()
134 .filter(|(i, _)| misses.iter().any(|m| m.0 == *i))
135 .count();
136 self.stats.misses += n_ok as u64;
137 self.stats.bytes_read += (n_ok * bytes) as u64;
138 self.stats.waited += n_wait as u64;
139 if self.trace.is_some() {
140 let ev = self.stats.evictions - evictions0;
141 self.trace_line(keys, misses.len(), ev, n_wait);
142 }
143 if let Some((_, first)) = failed.first() {
144 let msg = format!(
145 "{} of {} expert reads failed; first: {first}",
146 failed.len(),
147 waits.len()
148 );
149 for (i, _) in &failed {
150 self.unmap(*i);
151 }
152 bail!(msg);
153 }
154 Ok(out.into_iter().map(|i| self.slot(i)).collect())
155 }
156
157 pub fn prefetch(&mut self, predicted: &[(u32, u32)]) -> Result<()> {
159 ensure!(self.pool.is_some(), "prefetch without a pool");
160 let mut n = 0usize;
161 for &key in predicted {
162 if self.map.contains_key(&key) {
163 continue;
164 }
165 let Ok(i) = self.assign(key) else { break };
166 self.meta[i as usize].speculative = true;
167 self.enqueue_read(i, key, 4, false);
168 n += 1;
169 }
170 self.stats.prefetched += n as u64;
171 Ok(())
172 }
173
174 pub fn wait_in_flight(&self) {
176 for &i in &self.in_flight {
177 if let Some(t) = &self.meta[i as usize].ticket {
178 let _ = t.wait();
179 }
180 }
181 }
182
183 pub(super) fn reap(&mut self) {
186 let mut still = Vec::new();
187 for i in std::mem::take(&mut self.in_flight) {
188 let Some(t) = self.meta[i as usize].ticket.clone() else {
189 continue;
190 };
191 if !t.is_done() {
192 still.push(i);
193 continue;
194 }
195 self.meta[i as usize].ticket = None;
196 if t.error().is_some() {
197 self.unmap(i);
198 continue;
199 }
200 if self.meta[i as usize].speculative {
201 self.meta[i as usize].speculative = false;
202 self.stats.prefetch_unused += 1;
203 self.demote(i);
204 }
205 }
206 self.in_flight = still;
207 }
208
209 pub fn set_trace(&mut self, path: &str) -> Result<()> {
216 let f = std::fs::File::create(path).with_context(|| format!("route trace {path}"))?;
217 self.trace = Some(std::io::BufWriter::with_capacity(1 << 16, f));
218 Ok(())
219 }
220
221 pub(super) fn trace_line(
222 &mut self,
223 keys: &[(u32, u32)],
224 misses: usize,
225 evictions: u64,
226 wait: usize,
227 ) {
228 let Some(w) = self.trace.as_mut() else {
229 return;
230 };
231 let us = self.t0.elapsed().as_micros();
232 let layer = keys.first().map(|k| k.0).unwrap_or(u32::MAX);
233 let (pf, pfu) = (self.stats.prefetched, self.stats.prefetch_unused);
234 let _ = write!(
235 w,
236 "{us} L{layer} n={} miss={misses} evict={evictions} wait={wait} pf={pf} pfu={pfu}",
237 keys.len()
238 );
239 for k in keys {
240 let _ = write!(w, " {}", k.1);
241 }
242 let _ = writeln!(w);
243 let _ = w.flush();
245 }
246}