mirror of
https://github.com/qdrant/qdrant.git
synced 2026-09-21 13:37:46 -05:00
[UIO] Explicit padding for numeric point (#8668)
* derive Pod for Point<T> * remove clones on Copy type * fix annoying clippy lint * serde skip padding * extract point into a new file * assert aligment
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::Bound::{Excluded, Included, Unbounded};
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Bound;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use common::fs::{atomic_save_bin, atomic_save_json, read_bin, read_json};
|
||||
use common::types::PointOffsetType;
|
||||
use itertools::Itertools;
|
||||
use num_traits::Num;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::common::operation_error::OperationResult;
|
||||
use crate::index::field_index::numeric_point::{Numericable, Point};
|
||||
use crate::index::field_index::utils::check_boundaries;
|
||||
|
||||
const MIN_BUCKET_SIZE: usize = 10;
|
||||
@@ -23,114 +24,6 @@ pub struct Counts {
|
||||
pub right: usize,
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_ord_xor_partial_ord)]
|
||||
#[derive(PartialEq, PartialOrd, Debug, Clone, Serialize, Deserialize)]
|
||||
#[repr(C)]
|
||||
pub struct Point<T> {
|
||||
pub val: T,
|
||||
pub idx: PointOffsetType,
|
||||
}
|
||||
|
||||
impl<T> Point<T> {
|
||||
pub fn new(val: T, idx: PointOffsetType) -> Self {
|
||||
Self { val, idx }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> Eq for Point<T> {}
|
||||
|
||||
impl<T: PartialOrd + Copy> Ord for Point<T> {
|
||||
fn cmp(&self, other: &Point<T>) -> std::cmp::Ordering {
|
||||
(self.val, self.idx)
|
||||
.partial_cmp(&(other.val, other.idx))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait that should represent common properties of integer and floating point types.
|
||||
/// In particular, i64 and f64.
|
||||
pub trait Numericable: Num + PartialEq + PartialOrd + Copy {
|
||||
fn min_value() -> Self;
|
||||
fn max_value() -> Self;
|
||||
fn to_f64(self) -> f64;
|
||||
fn from_f64(x: f64) -> Self;
|
||||
fn from_u128(x: u128) -> Self;
|
||||
fn min(self, b: Self) -> Self {
|
||||
if self < b { self } else { b }
|
||||
}
|
||||
fn max(self, b: Self) -> Self {
|
||||
if self > b { self } else { b }
|
||||
}
|
||||
fn abs_diff(self, b: Self) -> Self {
|
||||
if self > b { self - b } else { b - self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Numericable for i64 {
|
||||
fn min_value() -> Self {
|
||||
i64::MIN
|
||||
}
|
||||
fn max_value() -> Self {
|
||||
i64::MAX
|
||||
}
|
||||
fn to_f64(self) -> f64 {
|
||||
self as f64
|
||||
}
|
||||
fn from_f64(x: f64) -> Self {
|
||||
x as Self
|
||||
}
|
||||
fn from_u128(x: u128) -> Self {
|
||||
x as i64
|
||||
}
|
||||
fn abs_diff(self, b: Self) -> Self {
|
||||
i64::abs_diff(self, b) as i64
|
||||
}
|
||||
}
|
||||
|
||||
impl Numericable for f64 {
|
||||
fn min_value() -> Self {
|
||||
f64::MIN
|
||||
}
|
||||
fn max_value() -> Self {
|
||||
f64::MAX
|
||||
}
|
||||
fn to_f64(self) -> f64 {
|
||||
self
|
||||
}
|
||||
fn from_f64(x: f64) -> Self {
|
||||
x
|
||||
}
|
||||
fn from_u128(x: u128) -> Self {
|
||||
x as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Numericable for u128 {
|
||||
fn min_value() -> Self {
|
||||
u128::MIN
|
||||
}
|
||||
|
||||
fn max_value() -> Self {
|
||||
u128::MAX
|
||||
}
|
||||
|
||||
fn to_f64(self) -> f64 {
|
||||
self as f64
|
||||
}
|
||||
|
||||
fn from_f64(x: f64) -> Self {
|
||||
x as u128
|
||||
}
|
||||
|
||||
fn from_u128(x: u128) -> Self {
|
||||
x
|
||||
}
|
||||
|
||||
fn abs_diff(self, b: Self) -> Self {
|
||||
u128::abs_diff(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq)]
|
||||
pub struct Histogram<T: Numericable + Serialize + DeserializeOwned> {
|
||||
max_bucket_size: usize,
|
||||
@@ -186,11 +79,8 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
},
|
||||
)?;
|
||||
|
||||
let borders: Vec<(Point<T>, Counts)> = self
|
||||
.borders
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect();
|
||||
let borders: Vec<(Point<T>, Counts)> =
|
||||
self.borders.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
atomic_save_bin(&borders_path, &borders)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -229,14 +119,8 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
/// Returns `Unbounded` if there are no points stored
|
||||
pub fn get_range_by_size(&self, from: Bound<T>, range_size: usize) -> Bound<T> {
|
||||
let from_ = match from {
|
||||
Included(val) => Included(Point {
|
||||
val,
|
||||
idx: PointOffsetType::MIN,
|
||||
}),
|
||||
Excluded(val) => Excluded(Point {
|
||||
val,
|
||||
idx: PointOffsetType::MAX,
|
||||
}),
|
||||
Included(val) => Included(Point::new(val, PointOffsetType::MIN)),
|
||||
Excluded(val) => Excluded(Point::new(val, PointOffsetType::MAX)),
|
||||
Unbounded => Unbounded,
|
||||
};
|
||||
|
||||
@@ -256,26 +140,14 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
|
||||
pub fn estimate(&self, from: Bound<T>, to: Bound<T>) -> (usize, usize, usize) {
|
||||
let from_ = match &from {
|
||||
Included(val) => Included(Point {
|
||||
val: *val,
|
||||
idx: PointOffsetType::MIN,
|
||||
}),
|
||||
Excluded(val) => Excluded(Point {
|
||||
val: *val,
|
||||
idx: PointOffsetType::MAX,
|
||||
}),
|
||||
Included(val) => Included(Point::new(*val, PointOffsetType::MIN)),
|
||||
Excluded(val) => Excluded(Point::new(*val, PointOffsetType::MAX)),
|
||||
Unbounded => Unbounded,
|
||||
};
|
||||
|
||||
let to_ = match &to {
|
||||
Included(val) => Included(Point {
|
||||
val: *val,
|
||||
idx: PointOffsetType::MAX,
|
||||
}),
|
||||
Excluded(val) => Excluded(Point {
|
||||
val: *val,
|
||||
idx: PointOffsetType::MIN,
|
||||
}),
|
||||
Included(val) => Included(Point::new(*val, PointOffsetType::MAX)),
|
||||
Excluded(val) => Excluded(Point::new(*val, PointOffsetType::MIN)),
|
||||
Unbounded => Unbounded,
|
||||
};
|
||||
|
||||
@@ -296,7 +168,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
if matches!(from_, Unbounded) {
|
||||
None
|
||||
} else {
|
||||
self.borders.range((Unbounded, from_.clone())).next_back()
|
||||
self.borders.range((Unbounded, from_)).next_back()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -304,7 +176,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
if matches!(to_, Unbounded) {
|
||||
None
|
||||
} else {
|
||||
self.borders.range((to_.clone(), Unbounded)).next()
|
||||
self.borders.range((to_, Unbounded)).next()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -359,12 +231,12 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
let (mut close_neighbors, (mut far_left_neighbor, mut far_right_neighbor)) = {
|
||||
let mut left_iterator = self
|
||||
.borders
|
||||
.range((Unbounded, Included(val.clone())))
|
||||
.map(|(k, v)| (k.clone(), v.clone()));
|
||||
.range((Unbounded, Included(val)))
|
||||
.map(|(k, v)| (*k, v.clone()));
|
||||
let mut right_iterator = self
|
||||
.borders
|
||||
.range((Excluded(val.clone()), Unbounded))
|
||||
.map(|(k, v)| (k.clone(), v.clone()));
|
||||
.range((Excluded(val), Unbounded))
|
||||
.map(|(k, v)| (*k, v.clone()));
|
||||
(
|
||||
(left_iterator.next_back(), right_iterator.next()),
|
||||
(left_iterator.next_back(), right_iterator.next()),
|
||||
@@ -380,7 +252,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
if left_border_count.left == 0 {
|
||||
// ...||
|
||||
// ...|
|
||||
(Some(left_border.clone()), None, true)
|
||||
(Some(*left_border), None, true)
|
||||
} else {
|
||||
// ...|..|
|
||||
// ...|.|
|
||||
@@ -395,7 +267,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
},
|
||||
);
|
||||
(
|
||||
Some(left_border.clone()),
|
||||
Some(*left_border),
|
||||
Some((new_border, new_border_count)),
|
||||
true,
|
||||
)
|
||||
@@ -411,7 +283,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
if right_border_count.right == 0 {
|
||||
// ||...
|
||||
// |...
|
||||
(Some(right_border.clone()), None, true)
|
||||
(Some(*right_border), None, true)
|
||||
} else {
|
||||
// |..|...
|
||||
// |.|...
|
||||
@@ -426,7 +298,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
},
|
||||
);
|
||||
(
|
||||
Some(right_border.clone()),
|
||||
Some(*right_border),
|
||||
Some((new_border, new_border_count)),
|
||||
true,
|
||||
)
|
||||
@@ -444,7 +316,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
// ...||...
|
||||
// ... |...
|
||||
right_border_count.left = left_border_count.left;
|
||||
(Some(left_border.clone()), None, true)
|
||||
(Some(*left_border), None, true)
|
||||
} else if right_border_count.left + left_border_count.left
|
||||
<= self.current_bucket_size()
|
||||
&& far_left_neighbor.is_some()
|
||||
@@ -455,7 +327,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
fln_count.right += right_border_count.left;
|
||||
right_border_count.left = fln_count.right;
|
||||
}
|
||||
(Some(left_border.clone()), None, true)
|
||||
(Some(*left_border), None, true)
|
||||
} else {
|
||||
// ...|..|...
|
||||
// ... |.|...
|
||||
@@ -468,7 +340,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
},
|
||||
);
|
||||
(
|
||||
Some(left_border.clone()),
|
||||
Some(*left_border),
|
||||
Some((new_border, new_border_count)),
|
||||
true,
|
||||
)
|
||||
@@ -480,7 +352,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
// ...||...
|
||||
// ...| ...
|
||||
left_border_count.right = right_border_count.left;
|
||||
(Some(right_border.clone()), None, true)
|
||||
(Some(*right_border), None, true)
|
||||
} else if left_border_count.right + right_border_count.right
|
||||
<= self.current_bucket_size()
|
||||
&& far_right_neighbor.is_some()
|
||||
@@ -491,7 +363,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
frn_count.left += left_border_count.right;
|
||||
left_border_count.right = frn_count.left;
|
||||
}
|
||||
(Some(right_border.clone()), None, true)
|
||||
(Some(*right_border), None, true)
|
||||
} else {
|
||||
// ...|..|...
|
||||
// ...|.| ...
|
||||
@@ -504,7 +376,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
},
|
||||
);
|
||||
(
|
||||
Some(right_border.clone()),
|
||||
Some(*right_border),
|
||||
Some((new_border, new_border_count)),
|
||||
true,
|
||||
)
|
||||
@@ -570,12 +442,12 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
let (mut close_neighbors, (mut far_left_neighbor, mut far_right_neighbor)) = {
|
||||
let mut left_iterator = self
|
||||
.borders
|
||||
.range((Unbounded, Included(val.clone())))
|
||||
.map(|(k, v)| (k.clone(), v.clone()));
|
||||
.range((Unbounded, Included(val)))
|
||||
.map(|(k, v)| (*k, v.clone()));
|
||||
let mut right_iterator = self
|
||||
.borders
|
||||
.range((Excluded(val.clone()), Unbounded))
|
||||
.map(|(k, v)| (k.clone(), v.clone()));
|
||||
.range((Excluded(val), Unbounded))
|
||||
.map(|(k, v)| (*k, v.clone()));
|
||||
(
|
||||
(left_iterator.next_back(), right_iterator.next()),
|
||||
(left_iterator.next_back(), right_iterator.next()),
|
||||
@@ -606,10 +478,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
if let Some((_frn, frn_count)) = &mut far_right_neighbor {
|
||||
frn_count.left = new_count;
|
||||
}
|
||||
(
|
||||
Some(right_border.clone()),
|
||||
Some((new_border, new_border_count)),
|
||||
)
|
||||
(Some(*right_border), Some((new_border, new_border_count)))
|
||||
}
|
||||
}
|
||||
(Some((left_border, left_border_count)), None) => {
|
||||
@@ -634,10 +503,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
if let Some((_fln, fln_count)) = &mut far_left_neighbor {
|
||||
fln_count.right = new_count
|
||||
}
|
||||
(
|
||||
Some(left_border.clone()),
|
||||
Some((new_border, new_border_count)),
|
||||
)
|
||||
(Some(*left_border), Some((new_border, new_border_count)))
|
||||
}
|
||||
}
|
||||
(Some((left_border, left_border_count)), Some((right_border, right_border_count))) => {
|
||||
@@ -669,10 +535,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
if let Some((_fln, fln_count)) = &mut far_left_neighbor {
|
||||
fln_count.right = new_border_count.left
|
||||
}
|
||||
(
|
||||
Some(left_border.clone()),
|
||||
Some((new_border, new_border_count)),
|
||||
)
|
||||
(Some(*left_border), Some((new_border, new_border_count)))
|
||||
} else {
|
||||
// Can't be moved anymore, create an additional one
|
||||
// ...|..x.........|...
|
||||
@@ -701,10 +564,7 @@ impl<T: Numericable + Serialize + DeserializeOwned> Histogram<T> {
|
||||
if let Some((_frn, frn_count)) = &mut far_right_neighbor {
|
||||
frn_count.left = new_border_count.right
|
||||
}
|
||||
(
|
||||
Some(right_border.clone()),
|
||||
Some((new_border, new_border_count)),
|
||||
)
|
||||
(Some(*right_border), Some((new_border, new_border_count)))
|
||||
} else {
|
||||
// Can't be moved anymore, create a new one
|
||||
// 1: ...|........x...|...
|
||||
|
||||
@@ -5,7 +5,6 @@ use gridstore::Blob;
|
||||
use super::bool_index::BoolIndex;
|
||||
use super::bool_index::mutable_bool_index::MutableBoolIndex;
|
||||
use super::geo_index::{GeoMapIndexGridstoreBuilder, GeoMapIndexMmapBuilder};
|
||||
use super::histogram::Numericable;
|
||||
use super::map_index::{MapIndex, MapIndexGridstoreBuilder, MapIndexKey, MapIndexMmapBuilder};
|
||||
use super::numeric_index::{
|
||||
Encodable, NumericIndexGridstoreBuilder, NumericIndexIntoInnerValue, NumericIndexMmapBuilder,
|
||||
@@ -19,6 +18,7 @@ use crate::index::field_index::full_text_index::text_index::FullTextIndex;
|
||||
use crate::index::field_index::geo_index::GeoMapIndex;
|
||||
use crate::index::field_index::null_index::MutableNullIndex;
|
||||
use crate::index::field_index::numeric_index::NumericIndex;
|
||||
use crate::index::field_index::numeric_point::Numericable;
|
||||
use crate::index::payload_config::{FullPayloadIndexType, PayloadIndexType};
|
||||
use crate::json_path::JsonPath;
|
||||
use crate::types::{PayloadFieldSchema, PayloadSchemaParams};
|
||||
|
||||
@@ -18,6 +18,7 @@ pub mod map_index;
|
||||
mod memory_reporter;
|
||||
pub mod null_index;
|
||||
pub mod numeric_index;
|
||||
mod numeric_point;
|
||||
mod stat_tools;
|
||||
mod stored_point_to_values;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -11,8 +11,9 @@ use super::mmap_numeric_index::MmapNumericIndex;
|
||||
use super::mutable_numeric_index::InMemoryNumericIndex;
|
||||
use crate::common::Flusher;
|
||||
use crate::common::operation_error::OperationResult;
|
||||
use crate::index::field_index::histogram::{Histogram, Numericable, Point};
|
||||
use crate::index::field_index::histogram::Histogram;
|
||||
use crate::index::field_index::immutable_point_to_values::ImmutablePointToValues;
|
||||
use crate::index::field_index::numeric_point::{Numericable, Point};
|
||||
use crate::index::field_index::stored_point_to_values::StoredValue;
|
||||
use crate::index::payload_config::StorageType;
|
||||
|
||||
@@ -149,7 +150,7 @@ impl<T: Encodable + Numericable> Iterator for NumericKeySortedVecIterator<'_, T>
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while self.start_index < self.end_index {
|
||||
let key = self.set.data[self.start_index].clone();
|
||||
let key = self.set.data[self.start_index];
|
||||
let deleted = self.set.deleted.get_bit(self.start_index).unwrap_or(true);
|
||||
self.start_index += 1;
|
||||
if deleted {
|
||||
@@ -164,7 +165,7 @@ impl<T: Encodable + Numericable> Iterator for NumericKeySortedVecIterator<'_, T>
|
||||
impl<T: Encodable + Numericable> DoubleEndedIterator for NumericKeySortedVecIterator<'_, T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
while self.start_index < self.end_index {
|
||||
let key = self.set.data[self.end_index - 1].clone();
|
||||
let key = self.set.data[self.end_index - 1];
|
||||
let deleted = self.set.deleted.get_bit(self.end_index - 1).unwrap_or(true);
|
||||
self.end_index -= 1;
|
||||
if deleted {
|
||||
@@ -353,7 +354,7 @@ where
|
||||
map: &NumericKeySortedVec<T>,
|
||||
point: &Point<T>,
|
||||
) -> Option<Point<T>> {
|
||||
map.values_range(Bound::Unbounded, Bound::Excluded(point.clone()))
|
||||
map.values_range(Bound::Unbounded, Bound::Excluded(*point))
|
||||
.next_back()
|
||||
}
|
||||
|
||||
@@ -361,7 +362,7 @@ where
|
||||
map: &NumericKeySortedVec<T>,
|
||||
point: &Point<T>,
|
||||
) -> Option<Point<T>> {
|
||||
map.values_range(Bound::Excluded(point.clone()), Bound::Unbounded)
|
||||
map.values_range(Bound::Excluded(*point), Bound::Unbounded)
|
||||
.next()
|
||||
}
|
||||
|
||||
@@ -388,7 +389,7 @@ mod tests {
|
||||
end_bound: Bound<Point<FloatPayloadType>>,
|
||||
) {
|
||||
let set1 = key_set
|
||||
.values_range(start_bound.clone(), end_bound.clone())
|
||||
.values_range(start_bound, end_bound)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let set2 = encoded_map
|
||||
|
||||
@@ -20,7 +20,8 @@ use crate::common::Flusher;
|
||||
use crate::common::buffered_update_bitslice::BufferedUpdateBitSlice;
|
||||
use crate::common::operation_error::{OperationError, OperationResult};
|
||||
use crate::common::stored_bitslice::MmapBitSlice;
|
||||
use crate::index::field_index::histogram::{Histogram, Numericable, Point};
|
||||
use crate::index::field_index::histogram::Histogram;
|
||||
use crate::index::field_index::numeric_point::{Numericable, Point};
|
||||
use crate::index::field_index::stored_point_to_values::{StoredPointToValues, StoredValue};
|
||||
|
||||
const PAIRS_PATH: &str = "data.bin";
|
||||
@@ -63,7 +64,7 @@ impl<T: Encodable + Numericable> Iterator for NumericIndexPairsIterator<'_, T> {
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while self.start_index < self.end_index {
|
||||
let key = self.pairs[self.start_index].clone();
|
||||
let key = self.pairs[self.start_index];
|
||||
let deleted = self.deleted.get(key.idx as usize).unwrap_or(true);
|
||||
self.start_index += 1;
|
||||
if deleted {
|
||||
@@ -78,7 +79,7 @@ impl<T: Encodable + Numericable> Iterator for NumericIndexPairsIterator<'_, T> {
|
||||
impl<T: Encodable + Numericable> DoubleEndedIterator for NumericIndexPairsIterator<'_, T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
while self.start_index < self.end_index {
|
||||
let key = self.pairs[self.end_index - 1].clone();
|
||||
let key = self.pairs[self.end_index - 1];
|
||||
let deleted = self.deleted.get(key.idx as usize).unwrap_or(true);
|
||||
self.end_index -= 1;
|
||||
if deleted {
|
||||
@@ -128,7 +129,7 @@ impl<T: Encodable + Numericable + Default + StoredValue> MmapNumericIndex<T> {
|
||||
let pairs_mmap = unsafe { MmapMut::map_mut(&pairs_file)? };
|
||||
let mut pairs = unsafe { MmapSlice::<Point<T>>::try_from(pairs_mmap)? };
|
||||
for (src, dst) in in_memory_index.map.iter().zip(pairs.iter_mut()) {
|
||||
*dst = src.clone();
|
||||
*dst = *src;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +299,7 @@ impl<T: Encodable + Numericable + Default + StoredValue> MmapNumericIndex<T> {
|
||||
end_bound: Bound<Point<T>>,
|
||||
) -> impl DoubleEndedIterator<Item = (T, PointOffsetType)> + '_ {
|
||||
self.values_range_iterator(start_bound, end_bound)
|
||||
.map(|Point { val, idx }| (val, idx))
|
||||
.map(|Point { val, idx, .. }| (val, idx))
|
||||
}
|
||||
|
||||
pub fn remove_point(&mut self, idx: PointOffsetType) {
|
||||
|
||||
@@ -27,12 +27,12 @@ use uuid::Uuid;
|
||||
|
||||
use self::immutable_numeric_index::ImmutableNumericIndex;
|
||||
use super::FieldIndexBuilderTrait;
|
||||
use super::histogram::Point;
|
||||
use super::stored_point_to_values::StoredValue;
|
||||
use super::utils::{check_boundaries, value_to_integer};
|
||||
use crate::common::Flusher;
|
||||
use crate::common::operation_error::{OperationError, OperationResult};
|
||||
use crate::index::field_index::histogram::{Histogram, Numericable};
|
||||
use crate::index::field_index::histogram::Histogram;
|
||||
use crate::index::field_index::numeric_point::{Numericable, Point};
|
||||
use crate::index::field_index::stat_tools::estimate_multi_value_selection_cardinality;
|
||||
use crate::index::field_index::{
|
||||
CardinalityEstimation, PayloadBlockCondition, PayloadFieldIndex, PrimaryCondition, ValueIndexer,
|
||||
|
||||
@@ -13,7 +13,8 @@ use super::mmap_numeric_index::MmapNumericIndex;
|
||||
use super::{Encodable, HISTOGRAM_MAX_BUCKET_SIZE, HISTOGRAM_PRECISION};
|
||||
use crate::common::Flusher;
|
||||
use crate::common::operation_error::{OperationError, OperationResult};
|
||||
use crate::index::field_index::histogram::{Histogram, Numericable, Point};
|
||||
use crate::index::field_index::histogram::Histogram;
|
||||
use crate::index::field_index::numeric_point::{Numericable, Point};
|
||||
use crate::index::field_index::stored_point_to_values::StoredValue;
|
||||
use crate::index::payload_config::StorageType;
|
||||
|
||||
@@ -186,15 +187,15 @@ impl<T: Encodable + Numericable + Default> InMemoryNumericIndex<T> {
|
||||
}
|
||||
|
||||
fn add_to_map(map: &mut BTreeSet<Point<T>>, histogram: &mut Histogram<T>, key: Point<T>) {
|
||||
let was_added = map.insert(key.clone());
|
||||
let was_added = map.insert(key);
|
||||
// Histogram works with unique values (idx + value) only, so we need to
|
||||
// make sure that we don't add the same value twice.
|
||||
// key is a combination of value + idx, so we can use it to ensure than the pair is unique
|
||||
if was_added {
|
||||
histogram.insert(
|
||||
key,
|
||||
|x| Self::get_histogram_left_neighbor(map, x.clone()),
|
||||
|x| Self::get_histogram_right_neighbor(map, x.clone()),
|
||||
|x| Self::get_histogram_left_neighbor(map, *x),
|
||||
|x| Self::get_histogram_right_neighbor(map, *x),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -204,18 +205,18 @@ impl<T: Encodable + Numericable + Default> InMemoryNumericIndex<T> {
|
||||
if was_removed {
|
||||
histogram.remove(
|
||||
&key,
|
||||
|x| Self::get_histogram_left_neighbor(map, x.clone()),
|
||||
|x| Self::get_histogram_right_neighbor(map, x.clone()),
|
||||
|x| Self::get_histogram_left_neighbor(map, *x),
|
||||
|x| Self::get_histogram_right_neighbor(map, *x),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_histogram_left_neighbor(map: &BTreeSet<Point<T>>, key: Point<T>) -> Option<Point<T>> {
|
||||
map.range((Unbounded, Excluded(key))).next_back().cloned()
|
||||
map.range((Unbounded, Excluded(key))).next_back().copied()
|
||||
}
|
||||
|
||||
fn get_histogram_right_neighbor(map: &BTreeSet<Point<T>>, key: Point<T>) -> Option<Point<T>> {
|
||||
map.range((Excluded(key), Unbounded)).next().cloned()
|
||||
map.range((Excluded(key), Unbounded)).next().copied()
|
||||
}
|
||||
|
||||
pub fn get_histogram(&self) -> &Histogram<T> {
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use common::types::PointOffsetType;
|
||||
use num_traits::Num;
|
||||
use serde::Serialize;
|
||||
|
||||
pub use self::point::Point;
|
||||
|
||||
// bytemuck macros expand to code that triggers this clippy lint
|
||||
// The only reason this is its own module is so that we scope the lint suppression
|
||||
#[expect(clippy::multiple_bound_locations)]
|
||||
mod point {
|
||||
use common::types::PointOffsetType;
|
||||
|
||||
use super::Numericable;
|
||||
|
||||
#[expect(clippy::derive_ord_xor_partial_ord)]
|
||||
#[derive(
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
bytemuck::Pod,
|
||||
bytemuck::Zeroable,
|
||||
)]
|
||||
#[repr(C, packed)]
|
||||
pub struct Point<T: Numericable> {
|
||||
pub val: T,
|
||||
pub idx: PointOffsetType,
|
||||
#[serde(skip)]
|
||||
_padding: T::PointPadding,
|
||||
}
|
||||
|
||||
impl<T: Numericable> Point<T> {
|
||||
pub fn new(val: T, idx: PointOffsetType) -> Self {
|
||||
Self {
|
||||
val,
|
||||
idx,
|
||||
_padding: bytemuck::Zeroable::zeroed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Numericable> Eq for Point<T> {}
|
||||
|
||||
impl<T: PartialOrd + Copy + Numericable> Ord for Point<T> {
|
||||
fn cmp(&self, other: &Point<T>) -> std::cmp::Ordering {
|
||||
(self.val, self.idx)
|
||||
.partial_cmp(&(other.val, other.idx))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the exact padding so that the [`Point<T>`] type would have explicit alignment, so that
|
||||
/// it is able to derive [`bytemuck::Pod`] and use #[repr(packed)] safely
|
||||
const fn derive_point_padding<T: bytemuck::Pod>() -> usize {
|
||||
struct Point<T> {
|
||||
_t: T,
|
||||
_idx: PointOffsetType,
|
||||
}
|
||||
let align = std::mem::align_of::<Point<T>>();
|
||||
|
||||
// Since we are adding padding at the end, we need to ensure that the align of T is larger the one
|
||||
// of PointOffsetType, so that there is no inter-field padding
|
||||
assert!(std::mem::align_of::<T>() >= std::mem::align_of::<PointOffsetType>());
|
||||
|
||||
align - (std::mem::size_of::<T>() + std::mem::size_of::<PointOffsetType>()) % align
|
||||
}
|
||||
|
||||
/// A trait that should represent common properties of integer and floating point types.
|
||||
/// In particular, i64 and f64.
|
||||
pub trait Numericable: Num + PartialEq + PartialOrd + Copy + bytemuck::Pod {
|
||||
/// This is to be able to derive [`bytemuck::Pod`] for [`Point<T>`], which is required for safe de/serialization.
|
||||
///
|
||||
/// Since we need ['Point<T>`] to be repr(packed), this padding must be picked to be the next multiple of the largest
|
||||
/// field in the struct, which fits the entire struct.
|
||||
type PointPadding: bytemuck::Pod
|
||||
+ Debug
|
||||
+ Default
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ Serialize
|
||||
+ for<'de> serde::Deserialize<'de>;
|
||||
|
||||
fn min_value() -> Self;
|
||||
fn max_value() -> Self;
|
||||
fn to_f64(self) -> f64;
|
||||
fn from_f64(x: f64) -> Self;
|
||||
fn from_u128(x: u128) -> Self;
|
||||
fn min(self, b: Self) -> Self {
|
||||
if self < b { self } else { b }
|
||||
}
|
||||
fn max(self, b: Self) -> Self {
|
||||
if self > b { self } else { b }
|
||||
}
|
||||
fn abs_diff(self, b: Self) -> Self {
|
||||
if self > b { self - b } else { b - self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Numericable for i64 {
|
||||
type PointPadding = [u8; derive_point_padding::<Self>()];
|
||||
|
||||
fn min_value() -> Self {
|
||||
i64::MIN
|
||||
}
|
||||
fn max_value() -> Self {
|
||||
i64::MAX
|
||||
}
|
||||
fn to_f64(self) -> f64 {
|
||||
self as f64
|
||||
}
|
||||
fn from_f64(x: f64) -> Self {
|
||||
x as Self
|
||||
}
|
||||
fn from_u128(x: u128) -> Self {
|
||||
x as i64
|
||||
}
|
||||
fn abs_diff(self, b: Self) -> Self {
|
||||
i64::abs_diff(self, b) as i64
|
||||
}
|
||||
}
|
||||
|
||||
impl Numericable for f64 {
|
||||
type PointPadding = [u8; derive_point_padding::<Self>()];
|
||||
|
||||
fn min_value() -> Self {
|
||||
f64::MIN
|
||||
}
|
||||
fn max_value() -> Self {
|
||||
f64::MAX
|
||||
}
|
||||
fn to_f64(self) -> f64 {
|
||||
self
|
||||
}
|
||||
fn from_f64(x: f64) -> Self {
|
||||
x
|
||||
}
|
||||
fn from_u128(x: u128) -> Self {
|
||||
x as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Numericable for u128 {
|
||||
type PointPadding = [u8; derive_point_padding::<Self>()];
|
||||
|
||||
fn min_value() -> Self {
|
||||
u128::MIN
|
||||
}
|
||||
|
||||
fn max_value() -> Self {
|
||||
u128::MAX
|
||||
}
|
||||
|
||||
fn to_f64(self) -> f64 {
|
||||
self as f64
|
||||
}
|
||||
|
||||
fn from_f64(x: f64) -> Self {
|
||||
x as u128
|
||||
}
|
||||
|
||||
fn from_u128(x: u128) -> Self {
|
||||
x
|
||||
}
|
||||
|
||||
fn abs_diff(self, b: Self) -> Self {
|
||||
u128::abs_diff(self, b)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,8 @@ use std::collections::Bound::Included;
|
||||
use common::types::PointOffsetType;
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
use crate::index::field_index::histogram::{Histogram, Point};
|
||||
use crate::index::field_index::histogram::Histogram;
|
||||
use crate::index::field_index::numeric_point::Point;
|
||||
use crate::index::field_index::tests::histogram_test_utils::print_results;
|
||||
use crate::index::field_index::tests::histogram_tests::{build_histogram, count_range};
|
||||
|
||||
@@ -191,10 +192,7 @@ fn test_build_i64_histogram() {
|
||||
|
||||
// let points = (0..100000).map(|i| Point { val: rnd.random_range(-10.0..10.0), idx: i }).collect_vec();
|
||||
let points: Vec<_> = (0..num_samples)
|
||||
.map(|i| Point {
|
||||
val: rand::random::<i64>(),
|
||||
idx: i,
|
||||
})
|
||||
.map(|i| Point::new(rand::random::<i64>(), i))
|
||||
.collect();
|
||||
|
||||
let (histogram, points_index) = build_histogram(max_bucket_size, precision, points);
|
||||
|
||||
@@ -6,7 +6,8 @@ use std::io::Write;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::index::field_index::histogram::{Histogram, Numericable, Point};
|
||||
use crate::index::field_index::histogram::Histogram;
|
||||
use crate::index::field_index::numeric_point::{Numericable, Point};
|
||||
|
||||
pub fn print_results<T: Numericable + Serialize + DeserializeOwned + Display>(
|
||||
points_index: &BTreeSet<Point<T>>,
|
||||
|
||||
@@ -9,13 +9,21 @@ use rand_distr::StandardNormal;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::index::field_index::histogram::{Histogram, Numericable, Point};
|
||||
use crate::index::field_index::histogram::Histogram;
|
||||
use crate::index::field_index::numeric_point::{Numericable, Point};
|
||||
use crate::index::field_index::tests::histogram_test_utils::print_results;
|
||||
|
||||
pub fn count_range<T: PartialOrd>(points_index: &BTreeSet<Point<T>>, a: T, b: T) -> usize {
|
||||
pub fn count_range<T: PartialOrd + Numericable>(
|
||||
points_index: &BTreeSet<Point<T>>,
|
||||
a: T,
|
||||
b: T,
|
||||
) -> usize {
|
||||
points_index
|
||||
.iter()
|
||||
.filter(|x| a <= x.val && x.val <= b)
|
||||
.filter(|x| {
|
||||
let v = x.val;
|
||||
a <= v && v <= b
|
||||
})
|
||||
.count()
|
||||
}
|
||||
|
||||
@@ -28,9 +36,11 @@ fn test_build_histogram_small() {
|
||||
|
||||
// let points = (0..100000).map(|i| Point { val: rnd.random_range(-10.0..10.0), idx: i }).collect_vec();
|
||||
let points = (0..num_samples)
|
||||
.map(|i| Point {
|
||||
val: f64::round(rnd.sample::<f64, _>(StandardNormal) * 10.0),
|
||||
idx: i % num_samples / 2,
|
||||
.map(|i| {
|
||||
Point::new(
|
||||
f64::round(rnd.sample::<f64, _>(StandardNormal) * 10.0),
|
||||
i % num_samples / 2,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
@@ -39,10 +49,10 @@ fn test_build_histogram_small() {
|
||||
let mut histogram = Histogram::new(max_bucket_size, precision);
|
||||
|
||||
for point in &points {
|
||||
points_index.insert(point.clone());
|
||||
points_index.insert(*point);
|
||||
// print_results(&points_index, &histogram, Some(point.clone()));
|
||||
histogram.insert(
|
||||
point.clone(),
|
||||
*point,
|
||||
|x| {
|
||||
points_index
|
||||
.range((Unbounded, Excluded(x)))
|
||||
@@ -54,7 +64,7 @@ fn test_build_histogram_small() {
|
||||
}
|
||||
|
||||
for point in &points {
|
||||
print_results(&points_index, &histogram, Some(point.clone()));
|
||||
print_results(&points_index, &histogram, Some(*point));
|
||||
points_index.remove(point);
|
||||
histogram.remove(
|
||||
point,
|
||||
@@ -193,7 +203,7 @@ pub fn build_histogram<T: Numericable + Serialize + DeserializeOwned + std::fmt:
|
||||
|
||||
let read_counter = Cell::new(0);
|
||||
for point in points {
|
||||
points_index.insert(point.clone());
|
||||
points_index.insert(point);
|
||||
// print_results(&points_index, &histogram, Some(point.clone()));
|
||||
histogram.insert(
|
||||
point,
|
||||
@@ -226,10 +236,8 @@ fn test_build_histogram_round() {
|
||||
let mut rnd = StdRng::seed_from_u64(42);
|
||||
|
||||
// let points = (0..100000).map(|i| Point { val: rnd.random_range(-10.0..10.0), idx: i }).collect_vec();
|
||||
let points = (0..num_samples).map(|i| Point {
|
||||
val: f64::round(rnd.sample::<f64, _>(StandardNormal) * 100.0),
|
||||
idx: i,
|
||||
});
|
||||
let points = (0..num_samples)
|
||||
.map(|i| Point::new(f64::round(rnd.sample::<f64, _>(StandardNormal) * 100.0), i));
|
||||
let (histogram, points_index) = build_histogram(max_bucket_size, precision, points.collect());
|
||||
|
||||
request_histogram(&histogram, &points_index);
|
||||
@@ -244,10 +252,7 @@ fn test_build_histogram() {
|
||||
|
||||
// let points = (0..100000).map(|i| Point { val: rnd.random_range(-10.0..10.0), idx: i }).collect_vec();
|
||||
let points = (0..num_samples)
|
||||
.map(|i| Point {
|
||||
val: rnd.sample(StandardNormal),
|
||||
idx: i,
|
||||
})
|
||||
.map(|i| Point::new(rnd.sample(StandardNormal), i))
|
||||
.collect_vec();
|
||||
|
||||
let (histogram, points_index) = build_histogram(max_bucket_size, precision, points);
|
||||
@@ -263,10 +268,7 @@ fn test_save_load_histogram() {
|
||||
let mut rnd = StdRng::seed_from_u64(42);
|
||||
|
||||
let points = (0..num_samples)
|
||||
.map(|i| Point {
|
||||
val: rnd.random_range(-10.0..10.0),
|
||||
idx: i,
|
||||
})
|
||||
.map(|i| Point::new(rnd.random_range(-10.0..10.0), i))
|
||||
.collect_vec();
|
||||
let (histogram, _) = build_histogram(max_bucket_size, precision, points);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user