Submit `IORING_OP_READ` SQEs for each index in `indices`. Indices that are already completed (from a prior prefetch), already in-flight, or cannot get a pool buffer are silently skipped — `fetch_fp32` will handle them via a sync fallback.
(&mut self, indices: &[u32])
| 195 | /// in-flight, or cannot get a pool buffer are silently skipped — |
| 196 | /// `fetch_fp32` will handle them via a sync fallback. |
| 197 | fn prefetch_batch(&mut self, indices: &[u32]) { |
| 198 | // Drain any already-completed CQEs to free pool slots. |
| 199 | self.drain_completions(); |
| 200 | |
| 201 | let dim = self.layout.dim as usize; |
| 202 | let needed = dim * 4; |
| 203 | let buf_size = align_up(needed, ALIGNMENT); |
| 204 | let fd = io_uring::types::Fd(self.file.as_raw_fd()); |
| 205 | |
| 206 | for &node_idx in indices { |
| 207 | // Skip: already completed, already in-flight. |
| 208 | if self.completed.contains_key(&node_idx) || self.pending.contains_key(&node_idx) { |
| 209 | continue; |
| 210 | } |
| 211 | // Skip: no free buffer. |
| 212 | let Some(slot) = self.free_bufs.pop() else { |
| 213 | continue; |
| 214 | }; |
| 215 | // Grow pool buffer to required size if needed (shouldn't happen |
| 216 | // since pool is sized at open, but guard against it). |
| 217 | if self.buf_pool[slot].capacity() < buf_size { |
| 218 | self.free_bufs.push(slot); |
| 219 | continue; |
| 220 | } |
| 221 | |
| 222 | let off = vector_offset(&self.layout, node_idx as u64); |
| 223 | let op = |
| 224 | io_uring::opcode::Read::new(fd, self.buf_pool[slot].as_mut_ptr(), buf_size as u32) |
| 225 | .offset(off) |
| 226 | .build() |
| 227 | .user_data(node_idx as u64); |
| 228 | |
| 229 | // SAFETY: buf_pool[slot] outlives the SQE and completion. |
| 230 | // `self.pending` keeps the slot reserved until drain. |
| 231 | let pushed = unsafe { self.ring.submission().push(&op).is_ok() }; |
| 232 | if pushed { |
| 233 | self.pending.insert(node_idx, slot); |
| 234 | self.in_flight += 1; |
| 235 | } else { |
| 236 | // SQ full — return the slot. |
| 237 | self.free_bufs.push(slot); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | // Flush the submission queue to the kernel without waiting. |
| 242 | let _ = self.ring.submit(); |
| 243 | } |
| 244 | |
| 245 | fn fetch_fp32(&mut self, idx: u32) -> Option<Vec<f32>> { |
| 246 | // Drain any completions that arrived since last prefetch/fetch. |