blob: 9cd08a9eb87b79792fe8ebb1e9c505ea7dd42ffa [file]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{ptr::NonNull, sync::Arc};
use arrow::{
array::{
Array, ArrayRef, ArrowPrimitiveType, BinaryArray, BooleanBufferBuilder, GenericByteArray,
PrimitiveArray, StringArray, downcast_primitive,
},
buffer::{MutableBuffer, NullBuffer, OffsetBuffer, ScalarBuffer},
datatypes::{ArrowNativeType, ByteArrayType},
record_batch::{RecordBatch, RecordBatchOptions},
};
use arrow_schema::DataType;
use datafusion::common::Result;
use crate::{downcast_any, prefetch_read_data, unchecked};
pub fn take_batch<T: ArrowPrimitiveType>(
batch: RecordBatch,
indices: impl Into<PrimitiveArray<T>>,
) -> Result<RecordBatch> {
let indices = indices.into();
let taken_num_batch_rows = indices.len();
Ok(RecordBatch::try_new_with_options(
batch.schema(),
take_cols_internal(batch.columns(), &indices)?,
&RecordBatchOptions::new().with_row_count(Some(taken_num_batch_rows)),
)?)
}
pub fn take_cols<T: ArrowPrimitiveType>(
cols: &[ArrayRef],
indices: impl Into<PrimitiveArray<T>>,
) -> Result<Vec<ArrayRef>> {
take_cols_internal(cols, &indices.into())
}
fn take_cols_internal<T: ArrowPrimitiveType>(
cols: &[ArrayRef],
indices: &PrimitiveArray<T>,
) -> Result<Vec<ArrayRef>> {
cols.iter()
.map(|c| Ok(arrow::compute::take(&c, indices, None)?))
.collect::<Result<_>>()
}
pub type ArrayInterleaver = Box<dyn Fn(&[(usize, usize)]) -> Result<ArrayRef> + Send>;
pub type BatchInterleaver = Box<dyn Fn(&[(usize, usize)]) -> Result<RecordBatch> + Send>;
#[inline]
pub fn create_batch_interleaver(
batches: &[RecordBatch],
with_prefetching: bool,
) -> Result<BatchInterleaver> {
let batch_schema = batches[0].schema();
let mut col_arrays = vec![vec![]; batches[0].num_columns()];
for batch in batches {
for (col_idx, col) in batch.columns().iter().enumerate() {
col_arrays[col_idx].push(col.clone());
}
}
let col_interleavers = col_arrays
.iter()
.map(|arrays| create_array_interleaver(arrays, with_prefetching))
.collect::<Result<Vec<_>>>()?;
Ok(Box::new(move |indices| {
let cols = col_interleavers
.iter()
.map(|col_interleaver| col_interleaver(indices))
.collect::<Result<Vec<_>>>();
let batch = RecordBatch::try_new_with_options(
batch_schema.clone(),
cols?,
&RecordBatchOptions::new().with_row_count(Some(indices.len())),
)?;
Ok(batch)
}))
}
#[inline]
pub fn create_array_interleaver(
values: &[ArrayRef],
with_prefetching: bool,
) -> Result<ArrayInterleaver> {
struct Interleave<T> {
arrays: Vec<T>,
has_nulls: bool,
}
impl<T: Array> Interleave<T> {
fn new(arrays: Vec<T>) -> Self {
let has_nulls = arrays.iter().any(|v| v.null_count() > 0);
Self { arrays, has_nulls }
}
fn nulls(&self, indices: &[(usize, usize)]) -> Option<NullBuffer> {
match self.has_nulls {
true => {
let mut builder = BooleanBufferBuilder::new(indices.len());
for (a, b) in indices {
let v = self.arrays[*a].is_valid(*b);
builder.append(v)
}
Some(NullBuffer::new(builder.finish()))
}
false => None,
}
}
}
#[inline]
fn interleave_primitive<T: ArrowPrimitiveType, const WITH_PREFETCHING: bool>(
interleaver: &Interleave<PrimitiveArray<T>>,
indices: &[(usize, usize)],
dt: &DataType,
) -> Result<ArrayRef> {
let nulls = interleaver.nulls(indices);
let mut values = Vec::with_capacity(indices.len());
for (i, &(array_idx, value_idx)) in indices.iter().enumerate() {
if WITH_PREFETCHING {
const PREFETCH_AHEAD: usize = 4;
if i + PREFETCH_AHEAD < indices.len() {
let (prefetch_array_idx, prefetch_value_idx) = indices[i + PREFETCH_AHEAD];
prefetch_read_data!({
let array = interleaver.arrays.get_unchecked(prefetch_array_idx);
(array
.values()
.get_unchecked(array.offset() + prefetch_value_idx))
as _
});
}
}
let array = &interleaver.arrays[array_idx];
if array.is_valid(value_idx) {
values.push(array.value(value_idx));
} else {
values.push(Default::default());
}
}
let array = PrimitiveArray::<T>::new(values.into(), nulls);
Ok(Arc::new(array.with_data_type(dt.clone())))
}
#[inline]
fn interleave_bytes<T: ByteArrayType, const WITH_PREFETCHING: bool>(
interleaver: &Interleave<GenericByteArray<T>>,
indices: &[(usize, usize)],
) -> Result<ArrayRef> {
let nulls = interleaver.nulls(indices);
let mut offsets = Vec::with_capacity(indices.len() + 1);
let mut take_value_ptrs = Vec::with_capacity(indices.len());
let mut capacity = 0;
let zero = T::Offset::default();
offsets.push(zero);
for &(array_idx, value_idx) in indices {
let array = &interleaver.arrays[array_idx];
if array.is_valid(value_idx) {
let value_offsets = unchecked!(array.value_offsets());
let value_start = value_offsets[value_idx];
let value_end = value_offsets[value_idx + 1];
let value_len = value_end - value_start;
capacity += value_len.as_usize();
if value_len > zero {
let value_ptr = array.values().as_ptr().wrapping_add(value_start.as_usize());
take_value_ptrs.push(value_ptr);
}
}
offsets.push(T::Offset::from_usize(capacity).expect("overflow"));
}
let array = unsafe {
let mut values = MutableBuffer::new(capacity);
let mut dest_ptr = NonNull::new_unchecked(values.as_mut_ptr());
values.set_len(capacity);
let mut src_start_ptr = NonNull::dangling(); // initial value is unused
let mut src_end_ptr = src_start_ptr;
macro_rules! apply_copy {
() => {{
if src_end_ptr != src_start_ptr {
let src_len = src_end_ptr.offset_from(src_start_ptr) as usize;
dest_ptr.copy_from_nonoverlapping(src_start_ptr, src_len);
dest_ptr = dest_ptr.add(src_len);
}
}};
}
let mut take_value_ptr_idx = 0;
for i in 0..indices.len() {
let take_value_ptrs = unchecked!(&take_value_ptrs);
let offsets = unchecked!(&offsets);
let value_len = (offsets[i + 1] - offsets[i]).as_usize();
if value_len == 0 {
continue;
}
let value_ptr =
NonNull::new_unchecked(take_value_ptrs[take_value_ptr_idx] as *mut u8);
take_value_ptr_idx += 1;
// for continuous elements, just extend the area to copy
// otherwise, copy current area end move to the next area
if src_end_ptr != value_ptr {
prefetch_read_data!(value_ptr.as_ptr()); // prefetch next while copying current
apply_copy!();
src_start_ptr = value_ptr;
}
src_end_ptr = value_ptr.add(value_len);
}
// copy last area
apply_copy!();
assert_eq!(
dest_ptr.as_ptr(),
values.as_mut_ptr().wrapping_add(capacity)
);
// build array
let offsets = OffsetBuffer::new_unchecked(ScalarBuffer::from(offsets));
GenericByteArray::<T>::new_unchecked(offsets, values.into(), nulls)
};
Ok(Arc::new(array))
}
if !values.is_empty() {
let dt = values[0].data_type();
macro_rules! primitive_helper {
($t:ty, $dt:ident) => {{
let interleaver = Interleave::new(
values
.iter()
.map(|v| {
downcast_any!(v, PrimitiveArray<$t>)
.expect("Excepted a PrimitiveArray")
.clone()
})
.collect::<Vec<_>>(),
);
let dt = $dt.clone();
return Ok(Box::new(move |indices| {
if with_prefetching {
interleave_primitive::<_, true>(&interleaver, indices, &dt)
} else {
interleave_primitive::<_, false>(&interleaver, indices, &dt)
}
}));
}};
}
downcast_primitive! {
dt => (primitive_helper, dt),
DataType::Utf8 => {
let interleaver = Interleave::new(values
.iter()
.map(|v| downcast_any!(v, StringArray).expect("Excepted a StringArray").clone())
.collect::<Vec<_>>(),
);
return Ok(Box::new(move |indices| if with_prefetching {
interleave_bytes::<_, true>(&interleaver, indices)
} else {
interleave_bytes::<_, false>(&interleaver, indices)
}));
}
DataType::Binary => {
let interleaver = Interleave::new(values
.iter()
.map(|v| downcast_any!(v, BinaryArray).expect("Excepted a BinaryArray").clone())
.collect::<Vec<_>>(),
);
return Ok(Box::new(move |indices| if with_prefetching {
interleave_bytes::<_, true>(&interleaver, indices)
} else {
interleave_bytes::<_, false>(&interleaver, indices)
}));
}
_ => {},
}
}
// fallback to arrow's implementation
let values_cloned = values.to_vec();
Ok(Box::new(move |indices| {
let value_refs = values_cloned.iter().map(|v| v.as_ref()).collect::<Vec<_>>();
Ok(arrow::compute::interleave(&value_refs, indices)?)
}))
}