Skip to content

Commit 44a9277

Browse files
authored
fix: BitPackedArray ptype changed by compute funcs (#1724)
We need to reinterpret_cast the array before we patch it
1 parent b1c2b1f commit 44a9277

File tree

6 files changed

+93
-21
lines changed

6 files changed

+93
-21
lines changed

bench-vortex/src/bin/clickbench.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,9 @@ fn main() {
163163
for _ in 0..args.iterations {
164164
let exec_duration = runtime.block_on(async {
165165
let start = Instant::now();
166-
execute_query(&context, &query).await.unwrap();
166+
execute_query(&context, &query)
167+
.await
168+
.unwrap_or_else(|e| panic!("executing query {query_idx}: {e}"));
167169
start.elapsed()
168170
});
169171

encodings/fastlanes/src/bitpacking/compress.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ pub fn unpack(array: BitPackedArray) -> VortexResult<PrimitiveArray> {
194194
let length = array.len();
195195
let offset = array.offset() as usize;
196196
let ptype = array.ptype();
197-
let mut unpacked = match_each_unsigned_integer_ptype!(array.ptype().to_unsigned(), |$P| {
197+
let mut unpacked = match_each_unsigned_integer_ptype!(ptype.to_unsigned(), |$P| {
198198
PrimitiveArray::from_vec(
199199
unpack_primitive::<$P>(array.packed_slice::<$P>(), bit_width, offset, length),
200200
array.validity(),

encodings/fastlanes/src/bitpacking/compute/filter.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,6 @@ mod test {
178178

179179
#[test]
180180
fn filter_bitpacked_signed() {
181-
// Elements 0..=499 are negative integers (patches)
182-
// Element 500 = 0 (packed)
183-
// Elements 501..999 are positive integers (packed)
184181
let values: Vec<i64> = (0..500).collect_vec();
185182
let unpacked = PrimitiveArray::from(values.clone());
186183
let bitpacked = BitPackedArray::encode(unpacked.as_ref(), 9).unwrap();

encodings/fastlanes/src/bitpacking/compute/take.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ impl TakeFn<BitPackedArray> for BitPackedEncoding {
2828

2929
// NOTE: we use the unsigned PType because all values in the BitPackedArray must
3030
// be non-negative (pre-condition of creating the BitPackedArray).
31-
let ptype: PType = PType::try_from(array.dtype())?.to_unsigned();
31+
let ptype: PType = PType::try_from(array.dtype())?;
3232
let validity = array.validity();
3333
let taken_validity = validity.take(indices)?;
3434

3535
let indices = indices.clone().into_primitive()?;
36-
let taken = match_each_unsigned_integer_ptype!(ptype, |$T| {
36+
let taken = match_each_unsigned_integer_ptype!(ptype.to_unsigned(), |$T| {
3737
match_each_integer_ptype!(indices.ptype(), |$I| {
3838
take_primitive::<$T, $I>(array, &indices, taken_validity)?
3939
})
@@ -107,7 +107,11 @@ fn take_primitive<T: NativePType + BitPacking, I: NativePType>(
107107
}
108108
});
109109

110-
let unpatched_taken = PrimitiveArray::from_vec(output, taken_validity);
110+
let mut unpatched_taken = PrimitiveArray::from_vec(output, taken_validity);
111+
// Flip back to signed type before patching.
112+
if array.ptype().is_signed_int() {
113+
unpatched_taken = unpatched_taken.reinterpret_cast(array.ptype());
114+
}
111115
if let Some(patches) = array.patches() {
112116
if let Some(patches) = patches.take(&indices.to_array())? {
113117
return unpatched_taken.patch(patches);
@@ -125,6 +129,7 @@ mod test {
125129
use rand::{thread_rng, Rng};
126130
use vortex_array::array::PrimitiveArray;
127131
use vortex_array::compute::{scalar_at, slice, take};
132+
use vortex_array::validity::Validity;
128133
use vortex_array::{IntoArrayData, IntoArrayVariant};
129134

130135
use crate::BitPackedArray;
@@ -210,4 +215,25 @@ mod test {
210215
);
211216
});
212217
}
218+
219+
#[test]
220+
#[cfg_attr(miri, ignore)]
221+
fn take_signed_with_patches() {
222+
let start = BitPackedArray::encode(
223+
&PrimitiveArray::from(vec![1i32, 2i32, 3i32, 4i32]).into_array(),
224+
1,
225+
)
226+
.unwrap();
227+
228+
let taken_primitive = super::take_primitive::<u32, u64>(
229+
&start,
230+
&PrimitiveArray::from(vec![0u64, 1, 2, 3]),
231+
Validity::NonNullable,
232+
)
233+
.unwrap();
234+
assert_eq!(
235+
taken_primitive.into_maybe_null_slice::<i32>(),
236+
vec![1i32, 2, 3, 4]
237+
);
238+
}
213239
}

encodings/fastlanes/src/bitpacking/mod.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,17 @@ impl BitPackedArray {
100100
);
101101
}
102102

103+
if let Some(ref patches) = patches {
104+
// Ensure that array and patches have same PType
105+
if !patches.dtype().eq_ignore_nullability(ptype.into()) {
106+
vortex_bail!(
107+
"Patches DType {} does not match BitPackedArray dtype {}",
108+
patches.dtype().as_nonnullable(),
109+
ptype
110+
)
111+
}
112+
}
113+
103114
// expected packed size is in bytes
104115
let expected_packed_size =
105116
((length + offset as usize + 1023) / 1024) * (128 * bit_width as usize);
@@ -276,8 +287,9 @@ impl PrimitiveArrayTrait for BitPackedArray {}
276287

277288
#[cfg(test)]
278289
mod test {
290+
use itertools::Itertools;
279291
use vortex_array::array::PrimitiveArray;
280-
use vortex_array::{IntoArrayData, IntoArrayVariant};
292+
use vortex_array::{IntoArrayData, IntoArrayVariant, IntoCanonical};
281293

282294
use crate::BitPackedArray;
283295

@@ -305,4 +317,22 @@ mod test {
305317
let _packed = BitPackedArray::encode(uncompressed.as_ref(), 9)
306318
.expect_err("Cannot pack value into larger width");
307319
}
320+
321+
#[test]
322+
fn signed_with_patches() {
323+
let values = (0i32..=512).collect_vec();
324+
let parray = PrimitiveArray::from(values.clone()).into_array();
325+
326+
let packed_with_patches = BitPackedArray::encode(&parray, 9).unwrap();
327+
assert!(packed_with_patches.patches().is_some());
328+
assert_eq!(
329+
packed_with_patches
330+
.into_canonical()
331+
.unwrap()
332+
.into_primitive()
333+
.unwrap()
334+
.into_maybe_null_slice::<i32>(),
335+
values
336+
);
337+
}
308338
}

vortex-array/src/array/primitive/patch.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,50 @@
1-
use itertools::Itertools;
2-
use vortex_dtype::{match_each_integer_ptype, match_each_native_ptype};
1+
use arrow_buffer::ArrowNativeType;
2+
use vortex_dtype::{match_each_integer_ptype, match_each_native_ptype, NativePType};
33
use vortex_error::VortexResult;
44

55
use crate::array::PrimitiveArray;
66
use crate::patches::Patches;
7+
use crate::validity::Validity;
78
use crate::variants::PrimitiveArrayTrait;
89
use crate::{ArrayLen, IntoArrayVariant};
910

1011
impl PrimitiveArray {
1112
#[allow(clippy::cognitive_complexity)]
1213
pub fn patch(self, patches: Patches) -> VortexResult<Self> {
13-
let (_, indices, values) = patches.into_parts();
14-
let indices = indices.into_primitive()?;
15-
let values = values.into_primitive()?;
14+
let (_, patch_indices, patch_values) = patches.into_parts();
15+
let patch_indices = patch_indices.into_primitive()?;
16+
let patch_values = patch_values.into_primitive()?;
1617

1718
let patched_validity =
1819
self.validity()
19-
.patch(self.len(), indices.as_ref(), values.validity())?;
20+
.patch(self.len(), patch_indices.as_ref(), patch_values.validity())?;
2021

21-
match_each_integer_ptype!(indices.ptype(), |$I| {
22+
match_each_integer_ptype!(patch_indices.ptype(), |$I| {
2223
match_each_native_ptype!(self.ptype(), |$T| {
23-
let mut own_values = self.into_maybe_null_slice::<$T>();
24-
for (idx, value) in indices.into_maybe_null_slice::<$I>().into_iter().zip_eq(values.into_maybe_null_slice::<$T>().into_iter()) {
25-
own_values[idx as usize] = value;
26-
}
27-
Ok(Self::from_vec(own_values, patched_validity))
24+
self.patch_typed::<$T, $I>(patch_indices, patch_values, patched_validity)
2825
})
2926
})
3027
}
28+
29+
fn patch_typed<T, I>(
30+
self,
31+
patch_indices: PrimitiveArray,
32+
patch_values: PrimitiveArray,
33+
patched_validity: Validity,
34+
) -> VortexResult<Self>
35+
where
36+
T: NativePType + ArrowNativeType,
37+
I: NativePType + ArrowNativeType,
38+
{
39+
let mut own_values = self.into_maybe_null_slice::<T>();
40+
41+
let patch_indices = patch_indices.into_maybe_null_slice::<I>();
42+
let patch_values = patch_values.into_maybe_null_slice::<T>();
43+
for (idx, value) in itertools::zip_eq(patch_indices, patch_values) {
44+
own_values[idx.as_usize()] = value;
45+
}
46+
Ok(Self::from_vec(own_values, patched_validity))
47+
}
3148
}
3249

3350
#[cfg(test)]

0 commit comments

Comments
 (0)