Skip to content

Commit df8dbf5

Browse files
committed
indices as child instead of buffer
Signed-off-by: Andrew Duffy <andrew@a10y.dev>
1 parent 60bf252 commit df8dbf5

File tree

6 files changed

+66
-60
lines changed

6 files changed

+66
-60
lines changed

vortex-array/src/arrays/patched/array.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ use crate::DynArray;
1414
use crate::ExecutionCtx;
1515
use crate::IntoArray;
1616
use crate::arrays::PrimitiveArray;
17-
use crate::arrays::patched::PatchAccessor;
1817
use crate::arrays::patched::TransposedPatches;
1918
use crate::arrays::patched::patch_lanes;
2019
use crate::buffer::BufferHandle;
2120
use crate::dtype::IntegerPType;
2221
use crate::dtype::NativePType;
22+
use crate::dtype::PType;
2323
use crate::match_each_native_ptype;
2424
use crate::match_each_unsigned_integer_ptype;
2525
use crate::patches::Patches;
@@ -48,7 +48,7 @@ pub struct PatchedArray {
4848
/// lane offsets. The PType of these MUST be u32
4949
pub(super) lane_offsets: BufferHandle,
5050
/// indices within a 1024-element chunk. The PType of these MUST be u16
51-
pub(super) indices: BufferHandle,
51+
pub(super) indices: ArrayRef,
5252
/// patch values corresponding to the indices. The ptype is specified by `values_ptype`.
5353
pub(super) values: ArrayRef,
5454

@@ -86,6 +86,12 @@ impl PatchedArray {
8686
values,
8787
} = transpose_patches(patches, ctx)?;
8888

89+
let indices = PrimitiveArray::from_buffer_handle(
90+
BufferHandle::new_host(indices),
91+
PType::U16,
92+
Validity::NonNullable,
93+
)
94+
.into_array();
8995
let values = PrimitiveArray::from_buffer_handle(
9096
BufferHandle::new_host(values),
9197
values_ptype,
@@ -102,19 +108,24 @@ impl PatchedArray {
102108
offset: 0,
103109
len,
104110
lane_offsets: BufferHandle::new_host(lane_offsets),
105-
indices: BufferHandle::new_host(indices),
111+
indices,
106112
values,
107113
stats_set: ArrayStats::default(),
108114
})
109115
}
116+
}
110117

111-
/// Get an accessor, which allows ranged access to patches by chunk/lane.
112-
pub fn accessor(&self) -> PatchAccessor<'_> {
113-
PatchAccessor {
114-
n_lanes: self.n_lanes,
115-
lane_offsets: self.lane_offsets.as_host().reinterpret::<u32>(),
116-
indices: self.indices.as_host().reinterpret::<u16>(),
117-
}
118+
impl PatchedArray {
119+
pub(crate) fn seek_to_lane(&self, chunk: usize, lane: usize) -> Range<usize> {
120+
assert!(chunk < self.n_chunks);
121+
assert!(lane < self.n_lanes);
122+
123+
let lane_offsets = self.lane_offsets.as_host().reinterpret::<u32>();
124+
125+
let start = lane_offsets[chunk * self.n_lanes + lane] as usize;
126+
let stop = lane_offsets[chunk * self.n_lanes + lane + 1] as usize;
127+
128+
start..stop
118129
}
119130

120131
/// Slice the array to just the patches and inner values that are within the chunk range.

vortex-array/src/arrays/patched/compute/compare.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,11 @@ impl CompareKernel for Patched {
9393
}
9494

9595
let lane_offsets = lhs.lane_offsets.as_host().reinterpret::<u32>();
96-
let indices = lhs.indices.as_host().reinterpret::<u16>();
96+
let indices = lhs.indices.clone().execute::<PrimitiveArray>(ctx)?;
9797
let values = lhs.values.clone().execute::<PrimitiveArray>(ctx)?;
9898

9999
match_each_native_ptype!(values.ptype(), |V| {
100+
let indices = indices.as_slice::<u16>();
100101
let values = values.as_slice::<V>();
101102
let constant = constant
102103
.as_primitive()

vortex-array/src/arrays/patched/compute/take.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ impl TakeExecute for Patched {
4646
match_each_unsigned_integer_ptype!(indices_ptype, |I| {
4747
match_each_native_ptype!(ptype, |V| {
4848
let indices = indices.clone().execute::<PrimitiveArray>(ctx)?;
49-
let values = array.values.clone().execute::<PrimitiveArray>(ctx)?;
49+
let patch_indices = array.indices.clone().execute::<PrimitiveArray>(ctx)?;
50+
let patch_values = array.values.clone().execute::<PrimitiveArray>(ctx)?;
5051
let mut output = Buffer::<V>::from_byte_buffer(buffer.unwrap_host()).into_mut();
5152
take_map(
5253
output.as_mut(),
@@ -56,8 +57,8 @@ impl TakeExecute for Patched {
5657
array.n_chunks,
5758
array.n_lanes,
5859
array.lane_offsets.as_host().reinterpret::<u32>(),
59-
array.indices.as_host().reinterpret::<u16>(),
60-
values.as_slice::<V>(),
60+
patch_indices.as_slice::<u16>(),
61+
patch_values.as_slice::<V>(),
6162
);
6263

6364
// SAFETY: output and validity still have same length after take_map returns.

vortex-array/src/arrays/patched/mod.rs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,3 @@ const fn patch_lanes<V: Sized>() -> usize {
3131
// from shared to global memory.
3232
if size_of::<V>() < 8 { 32 } else { 16 }
3333
}
34-
35-
/// A cached accessor to the patch values.
36-
pub struct PatchAccessor<'a> {
37-
n_lanes: usize,
38-
lane_offsets: &'a [u32],
39-
indices: &'a [u16],
40-
}
41-
42-
impl<'a> PatchAccessor<'a> {
43-
/// Get an iterator over indices and values offsets.
44-
///
45-
/// The first component is the index into the `indices` and `values`, and the second is
46-
/// the datum from `indices[index]` already prefetched.
47-
pub fn offsets_iter(
48-
&self,
49-
chunk: usize,
50-
lane: usize,
51-
) -> impl Iterator<Item = (usize, u16)> + '_ {
52-
let start = self.lane_offsets[chunk * self.n_lanes + lane] as usize;
53-
let stop = self.lane_offsets[chunk * self.n_lanes + lane + 1] as usize;
54-
55-
std::iter::zip(start..stop, self.indices[start..stop].iter().copied())
56-
}
57-
}

vortex-array/src/arrays/patched/vtable/mod.rs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use crate::builders::ArrayBuilder;
3939
use crate::builders::PrimitiveBuilder;
4040
use crate::dtype::DType;
4141
use crate::dtype::NativePType;
42+
use crate::dtype::PType;
4243
use crate::match_each_native_ptype;
4344
use crate::serde::ArrayChildren;
4445
use crate::stats::ArrayStats;
@@ -64,6 +65,8 @@ impl ValidityChild<Patched> for Patched {
6465
pub struct PatchedMetadata {
6566
#[prost(uint32, tag = "1")]
6667
pub(crate) offset: u32,
68+
#[prost(uint32, tag = "2")]
69+
pub(crate) n_patches: u32,
6770
}
6871

6972
impl VTable for Patched {
@@ -111,21 +114,19 @@ impl VTable for Patched {
111114
}
112115

113116
fn nbuffers(_array: &Self::Array) -> usize {
114-
3
117+
1
115118
}
116119

117120
fn buffer(array: &Self::Array, idx: usize) -> BufferHandle {
118121
match idx {
119122
0 => array.lane_offsets.clone(),
120-
1 => array.indices.clone(),
121123
_ => vortex_panic!("invalid buffer index for PatchedArray: {idx}"),
122124
}
123125
}
124126

125127
fn buffer_name(_array: &Self::Array, idx: usize) -> Option<String> {
126128
match idx {
127129
0 => Some("lane_offsets".to_string()),
128-
1 => Some("patch_indices".to_string()),
129130
_ => vortex_panic!("invalid buffer index for PatchedArray: {idx}"),
130131
}
131132
}
@@ -137,15 +138,17 @@ impl VTable for Patched {
137138
fn child(array: &Self::Array, idx: usize) -> ArrayRef {
138139
match idx {
139140
0 => array.inner.clone(),
140-
1 => array.values.clone(),
141+
1 => array.indices.clone(),
142+
2 => array.values.clone(),
141143
_ => vortex_panic!("invalid buffer index for PatchedArray: {idx}"),
142144
}
143145
}
144146

145147
fn child_name(_array: &Self::Array, idx: usize) -> String {
146148
match idx {
147149
0 => "inner".to_string(),
148-
1 => "patch_values".to_string(),
150+
1 => "patch_indices".to_string(),
151+
2 => "patch_values".to_string(),
149152
_ => vortex_panic!("invalid buffer index for PatchedArray: {idx}"),
150153
}
151154
}
@@ -154,6 +157,7 @@ impl VTable for Patched {
154157
fn metadata(array: &Self::Array) -> VortexResult<Self::Metadata> {
155158
Ok(ProstMetadata(PatchedMetadata {
156159
offset: array.offset as u32,
160+
n_patches: array.indices.len() as u32,
157161
}))
158162
}
159163

@@ -198,7 +202,7 @@ impl VTable for Patched {
198202
let offset = array.offset;
199203
let lane_offsets: Buffer<u32> =
200204
Buffer::from_byte_buffer(array.lane_offsets.clone().unwrap_host());
201-
let indices: Buffer<u16> = Buffer::from_byte_buffer(array.indices.clone().unwrap_host());
205+
let indices = array.indices.clone().execute::<PrimitiveArray>(ctx)?;
202206
let values = array.values.clone().execute::<PrimitiveArray>(ctx)?;
203207

204208
match_each_native_ptype!(ptype, |V| {
@@ -219,7 +223,7 @@ impl VTable for Patched {
219223
array.n_chunks,
220224
array.n_lanes,
221225
&lane_offsets,
222-
&indices,
226+
indices.as_slice::<u16>(),
223227
values.as_slice::<V>(),
224228
);
225229
});
@@ -234,19 +238,16 @@ impl VTable for Patched {
234238
buffers: &[BufferHandle],
235239
children: &dyn ArrayChildren,
236240
) -> VortexResult<PatchedArray> {
237-
let inner = children.get(0, dtype, len)?;
238-
239241
let n_chunks = len.div_ceil(1024);
240-
241242
let n_lanes = match_each_native_ptype!(dtype.as_ptype(), |P| { patch_lanes::<P>() });
242243

243-
let &[lane_offsets, indices] = &buffers else {
244+
let &[lane_offsets] = &buffers else {
244245
vortex_bail!("invalid buffer count for PatchedArray");
245246
};
246247

247-
// values and indices should have same len.
248-
let expected_len = indices.as_host().reinterpret::<u16>().len();
249-
let values = children.get(1, dtype, expected_len)?;
248+
let inner = children.get(0, dtype, len)?;
249+
let indices = children.get(1, PType::U16.into(), metadata.n_patches as usize)?;
250+
let values = children.get(1, dtype, metadata.n_patches as usize)?;
250251

251252
Ok(PatchedArray {
252253
inner,
@@ -255,7 +256,7 @@ impl VTable for Patched {
255256
offset: metadata.offset as usize,
256257
len,
257258
lane_offsets: lane_offsets.clone(),
258-
indices: indices.clone(),
259+
indices,
259260
values,
260261
stats_set: ArrayStats::default(),
261262
})
@@ -288,10 +289,10 @@ impl VTable for Patched {
288289

289290
let lane_offsets: Buffer<u32> =
290291
Buffer::from_byte_buffer(array.lane_offsets.clone().unwrap_host());
291-
let indices: Buffer<u16> = Buffer::from_byte_buffer(array.indices.clone().unwrap_host());
292-
let values = array.values.clone().execute::<PrimitiveArray>(ctx)?;
292+
let indices = array.indices.clone().execute::<PrimitiveArray>(ctx)?;
293293

294-
// TODO(aduffy): add support for non-primitive PatchedArray patches application.
294+
// TODO(aduffy): add support for non-primitive PatchedArray patches application (?)
295+
let values = array.values.clone().execute::<PrimitiveArray>(ctx)?;
295296

296297
let patched_values = match_each_native_ptype!(values.ptype(), |V| {
297298
let mut output = Buffer::<V>::from_byte_buffer(buffer.unwrap_host()).into_mut();
@@ -306,7 +307,7 @@ impl VTable for Patched {
306307
array.n_chunks,
307308
array.n_lanes,
308309
&lane_offsets,
309-
&indices,
310+
indices.as_slice::<u16>(),
310311
values.as_slice::<V>(),
311312
);
312313

vortex-array/src/arrays/patched/vtable/operations.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@
44
use vortex_error::VortexResult;
55

66
use crate::DynArray;
7+
use crate::ExecutionCtx;
8+
use crate::arrays::PrimitiveArray;
79
use crate::arrays::patched::Patched;
810
use crate::arrays::patched::PatchedArray;
911
use crate::arrays::patched::patch_lanes;
1012
use crate::dtype::PType;
1113
use crate::match_each_native_ptype;
14+
use crate::optimizer::ArrayOptimizer;
1215
use crate::scalar::Scalar;
1316
use crate::vtable::OperationsVTable;
1417

1518
impl OperationsVTable<Patched> for Patched {
16-
fn scalar_at(array: &PatchedArray, index: usize) -> VortexResult<Scalar> {
19+
fn scalar_at(
20+
array: &PatchedArray,
21+
index: usize,
22+
ctx: &mut ExecutionCtx,
23+
) -> VortexResult<Scalar> {
1724
// First check the patches
1825
let chunk = index / 1024;
1926
#[allow(clippy::cast_possible_truncation)]
@@ -22,11 +29,20 @@ impl OperationsVTable<Patched> for Patched {
2229
let values_ptype = PType::try_from(array.dtype())?;
2330

2431
let lane = match_each_native_ptype!(values_ptype, |V| { index % patch_lanes::<V>() });
25-
let accessor = array.accessor();
32+
33+
let range = array.seek_to_lane(chunk, lane);
34+
35+
// Get the range of indices corresponding to the lane, potentially decoding them to avoid
36+
// the overhead of repeated scalar_at calls.
37+
let patch_indices = array
38+
.indices
39+
.slice(range.clone())?
40+
.optimize()?
41+
.execute::<PrimitiveArray>(ctx)?;
2642

2743
// NOTE: we do linear scan as lane has <= 32 patches, binary search would likely
2844
// be slower.
29-
for (index, patch_index) in accessor.offsets_iter(chunk, lane) {
45+
for (&patch_index, index) in std::iter::zip(patch_indices.as_slice::<u16>(), range) {
3046
if patch_index == chunk_index {
3147
return array.values.scalar_at(index)?.cast(array.dtype());
3248
}

0 commit comments

Comments
 (0)