Skip to content

Commit 28c607a

Browse files
author
B Vadlamani
committed
expl_tuple_vec_on_demand_sort
expl_tuple_vec_on_demand_sort
1 parent b6e9863 commit 28c607a

2 files changed

Lines changed: 146 additions & 78 deletions

File tree

  • datafusion
    • functions-aggregate-common/src/aggregate/count_distinct
    • functions-aggregate/src

datafusion/functions-aggregate-common/src/aggregate/count_distinct/groups.rs

Lines changed: 93 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,91 @@ use arrow::array::{
2020
};
2121
use arrow::buffer::OffsetBuffer;
2222
use arrow::datatypes::{ArrowPrimitiveType, Field};
23-
use datafusion_common::HashSet;
24-
use datafusion_common::hash_utils::RandomState;
2523
use datafusion_expr_common::groups_accumulator::{EmitTo, GroupsAccumulator};
26-
use std::hash::Hash;
2724
use std::mem::size_of;
2825
use std::sync::Arc;
2926

3027
use crate::aggregate::groups_accumulator::accumulate::accumulate;
3128

29+
/// Trait for packing (group_idx, value) into a single sortable integer
30+
pub trait Packable: Copy + Send {
31+
type Packed: Ord + Copy + Send;
32+
fn pack(group_idx: usize, value: Self) -> Self::Packed;
33+
fn unpack(packed: Self::Packed) -> (usize, Self);
34+
}
35+
36+
macro_rules! impl_packable_signed {
37+
($native:ty, $unsigned:ty, $packed:ty, $bits:expr) => {
38+
impl Packable for $native {
39+
type Packed = $packed;
40+
#[inline]
41+
fn pack(group_idx: usize, value: Self) -> $packed {
42+
let val = (value as $unsigned ^ (1 << ($bits - 1))) as $packed;
43+
((group_idx as $packed) << $bits) | val
44+
}
45+
#[inline]
46+
fn unpack(packed: $packed) -> (usize, Self) {
47+
let group = (packed >> $bits) as usize;
48+
let val = ((packed as $unsigned) ^ (1 << ($bits - 1))) as $native;
49+
(group, val)
50+
}
51+
}
52+
};
53+
}
54+
55+
macro_rules! impl_packable_unsigned {
56+
($native:ty, $packed:ty, $bits:expr) => {
57+
impl Packable for $native {
58+
type Packed = $packed;
59+
#[inline]
60+
fn pack(group_idx: usize, value: Self) -> $packed {
61+
((group_idx as $packed) << $bits) | (value as $packed)
62+
}
63+
#[inline]
64+
fn unpack(packed: $packed) -> (usize, Self) {
65+
((packed >> $bits) as usize, packed as $native)
66+
}
67+
}
68+
};
69+
}
70+
71+
impl_packable_signed!(i64, u64, u128, 64);
72+
impl_packable_signed!(i32, u32, u64, 32);
73+
impl_packable_signed!(i16, u16, u64, 16);
74+
impl_packable_signed!(i8, u8, u64, 8);
75+
76+
impl_packable_unsigned!(u64, u128, 64);
77+
impl_packable_unsigned!(u32, u64, 32);
78+
impl_packable_unsigned!(u16, u64, 16);
79+
impl_packable_unsigned!(u8, u64, 8);
80+
81+
/// A `GroupsAccumulator` for COUNT(DISTINCT) on primitive types.
82+
///
83+
/// Uses a flat buffer with packed integers: (group_idx, value) packed into
84+
/// a single sortable integer. Sort and dedup at evaluate time.
3285
pub struct PrimitiveDistinctCountGroupsAccumulator<T: ArrowPrimitiveType>
3386
where
34-
T::Native: Eq + Hash,
87+
T::Native: Packable,
3588
{
36-
seen: HashSet<(usize, T::Native), RandomState>,
89+
buffer: Vec<<T::Native as Packable>::Packed>,
3790
num_groups: usize,
3891
}
3992

4093
impl<T: ArrowPrimitiveType> PrimitiveDistinctCountGroupsAccumulator<T>
4194
where
42-
T::Native: Eq + Hash,
95+
T::Native: Packable,
4396
{
4497
pub fn new() -> Self {
4598
Self {
46-
seen: HashSet::default(),
99+
buffer: Vec::new(),
47100
num_groups: 0,
48101
}
49102
}
50103
}
51104

52105
impl<T: ArrowPrimitiveType> Default for PrimitiveDistinctCountGroupsAccumulator<T>
53106
where
54-
T::Native: Eq + Hash,
107+
T::Native: Packable,
55108
{
56109
fn default() -> Self {
57110
Self::new()
@@ -61,7 +114,7 @@ where
61114
impl<T: ArrowPrimitiveType + Send + std::fmt::Debug> GroupsAccumulator
62115
for PrimitiveDistinctCountGroupsAccumulator<T>
63116
where
64-
T::Native: Eq + Hash,
117+
T::Native: Packable,
65118
{
66119
fn update_batch(
67120
&mut self,
@@ -73,8 +126,11 @@ where
73126
debug_assert_eq!(values.len(), 1);
74127
self.num_groups = self.num_groups.max(total_num_groups);
75128
let arr = values[0].as_primitive::<T>();
129+
130+
self.buffer.reserve(arr.len());
131+
76132
accumulate(group_indices, arr, opt_filter, |group_idx, value| {
77-
self.seen.insert((group_idx, value));
133+
self.buffer.push(T::Native::pack(group_idx, value));
78134
});
79135
Ok(())
80136
}
@@ -85,24 +141,31 @@ where
85141
EmitTo::First(n) => n,
86142
};
87143

144+
self.buffer.sort_unstable();
145+
88146
let mut counts = vec![0i64; num_emitted];
89147

90148
if matches!(emit_to, EmitTo::All) {
91-
for &(group_idx, _) in self.seen.iter() {
149+
self.buffer.dedup();
150+
for &packed in &self.buffer {
151+
let (group_idx, _) = T::Native::unpack(packed);
92152
counts[group_idx] += 1;
93153
}
94-
self.seen.clear();
154+
self.buffer.clear();
95155
self.num_groups = 0;
96156
} else {
97-
let mut remaining = HashSet::default();
98-
for (group_idx, value) in self.seen.drain() {
157+
self.buffer.dedup();
158+
let mut remaining = Vec::new();
159+
160+
for &packed in &self.buffer {
161+
let (group_idx, value) = T::Native::unpack(packed);
99162
if group_idx < num_emitted {
100163
counts[group_idx] += 1;
101164
} else {
102-
remaining.insert((group_idx - num_emitted, value));
165+
remaining.push(T::Native::pack(group_idx - num_emitted, value));
103166
}
104167
}
105-
self.seen = remaining;
168+
self.buffer = remaining;
106169
self.num_groups = self.num_groups.saturating_sub(num_emitted);
107170
}
108171

@@ -115,23 +178,29 @@ where
115178
EmitTo::First(n) => n,
116179
};
117180

181+
self.buffer.sort_unstable();
182+
self.buffer.dedup();
183+
118184
let mut group_values: Vec<Vec<T::Native>> = vec![Vec::new(); num_emitted];
119185

120186
if matches!(emit_to, EmitTo::All) {
121-
for (group_idx, value) in self.seen.drain() {
187+
for &packed in &self.buffer {
188+
let (group_idx, value) = T::Native::unpack(packed);
122189
group_values[group_idx].push(value);
123190
}
191+
self.buffer.clear();
124192
self.num_groups = 0;
125193
} else {
126-
let mut remaining = HashSet::default();
127-
for (group_idx, value) in self.seen.drain() {
194+
let mut remaining = Vec::new();
195+
for &packed in &self.buffer {
196+
let (group_idx, value) = T::Native::unpack(packed);
128197
if group_idx < num_emitted {
129198
group_values[group_idx].push(value);
130199
} else {
131-
remaining.insert((group_idx - num_emitted, value));
200+
remaining.push(T::Native::pack(group_idx - num_emitted, value));
132201
}
133202
}
134-
self.seen = remaining;
203+
self.buffer = remaining;
135204
self.num_groups = self.num_groups.saturating_sub(num_emitted);
136205
}
137206

@@ -167,8 +236,9 @@ where
167236
for (row_idx, group_idx) in group_indices.iter().enumerate() {
168237
let inner = list_array.value(row_idx);
169238
let inner_arr = inner.as_primitive::<T>();
239+
self.buffer.reserve(inner_arr.len());
170240
for value in inner_arr.values().iter() {
171-
self.seen.insert((*group_idx, *value));
241+
self.buffer.push(T::Native::pack(*group_idx, *value));
172242
}
173243
}
174244

@@ -177,6 +247,6 @@ where
177247

178248
fn size(&self) -> usize {
179249
size_of::<Self>()
180-
+ self.seen.capacity() * (size_of::<(usize, T::Native)>() + size_of::<u64>())
250+
+ self.buffer.capacity() * size_of::<<T::Native as Packable>::Packed>()
181251
}
182252
}

datafusion/functions-aggregate/src/count.rs

Lines changed: 53 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,43 @@ fn get_count_accumulator(data_type: &DataType) -> Box<dyn Accumulator> {
271271
}
272272
}
273273

274+
#[cold]
275+
#[inline(never)]
276+
fn create_distinct_groups_accumulator(
277+
data_type: &DataType,
278+
) -> Result<Box<dyn GroupsAccumulator>> {
279+
match data_type {
280+
DataType::Int8 => Ok(Box::new(
281+
PrimitiveDistinctCountGroupsAccumulator::<Int8Type>::new(),
282+
)),
283+
DataType::Int16 => Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
284+
Int16Type,
285+
>::new())),
286+
DataType::Int32 => Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
287+
Int32Type,
288+
>::new())),
289+
DataType::Int64 => Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
290+
Int64Type,
291+
>::new())),
292+
DataType::UInt8 => Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
293+
UInt8Type,
294+
>::new())),
295+
DataType::UInt16 => Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
296+
UInt16Type,
297+
>::new())),
298+
DataType::UInt32 => Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
299+
UInt32Type,
300+
>::new())),
301+
DataType::UInt64 => Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
302+
UInt64Type,
303+
>::new())),
304+
_ => not_impl_err!(
305+
"GroupsAccumulator not supported for COUNT(DISTINCT) with {}",
306+
data_type
307+
),
308+
}
309+
}
310+
274311
impl AggregateUDFImpl for Count {
275312
fn name(&self) -> &str {
276313
"count"
@@ -340,69 +377,30 @@ impl AggregateUDFImpl for Count {
340377
if args.exprs.len() != 1 {
341378
return false;
342379
}
343-
if args.is_distinct {
344-
// Only support primitive integer types for now
345-
matches!(
346-
args.expr_fields[0].data_type(),
347-
DataType::Int8
348-
| DataType::Int16
349-
| DataType::Int32
350-
| DataType::Int64
351-
| DataType::UInt8
352-
| DataType::UInt16
353-
| DataType::UInt32
354-
| DataType::UInt64
355-
)
356-
} else {
357-
true
380+
if !args.is_distinct {
381+
return true;
358382
}
383+
matches!(
384+
args.expr_fields[0].data_type(),
385+
DataType::Int8
386+
| DataType::Int16
387+
| DataType::Int32
388+
| DataType::Int64
389+
| DataType::UInt8
390+
| DataType::UInt16
391+
| DataType::UInt32
392+
| DataType::UInt64
393+
)
359394
}
360395

361396
fn create_groups_accumulator(
362397
&self,
363398
args: AccumulatorArgs,
364399
) -> Result<Box<dyn GroupsAccumulator>> {
365-
if args.is_distinct {
366-
let data_type = args.expr_fields[0].data_type();
367-
return match data_type {
368-
DataType::Int8 => Ok(Box::new(
369-
PrimitiveDistinctCountGroupsAccumulator::<Int8Type>::new(),
370-
)),
371-
DataType::Int16 => Ok(Box::new(
372-
PrimitiveDistinctCountGroupsAccumulator::<Int16Type>::new(),
373-
)),
374-
DataType::Int32 => Ok(Box::new(
375-
PrimitiveDistinctCountGroupsAccumulator::<Int32Type>::new(),
376-
)),
377-
DataType::Int64 => Ok(Box::new(
378-
PrimitiveDistinctCountGroupsAccumulator::<Int64Type>::new(),
379-
)),
380-
DataType::UInt8 => Ok(Box::new(
381-
PrimitiveDistinctCountGroupsAccumulator::<UInt8Type>::new(),
382-
)),
383-
DataType::UInt16 => {
384-
Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
385-
UInt16Type,
386-
>::new()))
387-
}
388-
DataType::UInt32 => {
389-
Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
390-
UInt32Type,
391-
>::new()))
392-
}
393-
DataType::UInt64 => {
394-
Ok(Box::new(PrimitiveDistinctCountGroupsAccumulator::<
395-
UInt64Type,
396-
>::new()))
397-
}
398-
_ => not_impl_err!(
399-
"GroupsAccumulator not supported for COUNT(DISTINCT) with {}",
400-
data_type
401-
),
402-
};
400+
if !args.is_distinct {
401+
return Ok(Box::new(CountGroupsAccumulator::new()));
403402
}
404-
// instantiate specialized accumulator
405-
Ok(Box::new(CountGroupsAccumulator::new()))
403+
create_distinct_groups_accumulator(args.expr_fields[0].data_type())
406404
}
407405

408406
fn reverse_expr(&self) -> ReversedUDAF {

0 commit comments

Comments
 (0)