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 struct StartPoints {
23 start: u32,
24 end: u32,
25}
26
27impl StartPoints {
28 pub fn new(valid_points: u32, frozen_points: NonZeroUsize) -> ANNResult<Self> {
29 Ok(Self {
30 start: valid_points,
31 end: match valid_points.checked_add(frozen_points.get() as u32) {
32 Some(end) => end,
33 None => {
34 return Err(ANNError::log_index_error(
35 "Sum of valid points and frozen points exceeds u32::MAX",
36 ));
37 }
38 },
39 })
40 }
41
42 pub fn range(&self) -> std::ops::Range<u32> {
43 self.start..self.end
44 }
45
46 pub fn len(&self) -> usize {
47 (self.end - self.start).into_usize()
48 }
49
50 pub fn is_empty(&self) -> bool {
51 self.len() == 0
52 }
53
54 pub fn start(&self) -> u32 {
55 self.start
56 }
57
58 pub fn end(&self) -> u32 {
59 self.end
60 }
61}
62
63pub struct VectorGuard<T> {
64 inner: Guard<Arc<Vec<T>>>,
65}
66
67impl<T> VectorGuard<T> {
68 pub(crate) fn from_guard(guard: Guard<Arc<Vec<T>>>) -> Self {
69 Self { inner: guard }
70 }
71}
72
73impl<T> Deref for VectorGuard<T> {
74 type Target = [T];
75
76 fn deref(&self) -> &Self::Target {
77 self.inner.deref()
78 }
79}
80
81pub struct AlignedMemoryVectorStore<T: bytemuck::Pod> {
88 store: UnsafeCell<Vec<T>>,
89 max_vectors: usize,
90 start_index: usize,
91 dim: usize,
92 padded_vector_dim: usize,
93}
94
95unsafe impl<T: bytemuck::Pod + Sync> Sync for AlignedMemoryVectorStore<T> {}
97
98unsafe impl<T: bytemuck::Pod + Send> Send for AlignedMemoryVectorStore<T> {}
100
101impl<T: bytemuck::Pod> AlignedMemoryVectorStore<T> {
102 pub fn with_capacity(max_vectors: usize, dim: usize) -> Self {
103 let elem_size = mem::size_of::<T>();
104 assert!(64 % elem_size == 0);
105 let vector_size = elem_size * dim;
106 let extra_size = vector_size % 64;
107 let padded_vector_dim = if extra_size == 0 {
108 dim
110 } else {
111 let padding_needed_size = 64 - extra_size;
112 assert!(padding_needed_size.is_multiple_of(elem_size));
113 let extra_elems = padding_needed_size / elem_size;
114 dim + extra_elems
115 };
116
117 assert!((padded_vector_dim * elem_size).is_multiple_of(64));
118
119 let last_elems: usize = 64 / elem_size - 1;
122
123 let count = max_vectors * padded_vector_dim + last_elems;
124 let mut store: UnsafeCell<Vec<T>> =
125 UnsafeCell::new(vec![<T as bytemuck::Zeroable>::zeroed(); count]);
126
127 let start_index = store.get_mut().as_ptr().align_offset(64);
128
129 Self {
130 store,
131 max_vectors,
132 start_index,
133 dim,
134 padded_vector_dim,
135 }
136 }
137
138 pub fn max_vectors(&self) -> usize {
139 self.max_vectors
140 }
141
142 pub fn dim(&self) -> usize {
143 self.dim
144 }
145
146 #[inline(always)]
156 pub unsafe fn get_slice(&self, index: usize) -> &[T] {
157 assert!(
158 index < self.max_vectors,
159 "index ({}) exceeded max_vectors ({})",
160 index,
161 self.max_vectors
162 );
163 let index = index * self.padded_vector_dim + self.start_index;
164
165 let buf = unsafe { (*self.store.get()).as_ptr() };
169
170 unsafe { slice::from_raw_parts(buf.add(index), self.dim) }
172 }
173
174 #[allow(clippy::mut_from_ref)]
181 pub unsafe fn get_mut_slice(&self, index: usize) -> &mut [T] {
182 assert!(index < self.max_vectors);
183 let index = index * self.padded_vector_dim + self.start_index;
184
185 unsafe {
189 let buf = (*self.store.get()).as_mut_ptr();
190 slice::from_raw_parts_mut(buf.add(index), self.dim)
191 }
192 }
193}
194
195#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
197pub enum PrefetchCacheLineLevel {
198 CacheLine4,
200 CacheLine8,
202 #[default]
204 CacheLine16,
205 All,
207}
208
209#[derive(Debug, Clone, Copy)]
219pub struct Unseeded;
220
221pub trait SetElementHelper<T> {
223 fn set_element(&self, index: &u32, element: &[T]) -> ANNResult<()>;
225}
226
227pub trait CreateVectorStore {
232 type Target: VectorStore;
234
235 fn create(
237 self,
238 max_points: usize,
239 metric: Metric,
240 prefetch_lookahead: Option<usize>,
241 ) -> Self::Target;
242}
243
244pub trait CreateDeleteProvider {
249 type Target;
251
252 fn create(self, total_points: usize) -> Self::Target;
258}
259
260pub trait VectorStore: AsyncFriendly {
261 fn total(&self) -> usize;
263
264 fn count_for_get_vector(&self) -> usize;
266}
267
268#[derive(Debug)]
274pub enum Panics {}
275
276impl std::fmt::Display for Panics {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 write!(f, "panics")
279 }
280}
281
282impl std::error::Error for Panics {}
283impl From<Panics> for ANNError {
284 #[cold]
285 fn from(_: Panics) -> ANNError {
286 ANNError::log_async_error("unreachable")
287 }
288}
289
290always_escalate!(Panics);
291
292#[derive(Debug, Clone, Copy)]
296pub struct NoStore;
297
298impl CreateVectorStore for NoStore {
299 type Target = NoStore;
300 fn create(
301 self,
302 _max_points: usize,
303 _metric: Metric,
304 _prefetch_lookahead: Option<usize>,
305 ) -> Self::Target {
306 self
307 }
308}
309
310impl VectorStore for NoStore {
311 fn total(&self) -> usize {
312 0
313 }
314
315 fn count_for_get_vector(&self) -> usize {
316 0
317 }
318}
319
320impl LoadWith<AsyncQuantLoadContext> for NoStore {
321 type Error = ANNError;
322 async fn load_with<P>(_: &P, _: &AsyncQuantLoadContext) -> ANNResult<Self>
323 where
324 P: StorageReadProvider,
325 {
326 Ok(Self)
327 }
328}
329
330impl SaveWith<AsyncIndexMetadata> for NoStore {
331 type Ok = usize;
332 type Error = ANNError;
333 async fn save_with<P>(&self, _provider: &P, _auxiliary: &AsyncIndexMetadata) -> ANNResult<usize>
334 where
335 P: StorageWriteProvider,
336 {
337 Ok(0)
338 }
339}
340
341impl<T> SetElementHelper<T> for NoStore {
342 fn set_element(&self, _index: &u32, _element: &[T]) -> ANNResult<()> {
343 Ok(())
344 }
345}
346
347#[derive(Debug, Clone, Copy)]
351pub struct NoDeletes;
352
353impl postprocess::DeletionCheck for NoDeletes {
354 #[inline(always)]
359 fn deletion_check(&self, _: u32) -> bool {
360 false
361 }
362}
363
364impl CreateDeleteProvider for NoDeletes {
365 type Target = Self;
366 fn create(self, _: usize) -> Self {
367 Self
368 }
369}
370
371#[derive(Debug, Clone, Copy)]
373pub struct TableBasedDeletes;
374
375impl CreateDeleteProvider for TableBasedDeletes {
376 type Target = TableDeleteProviderAsync;
377 fn create(self, total_points: usize) -> Self::Target {
378 TableDeleteProviderAsync::new(total_points)
379 }
380}
381
382#[derive(Debug, Clone, Copy)]
386pub struct FullPrecision;
387
388#[derive(Debug, Clone, Copy)]
393pub struct Quantized;
394
395#[derive(Debug, Clone, Copy)]
408pub struct Hybrid {
409 pub max_fp_vecs_per_prune: Option<usize>,
413}
414
415impl Hybrid {
416 pub fn new(max_fp_vecs_per_prune: Option<usize>) -> Self {
421 Self {
422 max_fp_vecs_per_prune,
423 }
424 }
425}
426
427#[cfg(test)]
428pub struct TestCallCount {
429 count: std::sync::atomic::AtomicUsize,
430}
431
432#[cfg(test)]
433impl TestCallCount {
434 pub fn new() -> Self {
435 TestCallCount {
436 count: std::sync::atomic::AtomicUsize::new(0),
437 }
438 }
439
440 pub fn enabled() -> bool {
441 true
442 }
443
444 pub fn increment(&self) {
445 self.count
446 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
447 }
448
449 pub fn get(&self) -> usize {
450 self.count.load(std::sync::atomic::Ordering::Relaxed)
451 }
452}
453
454#[cfg(not(test))]
455pub struct TestCallCount {}
456
457#[cfg(not(test))]
458impl TestCallCount {
459 pub fn new() -> Self {
460 TestCallCount {}
461 }
462
463 pub fn enabled() -> bool {
464 false
465 }
466
467 pub fn increment(&self) {}
468
469 pub fn get(&self) -> usize {
470 0
471 }
472}
473
474impl Default for TestCallCount {
475 fn default() -> Self {
476 Self::new()
477 }
478}
479
480#[cfg(test)]
481mod tests {
482 use std::num::NonZeroUsize;
483
484 use super::*;
485
486 #[test]
487 fn new_creates_correct_range() {
488 let sp = StartPoints::new(10, NonZeroUsize::new(5).unwrap())
490 .expect("should construct without overflow");
491 let r = sp.range().collect::<Vec<_>>();
492 assert_eq!(r, vec![10, 11, 12, 13, 14]);
493 assert_eq!(sp.end(), 15);
494 }
495
496 #[test]
497 fn new_returns_error_on_overflow() {
498 let max = u32::MAX;
500 let res = StartPoints::new(max, NonZeroUsize::new(1).unwrap());
501 assert!(res.is_err(), "expected an error when sum exceeds u32::MAX");
502 if let Err(err) = res {
503 let msg = err.to_string();
504 assert!(
505 msg.contains("Sum of valid points and frozen points exceeds u32::MAX"),
506 "unexpected error message: {}",
507 msg
508 );
509 }
510 }
511}