Skip to content

Commit e12b4f1

Browse files
authored
Improvement: Faster primitive is_constant (#2873)
1 parent 2c0825e commit e12b4f1

File tree

5 files changed

+49
-5
lines changed

5 files changed

+49
-5
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vortex-array/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ arrow-ord = { workspace = true }
2626
arrow-schema = { workspace = true }
2727
arrow-select = { workspace = true }
2828
arrow-string = { workspace = true }
29+
cfg-if = { workspace = true }
2930
enum-iterator = { workspace = true }
3031
flatbuffers = { workspace = true }
3132
flexbuffers = { workspace = true }
Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,67 @@
1+
use vortex_dtype::half::f16;
12
use vortex_dtype::{NativePType, match_each_native_ptype};
3+
use vortex_error::VortexResult;
24

35
use crate::arrays::{PrimitiveArray, PrimitiveEncoding};
46
use crate::compute::{IsConstantFn, IsConstantOpts};
57
use crate::variants::PrimitiveArrayTrait;
68

9+
cfg_if::cfg_if! {
10+
if #[cfg(target_feature = "avx2")] {
11+
const IS_CONST_LANE_WIDTH: usize = 32;
12+
} else {
13+
const IS_CONST_LANE_WIDTH: usize = 16;
14+
}
15+
}
16+
717
impl IsConstantFn<&PrimitiveArray> for PrimitiveEncoding {
818
fn is_constant(
919
&self,
1020
array: &PrimitiveArray,
1121
_opts: &IsConstantOpts,
12-
) -> vortex_error::VortexResult<Option<bool>> {
13-
let is_constant = match_each_native_ptype!(array.ptype(), |$P| {
14-
compute_is_constant(array.as_slice::<$P>())
22+
) -> VortexResult<Option<bool>> {
23+
let is_constant = match_each_native_ptype!(array.ptype(), integral: |$P| {
24+
compute_is_constant::<_, {IS_CONST_LANE_WIDTH / size_of::<$P>()}>(array.as_slice::<$P>())
25+
} floating_point: |$P| {
26+
compute_is_constant::<_, {IS_CONST_LANE_WIDTH / size_of::<$P>()}>(unsafe { std::mem::transmute::<&[$P], &[<$P as EqFloat>::IntType]>(array.as_slice::<$P>()) })
1527
});
1628

1729
Ok(Some(is_constant))
1830
}
1931
}
2032

33+
// Assumes any floating point has been cast into its bit representation for which != and !is_eq are the same
2134
// Assumes there's at least 1 value in the slice, which is an invariant of the entry level function.
22-
fn compute_is_constant<T: NativePType>(values: &[T]) -> bool {
35+
fn compute_is_constant<T: NativePType, const WIDTH: usize>(values: &[T]) -> bool {
2336
let first_value = values[0];
37+
let first_vec = &[first_value; WIDTH];
38+
39+
let mut chunks = values[1..].array_chunks::<WIDTH>();
40+
for chunk in &mut chunks {
41+
if first_vec != chunk {
42+
return false;
43+
}
44+
}
2445

25-
for value in &values[1..] {
46+
for value in chunks.remainder() {
2647
if !value.is_eq(first_value) {
2748
return false;
2849
}
2950
}
3051

3152
true
3253
}
54+
55+
trait EqFloat {
56+
type IntType;
57+
}
58+
59+
impl EqFloat for f16 {
60+
type IntType = u16;
61+
}
62+
impl EqFloat for f32 {
63+
type IntType = u32;
64+
}
65+
impl EqFloat for f64 {
66+
type IntType = u64;
67+
}

vortex-array/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![feature(portable_simd)]
33
#![feature(substr_range)]
44
#![feature(trusted_len)]
5+
#![feature(array_chunks)]
56
//! Vortex crate containing core logic for encoding and memory representation of [arrays](ArrayRef).
67
//!
78
//! At the heart of Vortex are [arrays](ArrayRef) and [encodings](vtable::EncodingVTable).

vortex-dtype/src/ptype.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,17 @@ macro_rules! native_ptype {
116116
impl NativePType for $T {
117117
const PTYPE: PType = PType::$ptype;
118118

119+
#[inline]
119120
fn is_nan(self) -> bool {
120121
false
121122
}
122123

124+
#[inline]
123125
fn total_compare(self, other: Self) -> Ordering {
124126
self.cmp(&other)
125127
}
126128

129+
#[inline]
127130
fn is_eq(self, other: Self) -> bool {
128131
self == other
129132
}
@@ -136,14 +139,17 @@ macro_rules! native_float_ptype {
136139
impl NativePType for $T {
137140
const PTYPE: PType = PType::$ptype;
138141

142+
#[inline]
139143
fn is_nan(self) -> bool {
140144
<$T>::is_nan(self)
141145
}
142146

147+
#[inline]
143148
fn total_compare(self, other: Self) -> Ordering {
144149
self.total_cmp(&other)
145150
}
146151

152+
#[inline]
147153
fn is_eq(self, other: Self) -> bool {
148154
self.to_bits() == other.to_bits()
149155
}

0 commit comments

Comments
 (0)