From 4e1dc0020c1fc9d8b764920676ade63bc6e8a51b Mon Sep 17 00:00:00 2001 From: Jorge Leitao Date: Fri, 10 Jun 2022 17:10:11 +0200 Subject: [PATCH] Added cow APIs (2x-10x vs non-cow) (#1061) --- Cargo.toml | 8 ++ benches/assign_ops.rs | 29 ++++ benches/bitmap_assign_ops.rs | 47 +++++++ src/array/boolean/mod.rs | 40 +++++- src/array/primitive/mod.rs | 35 ++++- src/bitmap/assign_ops.rs | 191 +++++++++++++++++++++++++++ src/bitmap/immutable.rs | 11 ++ src/bitmap/mod.rs | 3 + src/bitmap/mutable.rs | 11 +- src/bitmap/utils/chunks_exact_mut.rs | 60 +++++++++ src/bitmap/utils/mod.rs | 2 + src/buffer/immutable.rs | 11 ++ src/types/bit_chunk.rs | 2 + tests/it/array/boolean/mod.rs | 24 ++++ tests/it/array/primitive/mod.rs | 22 +++ tests/it/bitmap/assign_ops.rs | 42 ++++++ tests/it/bitmap/mod.rs | 1 + 17 files changed, 535 insertions(+), 4 deletions(-) create mode 100644 benches/assign_ops.rs create mode 100644 benches/bitmap_assign_ops.rs create mode 100644 src/bitmap/assign_ops.rs create mode 100644 src/bitmap/utils/chunks_exact_mut.rs create mode 100644 tests/it/bitmap/assign_ops.rs diff --git a/Cargo.toml b/Cargo.toml index 921facece59..17ddddab439 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -321,3 +321,11 @@ harness = false [[bench]] name = "slices_iterator" harness = false + +[[bench]] +name = "bitmap_assign_ops" +harness = false + +[[bench]] +name = "assign_ops" +harness = false diff --git a/benches/assign_ops.rs b/benches/assign_ops.rs new file mode 100644 index 00000000000..0376edccf08 --- /dev/null +++ b/benches/assign_ops.rs @@ -0,0 +1,29 @@ +use criterion::{criterion_group, criterion_main, Criterion}; + +use arrow2::{compute::arithmetics::basic::mul_scalar, util::bench_util::create_primitive_array}; + +fn add_benchmark(c: &mut Criterion) { + (10..=20).step_by(2).for_each(|log2_size| { + let size = 2usize.pow(log2_size); + + let mut arr_a = create_primitive_array::(size, 0.2); + c.bench_function(&format!("apply_mul 2^{}", log2_size), |b| { + b.iter(|| { + criterion::black_box(&mut arr_a) + .apply_values(|x| x.iter_mut().for_each(|x| *x *= 1.01)); + assert!(!arr_a.value(10).is_nan()); + }) + }); + + let arr_a = create_primitive_array::(size, 0.2); + c.bench_function(&format!("mul 2^{}", log2_size), |b| { + b.iter(|| { + let a = mul_scalar(criterion::black_box(&arr_a), &1.01f32); + assert!(!a.value(10).is_nan()); + }) + }); + }); +} + +criterion_group!(benches, add_benchmark); +criterion_main!(benches); diff --git a/benches/bitmap_assign_ops.rs b/benches/bitmap_assign_ops.rs new file mode 100644 index 00000000000..c962e8ed950 --- /dev/null +++ b/benches/bitmap_assign_ops.rs @@ -0,0 +1,47 @@ +use criterion::{criterion_group, criterion_main, Criterion}; + +use arrow2::bitmap::{binary_assign, unary_assign}; +use arrow2::bitmap::{Bitmap, MutableBitmap}; + +fn add_benchmark(c: &mut Criterion) { + (10..=20).step_by(2).for_each(|log2_size| { + let size = 2usize.pow(log2_size); + + let mut bitmap: MutableBitmap = (0..size).into_iter().map(|x| x % 3 == 0).collect(); + c.bench_function(&format!("mutablebitmap not 2^{}", log2_size), |b| { + b.iter(|| { + unary_assign(criterion::black_box(&mut bitmap), |x: u64| !x); + assert!(!bitmap.is_empty()); + }) + }); + + let bitmap: Bitmap = (0..size).into_iter().map(|x| x % 3 == 0).collect(); + c.bench_function(&format!("bitmap not 2^{}", log2_size), |b| { + b.iter(|| { + let r = !criterion::black_box(&bitmap); + assert!(!r.is_empty()); + }) + }); + + let mut lhs: MutableBitmap = (0..size).into_iter().map(|x| x % 3 == 0).collect(); + let rhs: Bitmap = (0..size).into_iter().map(|x| x % 4 == 0).collect(); + c.bench_function(&format!("mutablebitmap and 2^{}", log2_size), |b| { + b.iter(|| { + binary_assign(criterion::black_box(&mut lhs), &rhs, |x: u64, y| x & y); + assert!(!bitmap.is_empty()); + }) + }); + + let lhs: Bitmap = (0..size).into_iter().map(|x| x % 3 == 0).collect(); + let rhs: Bitmap = (0..size).into_iter().map(|x| x % 4 == 0).collect(); + c.bench_function(&format!("bitmap and 2^{}", log2_size), |b| { + b.iter(|| { + let r = criterion::black_box(&lhs) & &rhs; + assert!(!r.is_empty()); + }) + }); + }); +} + +criterion_group!(benches, add_benchmark); +criterion_main!(benches); diff --git a/src/array/boolean/mod.rs b/src/array/boolean/mod.rs index f25c044ae35..977529cf5ca 100644 --- a/src/array/boolean/mod.rs +++ b/src/array/boolean/mod.rs @@ -1,5 +1,5 @@ use crate::{ - bitmap::Bitmap, + bitmap::{Bitmap, MutableBitmap}, datatypes::{DataType, PhysicalType}, error::Error, }; @@ -92,6 +92,44 @@ impl BooleanArray { pub fn arced(self) -> std::sync::Arc { std::sync::Arc::new(self) } + + /// Applies a function `f` to the values of this array, cloning the values + /// iff they are being shared with others + /// + /// This is an API to use clone-on-write + /// # Implementation + /// This function is `O(f)` if the data is not being shared, and `O(N) + O(f)` + /// if it is being shared (since it results in a `O(N)` memcopy). + /// # Panics + /// This function panics if the function modifies the length of the [`MutableBitmap`]. + pub fn apply_values(&mut self, f: F) { + let values = std::mem::take(&mut self.values); + let mut values = values.make_mut(); + f(&mut values); + if let Some(validity) = &self.validity { + assert_eq!(validity.len(), values.len()); + } + self.values = values.into(); + } + + /// Applies a function `f` to the validity of this array, cloning it + /// iff it is being shared. + /// + /// This is an API to leverage clone-on-write + /// # Implementation + /// This function is `O(f)` if the data is not being shared, and `O(N) + O(f)` + /// if it is being shared (since it results in a `O(N)` memcopy). + /// # Panics + /// This function panics if the function modifies the length of the [`MutableBitmap`]. + pub fn apply_validity(&mut self, f: F) { + if let Some(validity) = self.validity.as_mut() { + let values = std::mem::take(validity); + let mut bitmap = values.make_mut(); + f(&mut bitmap); + assert_eq!(bitmap.len(), self.values.len()); + *validity = bitmap.into(); + } + } } // must use diff --git a/src/array/primitive/mod.rs b/src/array/primitive/mod.rs index 5270be8ea88..cc419c06458 100644 --- a/src/array/primitive/mod.rs +++ b/src/array/primitive/mod.rs @@ -1,7 +1,7 @@ use crate::{ bitmap::{ utils::{zip_validity, ZipValidity}, - Bitmap, + Bitmap, MutableBitmap, }, buffer::Buffer, datatypes::*, @@ -252,6 +252,39 @@ impl PrimitiveArray { arr } + /// Applies a function `f` to the values of this array, cloning the values + /// iff they are being shared with others + /// + /// This is an API to use clone-on-write + /// # Implementation + /// This function is `O(f)` if the data is not being shared, and `O(N) + O(f)` + /// if it is being shared (since it results in a `O(N)` memcopy). + pub fn apply_values(&mut self, f: F) { + let values = std::mem::take(&mut self.values); + let mut values = values.make_mut(); + f(&mut values); + self.values = values.into(); + } + + /// Applies a function `f` to the validity of this array, cloning it + /// iff it is being shared. + /// + /// This is an API to leverage clone-on-write + /// # Implementation + /// This function is `O(f)` if the data is not being shared, and `O(N) + O(f)` + /// if it is being shared (since it results in a `O(N)` memcopy). + /// # Panics + /// This function panics if the function modifies the length of the [`MutableBitmap`]. + pub fn apply_validity(&mut self, f: F) { + if let Some(validity) = self.validity.as_mut() { + let values = std::mem::take(validity); + let mut bitmap = values.make_mut(); + f(&mut bitmap); + assert_eq!(bitmap.len(), self.values.len()); + *validity = bitmap.into(); + } + } + /// Try to convert this [`PrimitiveArray`] to a [`MutablePrimitiveArray`] via copy-on-write semantics. /// /// A [`PrimitiveArray`] is backed by a [`Buffer`] and [`Bitmap`] which are essentially `Arc>`. diff --git a/src/bitmap/assign_ops.rs b/src/bitmap/assign_ops.rs new file mode 100644 index 00000000000..31bfa24e2ec --- /dev/null +++ b/src/bitmap/assign_ops.rs @@ -0,0 +1,191 @@ +use crate::bitmap::{Bitmap, MutableBitmap}; + +use super::utils::{BitChunk, BitChunkIterExact, BitChunksExact}; + +/// Applies a function to every bit of this [`MutableBitmap`] in chunks +/// +/// This function can be for operations like `!` to a [`MutableBitmap`]. +pub fn unary_assign T>(bitmap: &mut MutableBitmap, op: F) { + let mut chunks = bitmap.bitchunks_exact_mut::(); + + chunks.by_ref().for_each(|chunk| { + let new_chunk: T = match (chunk as &[u8]).try_into() { + Ok(a) => T::from_ne_bytes(a), + Err(_) => unreachable!(), + }; + let new_chunk = op(new_chunk); + chunk.copy_from_slice(new_chunk.to_ne_bytes().as_ref()); + }); + + if chunks.remainder().is_empty() { + return; + } + let mut new_remainder = T::zero().to_ne_bytes(); + chunks + .remainder() + .iter() + .enumerate() + .for_each(|(index, b)| new_remainder[index] = *b); + new_remainder = op(T::from_ne_bytes(new_remainder)).to_ne_bytes(); + + let len = chunks.remainder().len(); + chunks + .remainder() + .copy_from_slice(&new_remainder.as_ref()[..len]); +} + +impl std::ops::Not for MutableBitmap { + type Output = Self; + + #[inline] + fn not(mut self) -> Self { + unary_assign(&mut self, |a: u64| !a); + self + } +} + +fn binary_assign_impl(lhs: &mut MutableBitmap, mut rhs: I, op: F) +where + I: BitChunkIterExact, + T: BitChunk, + F: Fn(T, T) -> T, +{ + let mut lhs_chunks = lhs.bitchunks_exact_mut::(); + + lhs_chunks + .by_ref() + .zip(rhs.by_ref()) + .for_each(|(lhs, rhs)| { + let new_chunk: T = match (lhs as &[u8]).try_into() { + Ok(a) => T::from_ne_bytes(a), + Err(_) => unreachable!(), + }; + let new_chunk = op(new_chunk, rhs); + lhs.copy_from_slice(new_chunk.to_ne_bytes().as_ref()); + }); + + let rem_lhs = lhs_chunks.remainder(); + let rem_rhs = rhs.remainder(); + if rem_lhs.is_empty() { + return; + } + let mut new_remainder = T::zero().to_ne_bytes(); + lhs_chunks + .remainder() + .iter() + .enumerate() + .for_each(|(index, b)| new_remainder[index] = *b); + new_remainder = op(T::from_ne_bytes(new_remainder), rem_rhs).to_ne_bytes(); + + let len = lhs_chunks.remainder().len(); + lhs_chunks + .remainder() + .copy_from_slice(&new_remainder.as_ref()[..len]); +} + +/// Apply a bitwise binary operation to a [`MutableBitmap`]. +/// +/// This function can be used for operations like `&=` to a [`MutableBitmap`]. +/// # Panics +/// This function panics iff `lhs.len() != `rhs.len()` +pub fn binary_assign(lhs: &mut MutableBitmap, rhs: &Bitmap, op: F) +where + F: Fn(T, T) -> T, +{ + assert_eq!(lhs.len(), rhs.len()); + + let (slice, offset, length) = rhs.as_slice(); + if offset == 0 { + let iter = BitChunksExact::::new(slice, length); + binary_assign_impl(lhs, iter, op) + } else { + let rhs_chunks = rhs.chunks::(); + binary_assign_impl(lhs, rhs_chunks, op) + } +} + +#[inline] +/// Compute bitwise OR operation in-place +fn or_assign(lhs: &mut MutableBitmap, rhs: &Bitmap) { + if rhs.null_count() == 0 { + assert_eq!(lhs.len(), rhs.len()); + lhs.clear(); + lhs.extend_constant(rhs.len(), true); + } else if rhs.null_count() == rhs.len() { + // bitmap remains + } else { + binary_assign(lhs, rhs, |x: T, y| x | y) + } +} + +impl<'a, 'b> std::ops::BitOrAssign<&'a Bitmap> for &'b mut MutableBitmap { + #[inline] + fn bitor_assign(&mut self, rhs: &'a Bitmap) { + or_assign::(self, rhs) + } +} + +impl<'a, 'b> std::ops::BitOr<&'a Bitmap> for MutableBitmap { + type Output = Self; + + #[inline] + fn bitor(mut self, rhs: &'a Bitmap) -> Self { + or_assign::(&mut self, rhs); + self + } +} + +#[inline] +/// Compute bitwise `&` between `lhs` and `rhs`, assigning it to `lhs` +fn and_assign(lhs: &mut MutableBitmap, rhs: &Bitmap) { + if rhs.null_count() == 0 { + // bitmap remains + } + if rhs.null_count() == rhs.len() { + assert_eq!(lhs.len(), rhs.len()); + lhs.clear(); + lhs.extend_constant(rhs.len(), false); + } else { + binary_assign(lhs, rhs, |x: T, y| x & y) + } +} + +impl<'a, 'b> std::ops::BitAndAssign<&'a Bitmap> for &'b mut MutableBitmap { + #[inline] + fn bitand_assign(&mut self, rhs: &'a Bitmap) { + and_assign::(self, rhs) + } +} + +impl<'a, 'b> std::ops::BitAnd<&'a Bitmap> for MutableBitmap { + type Output = Self; + + #[inline] + fn bitand(mut self, rhs: &'a Bitmap) -> Self { + and_assign::(&mut self, rhs); + self + } +} + +#[inline] +/// Compute bitwise XOR operation +fn xor_assign(lhs: &mut MutableBitmap, rhs: &Bitmap) { + binary_assign(lhs, rhs, |x: T, y| x ^ y) +} + +impl<'a, 'b> std::ops::BitXorAssign<&'a Bitmap> for &'b mut MutableBitmap { + #[inline] + fn bitxor_assign(&mut self, rhs: &'a Bitmap) { + xor_assign::(self, rhs) + } +} + +impl<'a, 'b> std::ops::BitXor<&'a Bitmap> for MutableBitmap { + type Output = Self; + + #[inline] + fn bitxor(mut self, rhs: &'a Bitmap) -> Self { + xor_assign::(&mut self, rhs); + self + } +} diff --git a/src/bitmap/immutable.rs b/src/bitmap/immutable.rs index bee9e62c634..45d8e89e4e5 100644 --- a/src/bitmap/immutable.rs +++ b/src/bitmap/immutable.rs @@ -234,6 +234,17 @@ impl Bitmap { } } + /// Converts this [`Bitmap`] into a [`MutableBitmap`], cloning its internal + /// buffer if required (clone-on-write). + pub fn make_mut(self) -> MutableBitmap { + match self.into_mut() { + Either::Left(data) => { + MutableBitmap::from_vec(data.bytes.as_ref().to_vec(), data.length) + } + Either::Right(data) => data, + } + } + /// Initializes an new [`Bitmap`] filled with unset values. #[inline] pub fn new_zeroed(length: usize) -> Self { diff --git a/src/bitmap/mod.rs b/src/bitmap/mod.rs index 1ee5c3f3018..a9325286f7c 100644 --- a/src/bitmap/mod.rs +++ b/src/bitmap/mod.rs @@ -8,4 +8,7 @@ pub use mutable::MutableBitmap; mod bitmap_ops; pub use bitmap_ops::*; +mod assign_ops; +pub use assign_ops::*; + pub mod utils; diff --git a/src/bitmap/mutable.rs b/src/bitmap/mutable.rs index b9056164bb8..d490a74eaca 100644 --- a/src/bitmap/mutable.rs +++ b/src/bitmap/mutable.rs @@ -5,7 +5,9 @@ use crate::bitmap::utils::{merge_reversed, set_bit_unchecked}; use crate::error::Error; use crate::trusted_len::TrustedLen; -use super::utils::{count_zeros, fmt, get_bit, set, set_bit, BitmapIter}; +use super::utils::{ + count_zeros, fmt, get_bit, set, set_bit, BitChunk, BitChunksExactMut, BitmapIter, +}; use super::Bitmap; /// A container of booleans. [`MutableBitmap`] is semantically equivalent @@ -149,7 +151,7 @@ impl MutableBitmap { set_bit(self.buffer.as_mut_slice(), index, value) } - /// constructs a new iterator over the values of [`MutableBitmap`]. + /// constructs a new iterator over the bits of [`MutableBitmap`]. pub fn iter(&self) -> BitmapIter { BitmapIter::new(&self.buffer, 0, self.length) } @@ -308,6 +310,11 @@ impl MutableBitmap { pub fn shrink_to_fit(&mut self) { self.buffer.shrink_to_fit(); } + + /// Returns an iterator over mutable slices, [`BitChunksExactMut`] + pub(crate) fn bitchunks_exact_mut(&mut self) -> BitChunksExactMut { + BitChunksExactMut::new(&mut self.buffer, self.length) + } } impl From for Bitmap { diff --git a/src/bitmap/utils/chunks_exact_mut.rs b/src/bitmap/utils/chunks_exact_mut.rs new file mode 100644 index 00000000000..afc55ee093d --- /dev/null +++ b/src/bitmap/utils/chunks_exact_mut.rs @@ -0,0 +1,60 @@ +use super::BitChunk; + +/// An iterator over mutable slices of bytes of exact size. +/// +/// # Safety +/// The slices returned by this iterator are guaranteed to have length equal to +/// `std::mem::size_of::()`. +#[derive(Debug)] +pub struct BitChunksExactMut<'a, T: BitChunk> { + chunks: std::slice::ChunksExactMut<'a, u8>, + remainder: &'a mut [u8], + remainder_len: usize, + marker: std::marker::PhantomData, +} + +impl<'a, T: BitChunk> BitChunksExactMut<'a, T> { + /// Returns a new [`BitChunksExactMut`] + #[inline] + pub fn new(bitmap: &'a mut [u8], length: usize) -> Self { + let size_of = std::mem::size_of::(); + + let split = (length / 8 / size_of) * size_of; + let (chunks, remainder) = bitmap.split_at_mut(split); + let remainder_len = length - chunks.len() * 8; + + let chunks = chunks.chunks_exact_mut(size_of); + Self { + chunks, + remainder, + remainder_len, + marker: std::marker::PhantomData, + } + } + + /// The remainder slice + #[inline] + pub fn remainder(&mut self) -> &mut [u8] { + self.remainder + } + + /// The length of the remainder slice in bits. + #[inline] + pub fn remainder_len(&mut self) -> usize { + self.remainder_len + } +} + +impl<'a, T: BitChunk> Iterator for BitChunksExactMut<'a, T> { + type Item = &'a mut [u8]; + + #[inline] + fn next(&mut self) -> Option { + self.chunks.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.chunks.size_hint() + } +} diff --git a/src/bitmap/utils/mod.rs b/src/bitmap/utils/mod.rs index 55ca42ba148..e2a5fdcb987 100644 --- a/src/bitmap/utils/mod.rs +++ b/src/bitmap/utils/mod.rs @@ -1,5 +1,6 @@ //! General utilities for bitmaps representing items where LSB is the first item. mod chunk_iterator; +mod chunks_exact_mut; mod fmt; mod iterator; mod slice_iterator; @@ -9,6 +10,7 @@ use std::convert::TryInto; pub(crate) use chunk_iterator::merge_reversed; pub use chunk_iterator::{BitChunk, BitChunkIterExact, BitChunks, BitChunksExact}; +pub use chunks_exact_mut::BitChunksExactMut; pub use fmt::fmt; pub use iterator::BitmapIter; pub use slice_iterator::SlicesIterator; diff --git a/src/buffer/immutable.rs b/src/buffer/immutable.rs index 758de74f29e..726099b14d1 100644 --- a/src/buffer/immutable.rs +++ b/src/buffer/immutable.rs @@ -157,6 +157,17 @@ impl Buffer { } } } + + /// Converts this [`Buffer`] to a [`Vec`], cloning the data if needed, also + /// known as clone-on-write semantics. + /// + /// This function is O(1) under the same conditions that [`Self::into_mut`] returns `Vec`. + pub fn make_mut(self) -> Vec { + match self.into_mut() { + Either::Left(data) => data.as_ref().to_vec(), + Either::Right(data) => data, + } + } } impl From> for Buffer { diff --git a/src/types/bit_chunk.rs b/src/types/bit_chunk.rs index 5bc4e398fd9..0937b500583 100644 --- a/src/types/bit_chunk.rs +++ b/src/types/bit_chunk.rs @@ -9,6 +9,8 @@ use super::NativeType; /// A chunk of bits. This is used to create masks of a given length /// whose width is `1` bit. In `portable_simd` notation, this corresponds to `m1xY`. +/// +/// This (sealed) trait is implemented for [`u8`], [`u16`], [`u32`] and [`u64`]. pub trait BitChunk: super::private::Sealed + PrimInt diff --git a/tests/it/array/boolean/mod.rs b/tests/it/array/boolean/mod.rs index 2dd47d480d9..786fdb1dbc8 100644 --- a/tests/it/array/boolean/mod.rs +++ b/tests/it/array/boolean/mod.rs @@ -131,3 +131,27 @@ fn from_iter() { let a: BooleanArray = iter.collect(); assert_eq!(a.len(), 2); } + +#[test] +fn apply_values() { + let mut a = BooleanArray::from([Some(true), Some(false), None]); + a.apply_values(|x| { + let mut a = std::mem::take(x); + a = !a; + *x = a; + }); + let expected = BooleanArray::from([Some(false), Some(true), None]); + assert_eq!(a, expected); +} + +#[test] +fn apply_validity() { + let mut a = BooleanArray::from([Some(true), Some(false), None]); + a.apply_validity(|x| { + let mut a = std::mem::take(x); + a = !a; + *x = a; + }); + let expected = BooleanArray::from([None, None, Some(false)]); + assert_eq!(a, expected); +} diff --git a/tests/it/array/primitive/mod.rs b/tests/it/array/primitive/mod.rs index 058d503f479..2af71d504a5 100644 --- a/tests/it/array/primitive/mod.rs +++ b/tests/it/array/primitive/mod.rs @@ -124,3 +124,25 @@ fn into_mut_3() { let array = PrimitiveArray::new(DataType::Int32, values, validity); assert!(array.into_mut().is_right()); } + +#[test] +fn apply_values() { + let mut a = PrimitiveArray::from([Some(1), Some(2), None]); + a.apply_values(|x| { + x[0] = 10; + }); + let expected = PrimitiveArray::from([Some(10), Some(2), None]); + assert_eq!(a, expected); +} + +#[test] +fn apply_validity() { + let mut a = PrimitiveArray::from([Some(1), Some(2), None]); + a.apply_validity(|x| { + let mut a = std::mem::take(x); + a = !a; + *x = a; + }); + let expected = PrimitiveArray::from([None, None, Some(0)]); + assert_eq!(a, expected); +} diff --git a/tests/it/bitmap/assign_ops.rs b/tests/it/bitmap/assign_ops.rs new file mode 100644 index 00000000000..bea2b0dcbf8 --- /dev/null +++ b/tests/it/bitmap/assign_ops.rs @@ -0,0 +1,42 @@ +use arrow2::bitmap::{binary_assign, unary_assign, Bitmap, MutableBitmap}; + +#[test] +fn basics() { + let mut b = MutableBitmap::from_iter(std::iter::repeat(true).take(10)); + unary_assign(&mut b, |x: u8| !x); + assert_eq!( + b, + MutableBitmap::from_iter(std::iter::repeat(false).take(10)) + ); + + let mut b = MutableBitmap::from_iter(std::iter::repeat(true).take(10)); + let c = Bitmap::from_iter(std::iter::repeat(true).take(10)); + binary_assign(&mut b, &c, |x: u8, y| x | y); + assert_eq!( + b, + MutableBitmap::from_iter(std::iter::repeat(true).take(10)) + ); +} + +#[test] +fn fast_paths() { + let b = MutableBitmap::from([true, false]); + let c = Bitmap::from_iter([true, true]); + let b = b & &c; + assert_eq!(b, MutableBitmap::from_iter([true, false])); + + let b = MutableBitmap::from([true, false]); + let c = Bitmap::from_iter([false, false]); + let b = b & &c; + assert_eq!(b, MutableBitmap::from_iter([false, false])); + + let b = MutableBitmap::from([true, false]); + let c = Bitmap::from_iter([true, true]); + let b = b | &c; + assert_eq!(b, MutableBitmap::from_iter([true, true])); + + let b = MutableBitmap::from([true, false]); + let c = Bitmap::from_iter([false, false]); + let b = b | &c; + assert_eq!(b, MutableBitmap::from_iter([true, false])); +} diff --git a/tests/it/bitmap/mod.rs b/tests/it/bitmap/mod.rs index af0c030f5c9..c04e2c009d2 100644 --- a/tests/it/bitmap/mod.rs +++ b/tests/it/bitmap/mod.rs @@ -1,3 +1,4 @@ +mod assign_ops; mod bitmap_ops; mod immutable; mod mutable;