Skip to content

Commit 9e87476

Browse files
committed
implement batch execute for bitpacked arrays
Signed-off-by: Connor Tsui <[email protected]>
1 parent 878f9b1 commit 9e87476

File tree

2 files changed

+111
-2
lines changed

2 files changed

+111
-2
lines changed

encodings/fastlanes/src/bitpacking/array/bitpack_decompress.rs

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,118 @@ use vortex_array::ToCanonical;
77
use vortex_array::arrays::PrimitiveArray;
88
use vortex_array::builders::{ArrayBuilder, PrimitiveBuilder, UninitRange};
99
use vortex_array::patches::Patches;
10+
use vortex_array::validity::Validity;
11+
use vortex_array::vtable::ValidityHelper;
12+
use vortex_buffer::BufferMut;
1013
use vortex_dtype::{
11-
IntegerPType, NativePType, match_each_integer_ptype, match_each_unsigned_integer_ptype,
14+
IntegerPType, NativePType, UnsignedPType, match_each_integer_ptype,
15+
match_each_unsigned_integer_ptype,
1216
};
1317
use vortex_error::{VortexExpect, vortex_panic};
14-
use vortex_mask::Mask;
18+
use vortex_mask::{Mask, MaskMut};
1519
use vortex_scalar::Scalar;
20+
use vortex_vector::primitive::{PVectorMut, PrimitiveVectorMut};
1621

1722
use crate::BitPackedArray;
1823
use crate::unpack_iter::BitPacked;
1924

25+
/// Unpacks a bit-packed array into a primitive vector.
26+
pub fn unpack_to_primitive_vector(array: &BitPackedArray) -> PrimitiveVectorMut {
27+
match_each_integer_ptype!(array.ptype(), |P| { unpack_to_pvector::<P>(array).into() })
28+
}
29+
30+
/// Unpacks a bit-packed array into a generic [`PVectorMut`].
31+
pub fn unpack_to_pvector<P: BitPacked>(array: &BitPackedArray) -> PVectorMut<P> {
32+
if array.is_empty() {
33+
return PVectorMut::with_capacity(0);
34+
}
35+
36+
let len = array.len();
37+
let mut elements = BufferMut::<P>::with_capacity(len);
38+
let uninit_slice = &mut elements.spare_capacity_mut()[..len];
39+
debug_assert_eq!(uninit_slice.len(), len);
40+
41+
// Decode into an uninitialized slice.
42+
let mut bit_packed_iter = array.unpacked_chunks();
43+
bit_packed_iter.decode_into(uninit_slice);
44+
// SAFETY: `decode_into` initialized exactly `len` elements into the spare (existing) capacity.
45+
unsafe { elements.set_len(len) };
46+
47+
let mut validity = array.validity_mask().into_mut();
48+
debug_assert_eq!(validity.len(), len);
49+
50+
// TODO(connor): Implement a fused version of patching instead.
51+
if let Some(patches) = array.patches() {
52+
let patch_indices = patches.indices().to_primitive();
53+
let patch_values = patches.values().to_primitive();
54+
let patches_validity = patch_values.validity();
55+
let patch_offset = patches.offset();
56+
57+
let patch_values_slice = patch_values.as_slice::<P>();
58+
match_each_unsigned_integer_ptype!(patch_indices.ptype(), |I| {
59+
let patch_indices_slice = patch_indices.as_slice::<I>();
60+
61+
// SAFETY:
62+
// - `Patches` invariant guarantees indices are sorted and within array bounds.
63+
// - `patch_indices` and `patch_values` have equal length (from `Patches` invariant).
64+
// - `elements` and `validity` have equal length (both are `len` from the array).
65+
// - All patch indices are valid after offset adjustment (guaranteed by `Patches`).
66+
unsafe {
67+
apply_patches_inner(
68+
&mut elements,
69+
&mut validity,
70+
patch_indices_slice,
71+
patch_offset,
72+
patch_values_slice,
73+
patches_validity,
74+
)
75+
};
76+
});
77+
}
78+
79+
// SAFETY: `elements` and `validity` have the same length.
80+
unsafe { PVectorMut::new_unchecked(elements, validity) }
81+
}
82+
83+
/// # Safety
84+
///
85+
/// - All indices in `patch_indices` after subtracting `patch_offset` must be valid indices
86+
/// into both `buffer` and `validity`.
87+
/// - `patch_indices` must be sorted in ascending order.
88+
/// - `patch_indices` and `patch_values` must have the same length.
89+
/// - `buffer` and `validity` must have the same length.
90+
unsafe fn apply_patches_inner<P, I>(
91+
buffer: &mut [P],
92+
validity: &mut MaskMut,
93+
patch_indices: &[I],
94+
patch_offset: usize,
95+
patch_values: &[P],
96+
patches_validity: &Validity,
97+
) where
98+
P: NativePType,
99+
I: UnsignedPType,
100+
{
101+
debug_assert!(!patch_indices.is_empty());
102+
debug_assert_eq!(patch_indices.len(), patch_values.len());
103+
debug_assert_eq!(buffer.len(), validity.len());
104+
debug_assert!(patch_indices.is_sorted());
105+
debug_assert!(patch_indices.last().vortex_expect("can't be empty").as_() <= validity.len());
106+
107+
match patches_validity {
108+
Validity::NonNullable | Validity::AllValid => {
109+
for (&i, &value) in patch_indices.iter().zip_eq(patch_values) {
110+
let index = i.as_() - patch_offset;
111+
112+
// SAFETY: `index` is valid because caller guarantees all patch indices are within
113+
// bounds after offset adjustment.
114+
unsafe { validity.set_unchecked(index) };
115+
buffer[index] = value;
116+
}
117+
}
118+
_ => vortex_panic!("BitPackedArray somehow had nullable patch values"),
119+
}
120+
}
121+
20122
pub fn unpack_array(array: &BitPackedArray) -> PrimitiveArray {
21123
match_each_integer_ptype!(array.ptype(), |P| { unpack_primitive_array::<P>(array) })
22124
}

encodings/fastlanes/src/bitpacking/vtable/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

4+
use vortex_array::execution::ExecutionCtx;
45
use vortex_array::patches::{Patches, PatchesMetadata};
56
use vortex_array::serde::ArrayChildren;
67
use vortex_array::validity::Validity;
@@ -11,8 +12,10 @@ use vortex_array::{
1112
use vortex_buffer::ByteBuffer;
1213
use vortex_dtype::{DType, PType};
1314
use vortex_error::{VortexError, VortexResult, vortex_bail, vortex_err};
15+
use vortex_vector::{Vector, VectorMutOps};
1416

1517
use crate::BitPackedArray;
18+
use crate::bitpack_decompress::unpack_to_primitive_vector;
1619

1720
mod array;
1821
mod canonical;
@@ -158,6 +161,10 @@ impl VTable for BitPackedVTable {
158161
})?,
159162
)
160163
}
164+
165+
fn execute(array: &BitPackedArray, _ctx: &mut dyn ExecutionCtx) -> VortexResult<Vector> {
166+
Ok(unpack_to_primitive_vector(array).freeze().into())
167+
}
161168
}
162169

163170
#[derive(Clone, Debug)]

0 commit comments

Comments
 (0)