diskann_providers/model/graph/provider/async_/
common.rs1use std::{cell::UnsafeCell, mem, num::NonZeroUsize, ops::Deref, slice, sync::Arc};
7
8use crate::storage::{StorageReadProvider, StorageWriteProvider};
9use arc_swap::Guard;
10use diskann::{ANNError, ANNResult, always_escalate, utils::IntoUsize};
11use diskann_utils::future::AsyncFriendly;
12use diskann_vector::distance::Metric;
13
14use crate::{
15 model::graph::provider::async_::{TableDeleteProviderAsync, postprocess},
16 storage::{AsyncIndexMetadata, AsyncQuantLoadContext, LoadWith, SaveWith},
17};
18
19pub use diskann::graph::strategy::{FullPrecision, Quantized};
20
21pub struct StartPoints {
25 start: u32,
26 end: u32,
27}
28
29impl StartPoints {
30 pub fn new(valid_points: u32, frozen_points: NonZeroUsize) -> ANNResult<Self> {
31 Ok(Self {
32 start: valid_points,
33 end: match valid_points.checked_add(frozen_points.get() as u32) {
34 Some(end) => end,
35 None => {
36 return Err(ANNError::log_index_error(
37 "Sum of valid points and frozen points exceeds u32::MAX",
38 ));
39 }
40 },
41 })
42 }
43
44 pub fn range(&self) -> std::ops::Range<u32> {
45 self.start..self.end
46 }
47
48 pub fn len(&self) -> usize {
49 (self.end - self.start).into_usize()
50 }
51
52 pub fn is_empty(&self) -> bool {
53 self.len() == 0
54 }
55
56 pub fn start(&self) -> u32 {
57 self.start
58 }
59
60 pub fn end(&self) -> u32 {
61 self.end
62 }
63}
64
65pub struct VectorGuard<T> {
66 inner: Guard<Arc<Vec<T>>>,
67}
68
69impl<T> VectorGuard<T> {
70 pub(crate) fn from_guard(guard: Guard<Arc<Vec<T>>>) -> Self {
71 Self { inner: guard }
72 }
73}
74
75impl<T> Deref for VectorGuard<T> {
76 type Target = [T];
77
78 fn deref(&self) -> &Self::Target {
79 self.inner.deref()
80 }
81}
82
83pub struct AlignedMemoryVectorStore<T: bytemuck::Pod> {
90 store: UnsafeCell<Vec<T>>,
91 max_vectors: usize,
92 start_index: usize,
93 dim: usize,
94 padded_vector_dim: usize,
95}
96
97unsafe impl<T: bytemuck::Pod + Sync> Sync for AlignedMemoryVectorStore<T> {}
99
100unsafe impl<T: bytemuck::Pod + Send> Send for AlignedMemoryVectorStore<T> {}
102
103impl<T: bytemuck::Pod> AlignedMemoryVectorStore<T> {
104 pub fn with_capacity(max_vectors: usize, dim: usize) -> Self {
105 let elem_size = mem::size_of::<T>();
106 assert!(64 % elem_size == 0);
107 let vector_size = elem_size * dim;
108 let extra_size = vector_size % 64;
109 let padded_vector_dim = if extra_size == 0 {
110 dim
112 } else {
113 let padding_needed_size = 64 - extra_size;
114 assert!(padding_needed_size.is_multiple_of(elem_size));
115 let extra_elems = padding_needed_size / elem_size;
116 dim + extra_elems
117 };
118
119 assert!((padded_vector_dim * elem_size).is_multiple_of(64));
120
121 let last_elems: usize = 64 / elem_size - 1;
124
125 let count = max_vectors * padded_vector_dim + last_elems;
126 let mut store: UnsafeCell<Vec<T>> =
127 UnsafeCell::new(vec![<T as bytemuck::Zeroable>::zeroed(); count]);
128
129 let start_index = store.get_mut().as_ptr().align_offset(64);
130
131 Self {
132 store,
133 max_vectors,
134 start_index,
135 dim,
136 padded_vector_dim,
137 }
138 }
139
140 pub fn max_vectors(&self) -> usize {
141 self.max_vectors
142 }
143
144 pub fn dim(&self) -> usize {
145 self.dim
146 }
147
148 #[inline(always)]
158 pub unsafe fn get_slice(&self, index: usize) -> &[T] {
159 assert!(
160 index < self.max_vectors,
161 "index ({}) exceeded max_vectors ({})",
162 index,
163 self.max_vectors
164 );
165 let index = index * self.padded_vector_dim + self.start_index;
166
167 let buf = unsafe { (*self.store.get()).as_ptr() };
171
172 unsafe { slice::from_raw_parts(buf.add(index), self.dim) }
174 }
175
176 #[allow(clippy::mut_from_ref)]
183 pub unsafe fn get_mut_slice(&self, index: usize) -> &mut [T] {
184 assert!(index < self.max_vectors);
185 let index = index * self.padded_vector_dim + self.start_index;
186
187 unsafe {
191 let buf = (*self.store.get()).as_mut_ptr();
192 slice::from_raw_parts_mut(buf.add(index), self.dim)
193 }
194 }
195}
196
197#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
199pub enum PrefetchCacheLineLevel {
200 CacheLine4,
202 CacheLine8,
204 #[default]
206 CacheLine16,
207 All,
209}
210
211pub trait SetElementHelper<T> {
217 fn set_element(&self, index: &u32, element: &[T]) -> ANNResult<()>;
219}
220
221pub trait CreateVectorStore {
226 type Target: VectorStore;
228
229 fn create(
231 self,
232 max_points: usize,
233 metric: Metric,
234 prefetch_lookahead: Option<usize>,
235 ) -> Self::Target;
236}
237
238pub trait CreateDeleteProvider {
243 type Target;
245
246 fn create(self, total_points: usize) -> Self::Target;
252}
253
254pub trait VectorStore: AsyncFriendly {
255 fn total(&self) -> usize;
257
258 fn count_for_get_vector(&self) -> usize;
260}
261
262#[derive(Debug)]
268pub enum Panics {}
269
270impl std::fmt::Display for Panics {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 write!(f, "panics")
273 }
274}
275
276impl std::error::Error for Panics {}
277impl From<Panics> for ANNError {
278 #[cold]
279 fn from(_: Panics) -> ANNError {
280 ANNError::log_async_error("unreachable")
281 }
282}
283
284always_escalate!(Panics);
285
286#[derive(Debug, Clone, Copy)]
290pub struct NoStore;
291
292impl CreateVectorStore for NoStore {
293 type Target = NoStore;
294 fn create(
295 self,
296 _max_points: usize,
297 _metric: Metric,
298 _prefetch_lookahead: Option<usize>,
299 ) -> Self::Target {
300 self
301 }
302}
303
304impl VectorStore for NoStore {
305 fn total(&self) -> usize {
306 0
307 }
308
309 fn count_for_get_vector(&self) -> usize {
310 0
311 }
312}
313
314impl LoadWith<AsyncQuantLoadContext> for NoStore {
315 type Error = ANNError;
316 async fn load_with<P>(_: &P, _: &AsyncQuantLoadContext) -> ANNResult<Self>
317 where
318 P: StorageReadProvider,
319 {
320 Ok(Self)
321 }
322}
323
324impl SaveWith<AsyncIndexMetadata> for NoStore {
325 type Ok = usize;
326 type Error = ANNError;
327 async fn save_with<P>(&self, _provider: &P, _auxiliary: &AsyncIndexMetadata) -> ANNResult<usize>
328 where
329 P: StorageWriteProvider,
330 {
331 Ok(0)
332 }
333}
334
335impl<T> SetElementHelper<T> for NoStore {
336 fn set_element(&self, _index: &u32, _element: &[T]) -> ANNResult<()> {
337 Ok(())
338 }
339}
340
341#[derive(Debug, Clone, Copy)]
345pub struct NoDeletes;
346
347impl postprocess::DeletionCheck for NoDeletes {
348 #[inline(always)]
353 fn deletion_check(&self, _: u32) -> bool {
354 false
355 }
356}
357
358impl CreateDeleteProvider for NoDeletes {
359 type Target = Self;
360 fn create(self, _: usize) -> Self {
361 Self
362 }
363}
364
365#[derive(Debug, Clone, Copy)]
367pub struct TableBasedDeletes;
368
369impl CreateDeleteProvider for TableBasedDeletes {
370 type Target = TableDeleteProviderAsync;
371 fn create(self, total_points: usize) -> Self::Target {
372 TableDeleteProviderAsync::new(total_points)
373 }
374}
375
376#[derive(Debug, Clone, Copy)]
389pub struct Hybrid {
390 pub max_fp_vecs_per_prune: Option<usize>,
394}
395
396impl Hybrid {
397 pub fn new(max_fp_vecs_per_prune: Option<usize>) -> Self {
402 Self {
403 max_fp_vecs_per_prune,
404 }
405 }
406}
407
408#[cfg(test)]
409pub struct TestCallCount {
410 count: std::sync::atomic::AtomicUsize,
411}
412
413#[cfg(test)]
414impl TestCallCount {
415 pub fn new() -> Self {
416 TestCallCount {
417 count: std::sync::atomic::AtomicUsize::new(0),
418 }
419 }
420
421 pub fn enabled() -> bool {
422 true
423 }
424
425 pub fn increment(&self) {
426 self.count
427 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
428 }
429
430 pub fn get(&self) -> usize {
431 self.count.load(std::sync::atomic::Ordering::Relaxed)
432 }
433}
434
435#[cfg(not(test))]
436pub struct TestCallCount {}
437
438#[cfg(not(test))]
439impl TestCallCount {
440 pub fn new() -> Self {
441 TestCallCount {}
442 }
443
444 pub fn enabled() -> bool {
445 false
446 }
447
448 pub fn increment(&self) {}
449
450 pub fn get(&self) -> usize {
451 0
452 }
453}
454
455impl Default for TestCallCount {
456 fn default() -> Self {
457 Self::new()
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use std::num::NonZeroUsize;
464
465 use super::*;
466
467 #[test]
468 fn new_creates_correct_range() {
469 let sp = StartPoints::new(10, NonZeroUsize::new(5).unwrap())
471 .expect("should construct without overflow");
472 let r = sp.range().collect::<Vec<_>>();
473 assert_eq!(r, vec![10, 11, 12, 13, 14]);
474 assert_eq!(sp.end(), 15);
475 }
476
477 #[test]
478 fn new_returns_error_on_overflow() {
479 let max = u32::MAX;
481 let res = StartPoints::new(max, NonZeroUsize::new(1).unwrap());
482 assert!(res.is_err(), "expected an error when sum exceeds u32::MAX");
483 if let Err(err) = res {
484 let msg = err.to_string();
485 assert!(
486 msg.contains("Sum of valid points and frozen points exceeds u32::MAX"),
487 "unexpected error message: {}",
488 msg
489 );
490 }
491 }
492}