From 0274c982d57b9f0ab4c76832fe42d4c2fcc1d663 Mon Sep 17 00:00:00 2001 From: Paul Masurel Date: Fri, 17 Feb 2023 21:57:14 +0900 Subject: [PATCH] Refactoring. (#1881) `ColumnValues` wrongly located in column_values/column.rs due to historical reason moves to column_values/mod.rs u128 stuff gets its own directory like u64 stuff. --- columnar/src/column/serialize.rs | 10 +- .../src/column_index/multivalued_index.rs | 10 +- columnar/src/column_values/bench.rs | 135 ++++++ columnar/src/column_values/column.rs | 331 --------------- columnar/src/column_values/merge.rs | 41 ++ columnar/src/column_values/mod.rs | 386 ++++++++---------- .../src/column_values/monotonic_column.rs | 123 ++++++ .../compact_space/blank_range.rs | 0 .../compact_space/build_compact_space.rs | 0 .../{ => u128_based}/compact_space/mod.rs | 10 +- .../{serialize.rs => u128_based/mod.rs} | 61 ++- columnar/src/column_values/vec_column.rs | 52 +++ columnar/src/columnar/merge_index.rs | 1 - columnar/src/columnar/mod.rs | 1 - 14 files changed, 586 insertions(+), 575 deletions(-) create mode 100644 columnar/src/column_values/bench.rs delete mode 100644 columnar/src/column_values/column.rs create mode 100644 columnar/src/column_values/merge.rs create mode 100644 columnar/src/column_values/monotonic_column.rs rename columnar/src/column_values/{ => u128_based}/compact_space/blank_range.rs (100%) rename columnar/src/column_values/{ => u128_based}/compact_space/build_compact_space.rs (100%) rename columnar/src/column_values/{ => u128_based}/compact_space/mod.rs (99%) rename columnar/src/column_values/{serialize.rs => u128_based/mod.rs} (63%) create mode 100644 columnar/src/column_values/vec_column.rs delete mode 100644 columnar/src/columnar/merge_index.rs diff --git a/columnar/src/column/serialize.rs b/columnar/src/column/serialize.rs index 20ce2e83a..243876c4a 100644 --- a/columnar/src/column/serialize.rs +++ b/columnar/src/column/serialize.rs @@ -7,9 +7,10 @@ use sstable::Dictionary; use crate::column::{BytesColumn, Column}; use crate::column_index::{serialize_column_index, SerializableColumnIndex}; -use crate::column_values::serialize::serialize_column_values_u128; -use crate::column_values::u64_based::{serialize_u64_based_column_values, CodecType}; -use crate::column_values::{MonotonicallyMappableToU128, MonotonicallyMappableToU64}; +use crate::column_values::{ + load_u64_based_column_values, serialize_column_values_u128, serialize_u64_based_column_values, + CodecType, MonotonicallyMappableToU128, MonotonicallyMappableToU64, +}; use crate::iterable::Iterable; use crate::StrColumn; @@ -49,8 +50,7 @@ pub fn open_column_u64(bytes: OwnedBytes) -> io:: ); let (column_index_data, column_values_data) = body.split(column_index_num_bytes as usize); let column_index = crate::column_index::open_column_index(column_index_data)?; - let column_values = - crate::column_values::u64_based::load_u64_based_column_values(column_values_data)?; + let column_values = load_u64_based_column_values(column_values_data)?; Ok(Column { idx: column_index, values: column_values, diff --git a/columnar/src/column_index/multivalued_index.rs b/columnar/src/column_index/multivalued_index.rs index d68b95d40..06284a4ca 100644 --- a/columnar/src/column_index/multivalued_index.rs +++ b/columnar/src/column_index/multivalued_index.rs @@ -5,8 +5,9 @@ use std::sync::Arc; use common::OwnedBytes; -use crate::column_values::u64_based::CodecType; -use crate::column_values::ColumnValues; +use crate::column_values::{ + load_u64_based_column_values, serialize_u64_based_column_values, CodecType, ColumnValues, +}; use crate::iterable::Iterable; use crate::{DocId, RowId}; @@ -14,7 +15,7 @@ pub fn serialize_multivalued_index( multivalued_index: &dyn Iterable, output: &mut impl Write, ) -> io::Result<()> { - crate::column_values::u64_based::serialize_u64_based_column_values( + serialize_u64_based_column_values( multivalued_index, &[CodecType::Bitpacked, CodecType::Linear], output, @@ -23,8 +24,7 @@ pub fn serialize_multivalued_index( } pub fn open_multivalued_index(bytes: OwnedBytes) -> io::Result { - let start_index_column: Arc> = - crate::column_values::u64_based::load_u64_based_column_values(bytes)?; + let start_index_column: Arc> = load_u64_based_column_values(bytes)?; Ok(MultiValueIndex { start_index_column }) } diff --git a/columnar/src/column_values/bench.rs b/columnar/src/column_values/bench.rs new file mode 100644 index 000000000..20b0fa7e5 --- /dev/null +++ b/columnar/src/column_values/bench.rs @@ -0,0 +1,135 @@ +use std::sync::Arc; + +use common::OwnedBytes; +use rand::rngs::StdRng; +use rand::{Rng, SeedableRng}; +use test::{self, Bencher}; + +use super::*; +use crate::column_values::u64_based::*; + +fn get_data() -> Vec { + let mut rng = StdRng::seed_from_u64(2u64); + let mut data: Vec<_> = (100..55000_u64) + .map(|num| num + rng.gen::() as u64) + .collect(); + data.push(99_000); + data.insert(1000, 2000); + data.insert(2000, 100); + data.insert(3000, 4100); + data.insert(4000, 100); + data.insert(5000, 800); + data +} + +fn compute_stats(vals: impl Iterator) -> ColumnStats { + let mut stats_collector = StatsCollector::default(); + for val in vals { + stats_collector.collect(val); + } + stats_collector.stats() +} + +#[inline(never)] +fn value_iter() -> impl Iterator { + 0..20_000 +} +fn get_reader_for_bench(data: &[u64]) -> Codec::ColumnValues { + let mut bytes = Vec::new(); + let stats = compute_stats(data.iter().cloned()); + let mut codec_serializer = Codec::estimator(); + for val in data { + codec_serializer.collect(*val); + } + codec_serializer.serialize(&stats, Box::new(data.iter().copied()).as_mut(), &mut bytes); + + Codec::load(OwnedBytes::new(bytes)).unwrap() +} +fn bench_get(b: &mut Bencher, data: &[u64]) { + let col = get_reader_for_bench::(data); + b.iter(|| { + let mut sum = 0u64; + for pos in value_iter() { + let val = col.get_val(pos as u32); + sum = sum.wrapping_add(val); + } + sum + }); +} + +#[inline(never)] +fn bench_get_dynamic_helper(b: &mut Bencher, col: Arc) { + b.iter(|| { + let mut sum = 0u64; + for pos in value_iter() { + let val = col.get_val(pos as u32); + sum = sum.wrapping_add(val); + } + sum + }); +} + +fn bench_get_dynamic(b: &mut Bencher, data: &[u64]) { + let col = Arc::new(get_reader_for_bench::(data)); + bench_get_dynamic_helper(b, col); +} +fn bench_create(b: &mut Bencher, data: &[u64]) { + let stats = compute_stats(data.iter().cloned()); + + let mut bytes = Vec::new(); + b.iter(|| { + bytes.clear(); + let mut codec_serializer = Codec::estimator(); + for val in data.iter().take(1024) { + codec_serializer.collect(*val); + } + + codec_serializer.serialize(&stats, Box::new(data.iter().copied()).as_mut(), &mut bytes) + }); +} + +#[bench] +fn bench_fastfield_bitpack_create(b: &mut Bencher) { + let data: Vec<_> = get_data(); + bench_create::(b, &data); +} +#[bench] +fn bench_fastfield_linearinterpol_create(b: &mut Bencher) { + let data: Vec<_> = get_data(); + bench_create::(b, &data); +} +#[bench] +fn bench_fastfield_multilinearinterpol_create(b: &mut Bencher) { + let data: Vec<_> = get_data(); + bench_create::(b, &data); +} +#[bench] +fn bench_fastfield_bitpack_get(b: &mut Bencher) { + let data: Vec<_> = get_data(); + bench_get::(b, &data); +} +#[bench] +fn bench_fastfield_bitpack_get_dynamic(b: &mut Bencher) { + let data: Vec<_> = get_data(); + bench_get_dynamic::(b, &data); +} +#[bench] +fn bench_fastfield_linearinterpol_get(b: &mut Bencher) { + let data: Vec<_> = get_data(); + bench_get::(b, &data); +} +#[bench] +fn bench_fastfield_linearinterpol_get_dynamic(b: &mut Bencher) { + let data: Vec<_> = get_data(); + bench_get_dynamic::(b, &data); +} +#[bench] +fn bench_fastfield_multilinearinterpol_get(b: &mut Bencher) { + let data: Vec<_> = get_data(); + bench_get::(b, &data); +} +#[bench] +fn bench_fastfield_multilinearinterpol_get_dynamic(b: &mut Bencher) { + let data: Vec<_> = get_data(); + bench_get_dynamic::(b, &data); +} diff --git a/columnar/src/column_values/column.rs b/columnar/src/column_values/column.rs deleted file mode 100644 index cbc4dce29..000000000 --- a/columnar/src/column_values/column.rs +++ /dev/null @@ -1,331 +0,0 @@ -use std::fmt::Debug; -use std::marker::PhantomData; -use std::ops::{Range, RangeInclusive}; -use std::sync::Arc; - -use tantivy_bitpacker::minmax; - -use crate::column_values::monotonic_mapping::StrictlyMonotonicFn; -use crate::RowId; - -/// `ColumnValues` provides access to a dense field column. -/// -/// `Column` are just a wrapper over `ColumnValues` and a `ColumnIndex`. -/// -/// Any methods with a default and specialized implementation need to be called in the -/// wrappers that implement the trait: Arc and MonotonicMappingColumn -pub trait ColumnValues: Send + Sync { - /// Return the value associated with the given idx. - /// - /// This accessor should return as fast as possible. - /// - /// # Panics - /// - /// May panic if `idx` is greater than the column length. - fn get_val(&self, idx: u32) -> T; - - /// Fills an output buffer with the fast field values - /// associated with the `DocId` going from - /// `start` to `start + output.len()`. - /// - /// # Panics - /// - /// Must panic if `start + output.len()` is greater than - /// the segment's `maxdoc`. - #[inline(always)] - fn get_range(&self, start: u64, output: &mut [T]) { - for (out, idx) in output.iter_mut().zip(start..) { - *out = self.get_val(idx as u32); - } - } - - /// Get the row ids of values which are in the provided value range. - /// - /// Note that position == docid for single value fast fields - #[inline(always)] - fn get_row_ids_for_value_range( - &self, - value_range: RangeInclusive, - row_id_range: Range, - row_id_hits: &mut Vec, - ) { - let row_id_range = row_id_range.start..row_id_range.end.min(self.num_vals()); - for idx in row_id_range.start..row_id_range.end { - let val = self.get_val(idx); - if value_range.contains(&val) { - row_id_hits.push(idx); - } - } - } - - /// Returns the minimum value for this fast field. - /// - /// This min_value may not be exact. - /// For instance, the min value does not take in account of possible - /// deleted document. All values are however guaranteed to be higher than - /// `.min_value()`. - fn min_value(&self) -> T; - - /// Returns the maximum value for this fast field. - /// - /// This max_value may not be exact. - /// For instance, the max value does not take in account of possible - /// deleted document. All values are however guaranteed to be higher than - /// `.max_value()`. - fn max_value(&self) -> T; - - /// The number of values in the column. - fn num_vals(&self) -> u32; - - /// Returns a iterator over the data - fn iter<'a>(&'a self) -> Box + 'a> { - Box::new((0..self.num_vals()).map(|idx| self.get_val(idx))) - } -} - -impl ColumnValues for Arc> { - #[inline(always)] - fn get_val(&self, idx: u32) -> T { - self.as_ref().get_val(idx) - } - - #[inline(always)] - fn min_value(&self) -> T { - self.as_ref().min_value() - } - - #[inline(always)] - fn max_value(&self) -> T { - self.as_ref().max_value() - } - - #[inline(always)] - fn num_vals(&self) -> u32 { - self.as_ref().num_vals() - } - - #[inline(always)] - fn iter<'b>(&'b self) -> Box + 'b> { - self.as_ref().iter() - } - - #[inline(always)] - fn get_range(&self, start: u64, output: &mut [T]) { - self.as_ref().get_range(start, output) - } - - #[inline(always)] - fn get_row_ids_for_value_range( - &self, - range: RangeInclusive, - doc_id_range: Range, - positions: &mut Vec, - ) { - self.as_ref() - .get_row_ids_for_value_range(range, doc_id_range, positions) - } -} - -/// VecColumn provides `Column` over a slice. -pub struct VecColumn<'a, T = u64> { - pub(crate) values: &'a [T], - pub(crate) min_value: T, - pub(crate) max_value: T, -} - -impl<'a, T: Copy + PartialOrd + Send + Sync + Debug> ColumnValues for VecColumn<'a, T> { - fn get_val(&self, position: u32) -> T { - self.values[position as usize] - } - - fn iter(&self) -> Box + '_> { - Box::new(self.values.iter().copied()) - } - - fn min_value(&self) -> T { - self.min_value - } - - fn max_value(&self) -> T { - self.max_value - } - - fn num_vals(&self) -> u32 { - self.values.len() as u32 - } - - fn get_range(&self, start: u64, output: &mut [T]) { - output.copy_from_slice(&self.values[start as usize..][..output.len()]) - } -} - -impl<'a, T: Copy + PartialOrd + Default, V> From<&'a V> for VecColumn<'a, T> -where V: AsRef<[T]> + ?Sized -{ - fn from(values: &'a V) -> Self { - let values = values.as_ref(); - let (min_value, max_value) = minmax(values.iter().copied()).unwrap_or_default(); - Self { - values, - min_value, - max_value, - } - } -} - -struct MonotonicMappingColumn { - from_column: C, - monotonic_mapping: T, - _phantom: PhantomData, -} - -/// Creates a view of a column transformed by a strictly monotonic mapping. See -/// [`StrictlyMonotonicFn`]. -/// -/// E.g. apply a gcd monotonic_mapping([100, 200, 300]) == [1, 2, 3] -/// monotonic_mapping.mapping() is expected to be injective, and we should always have -/// monotonic_mapping.inverse(monotonic_mapping.mapping(el)) == el -/// -/// The inverse of the mapping is required for: -/// `fn get_positions_for_value_range(&self, range: RangeInclusive) -> Vec ` -/// The user provides the original value range and we need to monotonic map them in the same way the -/// serialization does before calling the underlying column. -/// -/// Note that when opening a codec, the monotonic_mapping should be the inverse of the mapping -/// during serialization. And therefore the monotonic_mapping_inv when opening is the same as -/// monotonic_mapping during serialization. -pub fn monotonic_map_column( - from_column: C, - monotonic_mapping: T, -) -> impl ColumnValues -where - C: ColumnValues, - T: StrictlyMonotonicFn + Send + Sync, - Input: PartialOrd + Debug + Send + Sync + Clone, - Output: PartialOrd + Debug + Send + Sync + Clone, -{ - MonotonicMappingColumn { - from_column, - monotonic_mapping, - _phantom: PhantomData, - } -} - -impl ColumnValues for MonotonicMappingColumn -where - C: ColumnValues, - T: StrictlyMonotonicFn + Send + Sync, - Input: PartialOrd + Send + Debug + Sync + Clone, - Output: PartialOrd + Send + Debug + Sync + Clone, -{ - #[inline] - fn get_val(&self, idx: u32) -> Output { - let from_val = self.from_column.get_val(idx); - self.monotonic_mapping.mapping(from_val) - } - - fn min_value(&self) -> Output { - let from_min_value = self.from_column.min_value(); - self.monotonic_mapping.mapping(from_min_value) - } - - fn max_value(&self) -> Output { - let from_max_value = self.from_column.max_value(); - self.monotonic_mapping.mapping(from_max_value) - } - - fn num_vals(&self) -> u32 { - self.from_column.num_vals() - } - - fn iter(&self) -> Box + '_> { - Box::new( - self.from_column - .iter() - .map(|el| self.monotonic_mapping.mapping(el)), - ) - } - - fn get_row_ids_for_value_range( - &self, - range: RangeInclusive, - doc_id_range: Range, - positions: &mut Vec, - ) { - self.from_column.get_row_ids_for_value_range( - self.monotonic_mapping.inverse(range.start().clone()) - ..=self.monotonic_mapping.inverse(range.end().clone()), - doc_id_range, - positions, - ) - } - - // We voluntarily do not implement get_range as it yields a regression, - // and we do not have any specialized implementation anyway. -} - -/// Wraps an iterator into a `Column`. -pub struct IterColumn(T); - -impl From for IterColumn -where T: Iterator + Clone + ExactSizeIterator -{ - fn from(iter: T) -> Self { - IterColumn(iter) - } -} - -impl ColumnValues for IterColumn -where - T: Iterator + Clone + ExactSizeIterator + Send + Sync, - T::Item: PartialOrd + Debug, -{ - fn get_val(&self, idx: u32) -> T::Item { - self.0.clone().nth(idx as usize).unwrap() - } - - fn min_value(&self) -> T::Item { - self.0.clone().next().unwrap() - } - - fn max_value(&self) -> T::Item { - self.0.clone().last().unwrap() - } - - fn num_vals(&self) -> u32 { - self.0.len() as u32 - } - - fn iter(&self) -> Box + '_> { - Box::new(self.0.clone()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::column_values::monotonic_mapping::{ - StrictlyMonotonicMappingInverter, StrictlyMonotonicMappingToInternal, - }; - - #[test] - fn test_range_as_col() { - let col = IterColumn::from(10..100); - assert_eq!(col.num_vals(), 90); - assert_eq!(col.max_value(), 99); - } - - #[test] - fn test_monotonic_mapping_iter() { - let vals: Vec = (0..100u64).map(|el| el * 10).collect(); - let col = VecColumn::from(&vals); - let mapped = monotonic_map_column( - col, - StrictlyMonotonicMappingInverter::from(StrictlyMonotonicMappingToInternal::::new()), - ); - let val_i64s: Vec = mapped.iter().collect(); - for i in 0..100 { - assert_eq!(val_i64s[i as usize], mapped.get_val(i)); - } - } -} diff --git a/columnar/src/column_values/merge.rs b/columnar/src/column_values/merge.rs new file mode 100644 index 000000000..d70b4295b --- /dev/null +++ b/columnar/src/column_values/merge.rs @@ -0,0 +1,41 @@ +use std::fmt::Debug; +use std::sync::Arc; + +use crate::iterable::Iterable; +use crate::{ColumnIndex, ColumnValues, MergeRowOrder}; + +pub(crate) struct MergedColumnValues<'a, T> { + pub(crate) column_indexes: &'a [Option], + pub(crate) column_values: &'a [Option>>], + pub(crate) merge_row_order: &'a MergeRowOrder, +} + +impl<'a, T: Copy + PartialOrd + Debug> Iterable for MergedColumnValues<'a, T> { + fn boxed_iter(&self) -> Box + '_> { + match self.merge_row_order { + MergeRowOrder::Stack(_) => Box::new( + self.column_values + .iter() + .flatten() + .flat_map(|column_value| column_value.iter()), + ), + MergeRowOrder::Shuffled(shuffle_merge_order) => Box::new( + shuffle_merge_order + .iter_new_to_old_row_addrs() + .flat_map(|row_addr| { + let column_index = + self.column_indexes[row_addr.segment_ord as usize].as_ref()?; + let column_values = + self.column_values[row_addr.segment_ord as usize].as_ref()?; + let value_range = column_index.value_row_ids(row_addr.row_id); + Some((value_range, column_values)) + }) + .flat_map(|(value_range, column_values)| { + value_range + .into_iter() + .map(|val| column_values.get_val(val)) + }), + ), + } + } +} diff --git a/columnar/src/column_values/mod.rs b/columnar/src/column_values/mod.rs index 5c67c437d..d761235c0 100644 --- a/columnar/src/column_values/mod.rs +++ b/columnar/src/column_values/mod.rs @@ -7,260 +7,200 @@ //! - Monotonically map values to u64/u128 use std::fmt::Debug; -use std::io; -use std::io::Write; +use std::ops::{Range, RangeInclusive}; use std::sync::Arc; -use common::{BinarySerializable, OwnedBytes}; -use compact_space::CompactSpaceDecompressor; pub use monotonic_mapping::{MonotonicallyMappableToU64, StrictlyMonotonicFn}; -use monotonic_mapping::{StrictlyMonotonicMappingInverter, StrictlyMonotonicMappingToInternal}; pub use monotonic_mapping_u128::MonotonicallyMappableToU128; -use serialize::U128Header; -mod compact_space; +mod merge; pub(crate) mod monotonic_mapping; pub(crate) mod monotonic_mapping_u128; mod stats; -pub(crate) mod u64_based; +mod u128_based; +mod u64_based; +mod vec_column; -mod column; -pub(crate) mod serialize; +mod monotonic_column; -pub use serialize::serialize_column_values_u128; +pub(crate) use merge::MergedColumnValues; pub use stats::ColumnStats; +pub use u128_based::{open_u128_mapped, serialize_column_values_u128}; pub use u64_based::{ load_u64_based_column_values, serialize_and_load_u64_based_column_values, serialize_u64_based_column_values, CodecType, ALL_U64_CODEC_TYPES, }; +pub use vec_column::VecColumn; -pub use self::column::{monotonic_map_column, ColumnValues, IterColumn, VecColumn}; -use crate::iterable::Iterable; -use crate::{ColumnIndex, MergeRowOrder}; +pub use self::monotonic_column::monotonic_map_column; +use crate::RowId; -pub(crate) struct MergedColumnValues<'a, T> { - pub(crate) column_indexes: &'a [Option], - pub(crate) column_values: &'a [Option>>], - pub(crate) merge_row_order: &'a MergeRowOrder, -} +/// `ColumnValues` provides access to a dense field column. +/// +/// `Column` are just a wrapper over `ColumnValues` and a `ColumnIndex`. +/// +/// Any methods with a default and specialized implementation need to be called in the +/// wrappers that implement the trait: Arc and MonotonicMappingColumn +pub trait ColumnValues: Send + Sync { + /// Return the value associated with the given idx. + /// + /// This accessor should return as fast as possible. + /// + /// # Panics + /// + /// May panic if `idx` is greater than the column length. + fn get_val(&self, idx: u32) -> T; -impl<'a, T: Copy + PartialOrd + Debug> Iterable for MergedColumnValues<'a, T> { - fn boxed_iter(&self) -> Box + '_> { - match self.merge_row_order { - MergeRowOrder::Stack(_) => { - Box::new(self - .column_values - .iter() - .flatten() - .flat_map(|column_value| column_value.iter())) - }, - MergeRowOrder::Shuffled(shuffle_merge_order) => { - Box::new(shuffle_merge_order - .iter_new_to_old_row_addrs() - .flat_map(|row_addr| { - let Some(column_index) = self.column_indexes[row_addr.segment_ord as usize].as_ref() else { - return None; - }; - let Some(column_values) = self.column_values[row_addr.segment_ord as usize].as_ref() else { - return None; - }; - let value_range = column_index.value_row_ids(row_addr.row_id); - Some((value_range, column_values)) - }) - .flat_map(|(value_range, column_values)| { - value_range - .into_iter() - .map(|val| column_values.get_val(val)) - }) - ) - }, + /// Fills an output buffer with the fast field values + /// associated with the `DocId` going from + /// `start` to `start + output.len()`. + /// + /// # Panics + /// + /// Must panic if `start + output.len()` is greater than + /// the segment's `maxdoc`. + #[inline(always)] + fn get_range(&self, start: u64, output: &mut [T]) { + for (out, idx) in output.iter_mut().zip(start..) { + *out = self.get_val(idx as u32); } } -} -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] -#[repr(u8)] -/// Available codecs to use to encode the u128 (via [`MonotonicallyMappableToU128`]) converted data. -pub enum U128FastFieldCodecType { - /// This codec takes a large number space (u128) and reduces it to a compact number space, by - /// removing the holes. - CompactSpace = 1, -} - -impl BinarySerializable for U128FastFieldCodecType { - fn serialize(&self, wrt: &mut W) -> io::Result<()> { - self.to_code().serialize(wrt) - } - - fn deserialize(reader: &mut R) -> io::Result { - let code = u8::deserialize(reader)?; - let codec_type: Self = Self::from_code(code) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Unknown code `{code}.`"))?; - Ok(codec_type) - } -} - -impl U128FastFieldCodecType { - pub(crate) fn to_code(self) -> u8 { - self as u8 - } - - pub(crate) fn from_code(code: u8) -> Option { - match code { - 1 => Some(Self::CompactSpace), - _ => None, + /// Get the row ids of values which are in the provided value range. + /// + /// Note that position == docid for single value fast fields + #[inline(always)] + fn get_row_ids_for_value_range( + &self, + value_range: RangeInclusive, + row_id_range: Range, + row_id_hits: &mut Vec, + ) { + let row_id_range = row_id_range.start..row_id_range.end.min(self.num_vals()); + for idx in row_id_range.start..row_id_range.end { + let val = self.get_val(idx); + if value_range.contains(&val) { + row_id_hits.push(idx); + } } } + + /// Returns the minimum value for this fast field. + /// + /// This min_value may not be exact. + /// For instance, the min value does not take in account of possible + /// deleted document. All values are however guaranteed to be higher than + /// `.min_value()`. + fn min_value(&self) -> T; + + /// Returns the maximum value for this fast field. + /// + /// This max_value may not be exact. + /// For instance, the max value does not take in account of possible + /// deleted document. All values are however guaranteed to be higher than + /// `.max_value()`. + fn max_value(&self) -> T; + + /// The number of values in the column. + fn num_vals(&self) -> u32; + + /// Returns a iterator over the data + fn iter<'a>(&'a self) -> Box + 'a> { + Box::new((0..self.num_vals()).map(|idx| self.get_val(idx))) + } } -/// Returns the correct codec reader wrapped in the `Arc` for the data. -pub fn open_u128_mapped( - mut bytes: OwnedBytes, -) -> io::Result>> { - let header = U128Header::deserialize(&mut bytes)?; - assert_eq!(header.codec_type, U128FastFieldCodecType::CompactSpace); - let reader = CompactSpaceDecompressor::open(bytes)?; +impl ColumnValues for Arc> { + #[inline(always)] + fn get_val(&self, idx: u32) -> T { + self.as_ref().get_val(idx) + } - let inverted: StrictlyMonotonicMappingInverter> = - StrictlyMonotonicMappingToInternal::::new().into(); - Ok(Arc::new(monotonic_map_column(reader, inverted))) + #[inline(always)] + fn min_value(&self) -> T { + self.as_ref().min_value() + } + + #[inline(always)] + fn max_value(&self) -> T { + self.as_ref().max_value() + } + + #[inline(always)] + fn num_vals(&self) -> u32 { + self.as_ref().num_vals() + } + + #[inline(always)] + fn iter<'b>(&'b self) -> Box + 'b> { + self.as_ref().iter() + } + + #[inline(always)] + fn get_range(&self, start: u64, output: &mut [T]) { + self.as_ref().get_range(start, output) + } + + #[inline(always)] + fn get_row_ids_for_value_range( + &self, + range: RangeInclusive, + doc_id_range: Range, + positions: &mut Vec, + ) { + self.as_ref() + .get_row_ids_for_value_range(range, doc_id_range, positions) + } +} + +/// Wraps an cloneable iterator into a `Column`. +pub struct IterColumn(T); + +impl From for IterColumn +where T: Iterator + Clone + ExactSizeIterator +{ + fn from(iter: T) -> Self { + IterColumn(iter) + } +} + +impl ColumnValues for IterColumn +where + T: Iterator + Clone + ExactSizeIterator + Send + Sync, + T::Item: PartialOrd + Debug, +{ + fn get_val(&self, idx: u32) -> T::Item { + self.0.clone().nth(idx as usize).unwrap() + } + + fn min_value(&self) -> T::Item { + self.0.clone().next().unwrap() + } + + fn max_value(&self) -> T::Item { + self.0.clone().last().unwrap() + } + + fn num_vals(&self) -> u32 { + self.0.len() as u32 + } + + fn iter(&self) -> Box + '_> { + Box::new(self.0.clone()) + } } #[cfg(all(test, feature = "unstable"))] -mod bench { - use std::sync::Arc; - - use common::OwnedBytes; - use rand::rngs::StdRng; - use rand::{Rng, SeedableRng}; - use test::{self, Bencher}; +mod bench; +#[cfg(test)] +mod tests { use super::*; - use crate::column_values::u64_based::*; - fn get_data() -> Vec { - let mut rng = StdRng::seed_from_u64(2u64); - let mut data: Vec<_> = (100..55000_u64) - .map(|num| num + rng.gen::() as u64) - .collect(); - data.push(99_000); - data.insert(1000, 2000); - data.insert(2000, 100); - data.insert(3000, 4100); - data.insert(4000, 100); - data.insert(5000, 800); - data - } - - fn compute_stats(vals: impl Iterator) -> ColumnStats { - let mut stats_collector = StatsCollector::default(); - for val in vals { - stats_collector.collect(val); - } - stats_collector.stats() - } - - #[inline(never)] - fn value_iter() -> impl Iterator { - 0..20_000 - } - fn get_reader_for_bench(data: &[u64]) -> Codec::ColumnValues { - let mut bytes = Vec::new(); - let stats = compute_stats(data.iter().cloned()); - let mut codec_serializer = Codec::estimator(); - for val in data { - codec_serializer.collect(*val); - } - codec_serializer.serialize(&stats, Box::new(data.iter().copied()).as_mut(), &mut bytes); - - Codec::load(OwnedBytes::new(bytes)).unwrap() - } - fn bench_get(b: &mut Bencher, data: &[u64]) { - let col = get_reader_for_bench::(data); - b.iter(|| { - let mut sum = 0u64; - for pos in value_iter() { - let val = col.get_val(pos as u32); - sum = sum.wrapping_add(val); - } - sum - }); - } - - #[inline(never)] - fn bench_get_dynamic_helper(b: &mut Bencher, col: Arc) { - b.iter(|| { - let mut sum = 0u64; - for pos in value_iter() { - let val = col.get_val(pos as u32); - sum = sum.wrapping_add(val); - } - sum - }); - } - - fn bench_get_dynamic(b: &mut Bencher, data: &[u64]) { - let col = Arc::new(get_reader_for_bench::(data)); - bench_get_dynamic_helper(b, col); - } - fn bench_create(b: &mut Bencher, data: &[u64]) { - let stats = compute_stats(data.iter().cloned()); - - let mut bytes = Vec::new(); - b.iter(|| { - bytes.clear(); - let mut codec_serializer = Codec::estimator(); - for val in data.iter().take(1024) { - codec_serializer.collect(*val); - } - - codec_serializer.serialize(&stats, Box::new(data.iter().copied()).as_mut(), &mut bytes) - }); - } - - #[bench] - fn bench_fastfield_bitpack_create(b: &mut Bencher) { - let data: Vec<_> = get_data(); - bench_create::(b, &data); - } - #[bench] - fn bench_fastfield_linearinterpol_create(b: &mut Bencher) { - let data: Vec<_> = get_data(); - bench_create::(b, &data); - } - #[bench] - fn bench_fastfield_multilinearinterpol_create(b: &mut Bencher) { - let data: Vec<_> = get_data(); - bench_create::(b, &data); - } - #[bench] - fn bench_fastfield_bitpack_get(b: &mut Bencher) { - let data: Vec<_> = get_data(); - bench_get::(b, &data); - } - #[bench] - fn bench_fastfield_bitpack_get_dynamic(b: &mut Bencher) { - let data: Vec<_> = get_data(); - bench_get_dynamic::(b, &data); - } - #[bench] - fn bench_fastfield_linearinterpol_get(b: &mut Bencher) { - let data: Vec<_> = get_data(); - bench_get::(b, &data); - } - #[bench] - fn bench_fastfield_linearinterpol_get_dynamic(b: &mut Bencher) { - let data: Vec<_> = get_data(); - bench_get_dynamic::(b, &data); - } - #[bench] - fn bench_fastfield_multilinearinterpol_get(b: &mut Bencher) { - let data: Vec<_> = get_data(); - bench_get::(b, &data); - } - #[bench] - fn bench_fastfield_multilinearinterpol_get_dynamic(b: &mut Bencher) { - let data: Vec<_> = get_data(); - bench_get_dynamic::(b, &data); + #[test] + fn test_range_as_col() { + let col = IterColumn::from(10..100); + assert_eq!(col.num_vals(), 90); + assert_eq!(col.max_value(), 99); } } diff --git a/columnar/src/column_values/monotonic_column.rs b/columnar/src/column_values/monotonic_column.rs new file mode 100644 index 000000000..65c8f11b7 --- /dev/null +++ b/columnar/src/column_values/monotonic_column.rs @@ -0,0 +1,123 @@ +use std::fmt::Debug; +use std::marker::PhantomData; +use std::ops::{Range, RangeInclusive}; + +use crate::column_values::monotonic_mapping::StrictlyMonotonicFn; +use crate::ColumnValues; + +struct MonotonicMappingColumn { + from_column: C, + monotonic_mapping: T, + _phantom: PhantomData, +} + +/// Creates a view of a column transformed by a strictly monotonic mapping. See +/// [`StrictlyMonotonicFn`]. +/// +/// E.g. apply a gcd monotonic_mapping([100, 200, 300]) == [1, 2, 3] +/// monotonic_mapping.mapping() is expected to be injective, and we should always have +/// monotonic_mapping.inverse(monotonic_mapping.mapping(el)) == el +/// +/// The inverse of the mapping is required for: +/// `fn get_positions_for_value_range(&self, range: RangeInclusive) -> Vec ` +/// The user provides the original value range and we need to monotonic map them in the same way the +/// serialization does before calling the underlying column. +/// +/// Note that when opening a codec, the monotonic_mapping should be the inverse of the mapping +/// during serialization. And therefore the monotonic_mapping_inv when opening is the same as +/// monotonic_mapping during serialization. +pub fn monotonic_map_column( + from_column: C, + monotonic_mapping: T, +) -> impl ColumnValues +where + C: ColumnValues, + T: StrictlyMonotonicFn + Send + Sync, + Input: PartialOrd + Debug + Send + Sync + Clone, + Output: PartialOrd + Debug + Send + Sync + Clone, +{ + MonotonicMappingColumn { + from_column, + monotonic_mapping, + _phantom: PhantomData, + } +} + +impl ColumnValues for MonotonicMappingColumn +where + C: ColumnValues, + T: StrictlyMonotonicFn + Send + Sync, + Input: PartialOrd + Send + Debug + Sync + Clone, + Output: PartialOrd + Send + Debug + Sync + Clone, +{ + #[inline] + fn get_val(&self, idx: u32) -> Output { + let from_val = self.from_column.get_val(idx); + self.monotonic_mapping.mapping(from_val) + } + + fn min_value(&self) -> Output { + let from_min_value = self.from_column.min_value(); + self.monotonic_mapping.mapping(from_min_value) + } + + fn max_value(&self) -> Output { + let from_max_value = self.from_column.max_value(); + self.monotonic_mapping.mapping(from_max_value) + } + + fn num_vals(&self) -> u32 { + self.from_column.num_vals() + } + + fn iter(&self) -> Box + '_> { + Box::new( + self.from_column + .iter() + .map(|el| self.monotonic_mapping.mapping(el)), + ) + } + + fn get_row_ids_for_value_range( + &self, + range: RangeInclusive, + doc_id_range: Range, + positions: &mut Vec, + ) { + self.from_column.get_row_ids_for_value_range( + self.monotonic_mapping.inverse(range.start().clone()) + ..=self.monotonic_mapping.inverse(range.end().clone()), + doc_id_range, + positions, + ) + } + + // We voluntarily do not implement get_range as it yields a regression, + // and we do not have any specialized implementation anyway. +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::column_values::monotonic_mapping::{ + StrictlyMonotonicMappingInverter, + StrictlyMonotonicMappingToInternal, + }; + use crate::column_values::VecColumn; + + #[test] + fn test_monotonic_mapping_iter() { + let vals: Vec = (0..100u64).map(|el| el * 10).collect(); + let col = VecColumn::from(&vals); + let mapped = monotonic_map_column( + col, + StrictlyMonotonicMappingInverter::from( + StrictlyMonotonicMappingToInternal::::new(), + ), + ); + let val_i64s: Vec = mapped.iter().collect(); + for i in 0..100 { + assert_eq!(val_i64s[i as usize], mapped.get_val(i)); + } + } +} diff --git a/columnar/src/column_values/compact_space/blank_range.rs b/columnar/src/column_values/u128_based/compact_space/blank_range.rs similarity index 100% rename from columnar/src/column_values/compact_space/blank_range.rs rename to columnar/src/column_values/u128_based/compact_space/blank_range.rs diff --git a/columnar/src/column_values/compact_space/build_compact_space.rs b/columnar/src/column_values/u128_based/compact_space/build_compact_space.rs similarity index 100% rename from columnar/src/column_values/compact_space/build_compact_space.rs rename to columnar/src/column_values/u128_based/compact_space/build_compact_space.rs diff --git a/columnar/src/column_values/compact_space/mod.rs b/columnar/src/column_values/u128_based/compact_space/mod.rs similarity index 99% rename from columnar/src/column_values/compact_space/mod.rs rename to columnar/src/column_values/u128_based/compact_space/mod.rs index c8f460d7d..a32b6f5b8 100644 --- a/columnar/src/column_values/compact_space/mod.rs +++ b/columnar/src/column_values/u128_based/compact_space/mod.rs @@ -17,16 +17,16 @@ use std::{ ops::{Range, RangeInclusive}, }; +mod blank_range; +mod build_compact_space; + +use build_compact_space::get_compact_space; use common::{BinarySerializable, CountingWriter, OwnedBytes, VInt, VIntU128}; use tantivy_bitpacker::{self, BitPacker, BitUnpacker}; -use crate::column_values::compact_space::build_compact_space::get_compact_space; use crate::column_values::ColumnValues; use crate::RowId; -mod blank_range; -mod build_compact_space; - /// The cost per blank is quite hard actually, since blanks are delta encoded, the actual cost of /// blanks depends on the number of blanks. /// @@ -464,7 +464,7 @@ mod tests { use itertools::Itertools; use super::*; - use crate::column_values::serialize::U128Header; + use crate::column_values::u128_based::U128Header; use crate::column_values::{open_u128_mapped, serialize_column_values_u128}; #[test] diff --git a/columnar/src/column_values/serialize.rs b/columnar/src/column_values/u128_based/mod.rs similarity index 63% rename from columnar/src/column_values/serialize.rs rename to columnar/src/column_values/u128_based/mod.rs index bbc5ba66c..0cae841c5 100644 --- a/columnar/src/column_values/serialize.rs +++ b/columnar/src/column_values/u128_based/mod.rs @@ -1,12 +1,19 @@ use std::fmt::Debug; use std::io; +use std::io::Write; +use std::sync::Arc; -use common::{BinarySerializable, VInt}; +mod compact_space; -use crate::column_values::compact_space::CompactSpaceCompressor; -use crate::column_values::U128FastFieldCodecType; +use common::{BinarySerializable, OwnedBytes, VInt}; +use compact_space::{CompactSpaceCompressor, CompactSpaceDecompressor}; + +use crate::column_values::monotonic_map_column; +use crate::column_values::monotonic_mapping::{ + StrictlyMonotonicMappingInverter, StrictlyMonotonicMappingToInternal, +}; use crate::iterable::Iterable; -use crate::MonotonicallyMappableToU128; +use crate::{ColumnValues, MonotonicallyMappableToU128}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) struct U128Header { @@ -55,6 +62,52 @@ pub fn serialize_column_values_u128( Ok(()) } +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] +#[repr(u8)] +/// Available codecs to use to encode the u128 (via [`MonotonicallyMappableToU128`]) converted data. +pub(crate) enum U128FastFieldCodecType { + /// This codec takes a large number space (u128) and reduces it to a compact number space, by + /// removing the holes. + CompactSpace = 1, +} + +impl BinarySerializable for U128FastFieldCodecType { + fn serialize(&self, wrt: &mut W) -> io::Result<()> { + self.to_code().serialize(wrt) + } + + fn deserialize(reader: &mut R) -> io::Result { + let code = u8::deserialize(reader)?; + let codec_type: Self = Self::from_code(code) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Unknown code `{code}.`"))?; + Ok(codec_type) + } +} + +impl U128FastFieldCodecType { + pub(crate) fn to_code(self) -> u8 { + self as u8 + } + + pub(crate) fn from_code(code: u8) -> Option { + match code { + 1 => Some(Self::CompactSpace), + _ => None, + } + } +} + +/// Returns the correct codec reader wrapped in the `Arc` for the data. +pub fn open_u128_mapped( + mut bytes: OwnedBytes, +) -> io::Result>> { + let header = U128Header::deserialize(&mut bytes)?; + assert_eq!(header.codec_type, U128FastFieldCodecType::CompactSpace); + let reader = CompactSpaceDecompressor::open(bytes)?; + let inverted: StrictlyMonotonicMappingInverter> = + StrictlyMonotonicMappingToInternal::::new().into(); + Ok(Arc::new(monotonic_map_column(reader, inverted))) +} #[cfg(test)] pub mod tests { use super::*; diff --git a/columnar/src/column_values/vec_column.rs b/columnar/src/column_values/vec_column.rs new file mode 100644 index 000000000..59f5d72ab --- /dev/null +++ b/columnar/src/column_values/vec_column.rs @@ -0,0 +1,52 @@ +use std::fmt::Debug; + +use tantivy_bitpacker::minmax; + +use crate::ColumnValues; + +/// VecColumn provides `Column` over a slice. +pub struct VecColumn<'a, T = u64> { + pub(crate) values: &'a [T], + pub(crate) min_value: T, + pub(crate) max_value: T, +} + +impl<'a, T: Copy + PartialOrd + Send + Sync + Debug> ColumnValues for VecColumn<'a, T> { + fn get_val(&self, position: u32) -> T { + self.values[position as usize] + } + + fn iter(&self) -> Box + '_> { + Box::new(self.values.iter().copied()) + } + + fn min_value(&self) -> T { + self.min_value + } + + fn max_value(&self) -> T { + self.max_value + } + + fn num_vals(&self) -> u32 { + self.values.len() as u32 + } + + fn get_range(&self, start: u64, output: &mut [T]) { + output.copy_from_slice(&self.values[start as usize..][..output.len()]) + } +} + +impl<'a, T: Copy + PartialOrd + Default, V> From<&'a V> for VecColumn<'a, T> +where V: AsRef<[T]> + ?Sized +{ + fn from(values: &'a V) -> Self { + let values = values.as_ref(); + let (min_value, max_value) = minmax(values.iter().copied()).unwrap_or_default(); + Self { + values, + min_value, + max_value, + } + } +} diff --git a/columnar/src/columnar/merge_index.rs b/columnar/src/columnar/merge_index.rs deleted file mode 100644 index 8b1378917..000000000 --- a/columnar/src/columnar/merge_index.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/columnar/src/columnar/mod.rs b/columnar/src/columnar/mod.rs index 05f1b4d53..3a2ca6af4 100644 --- a/columnar/src/columnar/mod.rs +++ b/columnar/src/columnar/mod.rs @@ -1,7 +1,6 @@ mod column_type; mod format_version; mod merge; -mod merge_index; mod reader; mod writer;