diff --git a/.github/workflows/arrow.yml b/.github/workflows/arrow.yml index da56c23b5cd9..9b41debc636d 100644 --- a/.github/workflows/arrow.yml +++ b/.github/workflows/arrow.yml @@ -67,8 +67,8 @@ jobs: run: cargo test -p arrow-data --all-features - name: Test arrow-schema with all features run: cargo test -p arrow-schema --all-features - - name: Test arrow-array with all features except SIMD - run: cargo test -p arrow-array + - name: Test arrow-array with all features + run: cargo test -p arrow-array --all-features - name: Test arrow-select with all features run: cargo test -p arrow-select --all-features - name: Test arrow-cast with all features @@ -85,16 +85,16 @@ jobs: run: cargo test -p arrow-string --all-features - name: Test arrow-ord with all features run: cargo test -p arrow-ord --all-features - - name: Test arrow-arith with all features except SIMD - run: cargo test -p arrow-arith + - name: Test arrow-arith with all features + run: cargo test -p arrow-arith --all-features - name: Test arrow-row with all features run: cargo test -p arrow-row --all-features - name: Test arrow-integration-test with all features run: cargo test -p arrow-integration-test --all-features - name: Test arrow with default features run: cargo test -p arrow - - name: Test arrow with all features apart from simd - run: cargo test -p arrow --features=force_validate,prettyprint,ipc_compression,ffi,chrono-tz + - name: Test arrow with all features + run: cargo test -p arrow --all-features - name: Run examples run: | # Test arrow examples @@ -132,29 +132,6 @@ jobs: - name: Check compilation --no-default-features --all-targets --features chrono-tz run: cargo check -p arrow --no-default-features --all-targets --features chrono-tz - # test the --features "simd" of the arrow crate. This requires nightly Rust. - linux-test-simd: - name: Test SIMD on AMD64 Rust ${{ matrix.rust }} - runs-on: ubuntu-latest - container: - image: amd64/rust - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - name: Setup Rust toolchain - uses: ./.github/actions/setup-builder - with: - rust-version: nightly - - name: Test arrow-array with SIMD - run: cargo test -p arrow-array --features simd - - name: Test arrow-arith with SIMD - run: cargo test -p arrow-arith --features simd - - name: Test arrow with SIMD - run: cargo test -p arrow --features simd - - name: Check compilation --features simd --all-targets - run: cargo check -p arrow --features simd --all-targets - # test the arrow crate builds against wasm32 in nightly rust wasm32-build: @@ -169,12 +146,11 @@ jobs: - name: Setup Rust toolchain uses: ./.github/actions/setup-builder with: - rust-version: nightly target: wasm32-unknown-unknown,wasm32-wasi - name: Build wasm32-unknown-unknown - run: cargo build -p arrow --no-default-features --features=json,csv,ipc,simd,ffi --target wasm32-unknown-unknown + run: cargo build -p arrow --no-default-features --all-features --target wasm32-unknown-unknown - name: Build wasm32-wasi - run: cargo build -p arrow --no-default-features --features=json,csv,ipc,simd,ffi --target wasm32-wasi + run: cargo build -p arrow --no-default-features --all-features --target wasm32-wasi clippy: name: Clippy @@ -193,8 +169,8 @@ jobs: run: cargo clippy -p arrow-data --all-targets --all-features -- -D warnings - name: Clippy arrow-schema with all features run: cargo clippy -p arrow-schema --all-targets --all-features -- -D warnings - - name: Clippy arrow-array with all features except SIMD - run: cargo clippy -p arrow-array --all-targets -- -D warnings + - name: Clippy arrow-array with all features + run: cargo clippy -p arrow-array --all-targets --all-features -- -D warnings - name: Clippy arrow-select with all features run: cargo clippy -p arrow-select --all-targets --all-features -- -D warnings - name: Clippy arrow-cast with all features @@ -211,12 +187,12 @@ jobs: run: cargo clippy -p arrow-string --all-targets --all-features -- -D warnings - name: Clippy arrow-ord with all features run: cargo clippy -p arrow-ord --all-targets --all-features -- -D warnings - - name: Clippy arrow-arith with all features except SIMD - run: cargo clippy -p arrow-arith --all-targets -- -D warnings + - name: Clippy arrow-arith with all features + run: cargo clippy -p arrow-arith --all-targets --all-features -- -D warnings - name: Clippy arrow-row with all features run: cargo clippy -p arrow-row --all-targets --all-features -- -D warnings - - name: Clippy arrow with all features except SIMD - run: cargo clippy -p arrow --features=prettyprint,csv,ipc,test_utils,ffi,ipc_compression,chrono-tz --all-targets -- -D warnings + - name: Clippy arrow with all features + run: cargo clippy -p arrow --all-features --all-targets -- -D warnings - name: Clippy arrow-integration-test with all features run: cargo clippy -p arrow-integration-test --all-targets --all-features -- -D warnings - name: Clippy arrow-integration-testing with all features diff --git a/.github/workflows/miri.sh b/.github/workflows/miri.sh index ec8712660c74..5057c876b952 100755 --- a/.github/workflows/miri.sh +++ b/.github/workflows/miri.sh @@ -14,5 +14,5 @@ cargo miri test -p arrow-buffer cargo miri test -p arrow-data --features ffi cargo miri test -p arrow-schema --features ffi cargo miri test -p arrow-array -cargo miri test -p arrow-arith --features simd +cargo miri test -p arrow-arith cargo miri test -p arrow-ord diff --git a/arrow-arith/Cargo.toml b/arrow-arith/Cargo.toml index 57dc033e9645..d2ee0b9e2c72 100644 --- a/arrow-arith/Cargo.toml +++ b/arrow-arith/Cargo.toml @@ -43,6 +43,3 @@ half = { version = "2.1", default-features = false } num = { version = "0.4", default-features = false, features = ["std"] } [dev-dependencies] - -[features] -simd = ["arrow-array/simd"] diff --git a/arrow-array/Cargo.toml b/arrow-array/Cargo.toml index 4f7ab24f9708..04eec8df6379 100644 --- a/arrow-array/Cargo.toml +++ b/arrow-array/Cargo.toml @@ -49,10 +49,6 @@ chrono-tz = { version = "0.8", optional = true } num = { version = "0.4.1", default-features = false, features = ["std"] } half = { version = "2.1", default-features = false, features = ["num-traits"] } hashbrown = { version = "0.14", default-features = false } -packed_simd = { version = "0.3.9", default-features = false, optional = true } - -[features] -simd = ["packed_simd"] [dev-dependencies] rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } diff --git a/arrow-array/src/numeric.rs b/arrow-array/src/numeric.rs index b5e474ba696a..30d9a7b56182 100644 --- a/arrow-array/src/numeric.rs +++ b/arrow-array/src/numeric.rs @@ -17,619 +17,8 @@ use crate::types::*; use crate::ArrowPrimitiveType; -#[cfg(feature = "simd")] -use packed_simd::*; -#[cfg(feature = "simd")] -use std::ops::{Add, BitAnd, BitAndAssign, BitOr, BitOrAssign, Div, Mul, Not, Rem, Sub}; /// A subtype of primitive type that represents numeric values. -/// -/// SIMD operations are defined in this trait if available on the target system. -#[cfg(feature = "simd")] -pub trait ArrowNumericType: ArrowPrimitiveType -where - Self::Simd: Add - + Sub - + Mul - + Div - + Rem - + Copy, - Self::SimdMask: BitAnd - + BitOr - + BitAndAssign - + BitOrAssign - + Not - + Copy, -{ - /// Defines the SIMD type that should be used for this numeric type - type Simd; - - /// Defines the SIMD Mask type that should be used for this numeric type - type SimdMask; - - /// The number of SIMD lanes available - fn lanes() -> usize; - - /// Initializes a SIMD register to a constant value - fn init(value: Self::Native) -> Self::Simd; - - /// Loads a slice into a SIMD register - fn load(slice: &[Self::Native]) -> Self::Simd; - - /// Creates a new SIMD mask for this SIMD type filling it with `value` - fn mask_init(value: bool) -> Self::SimdMask; - - /// Creates a new SIMD mask for this SIMD type from the lower-most bits of the given `mask`. - /// The number of bits used corresponds to the number of lanes of this type - fn mask_from_u64(mask: u64) -> Self::SimdMask; - - /// Creates a bitmask from the given SIMD mask. - /// Each bit corresponds to one vector lane, starting with the least-significant bit. - fn mask_to_u64(mask: &Self::SimdMask) -> u64; - - /// Gets the value of a single lane in a SIMD mask - fn mask_get(mask: &Self::SimdMask, idx: usize) -> bool; - - /// Sets the value of a single lane of a SIMD mask - fn mask_set(mask: Self::SimdMask, idx: usize, value: bool) -> Self::SimdMask; - - /// Selects elements of `a` and `b` using `mask` - fn mask_select(mask: Self::SimdMask, a: Self::Simd, b: Self::Simd) -> Self::Simd; - - /// Returns `true` if any of the lanes in the mask are `true` - fn mask_any(mask: Self::SimdMask) -> bool; - - /// Performs a SIMD binary operation - fn bin_op Self::Simd>( - left: Self::Simd, - right: Self::Simd, - op: F, - ) -> Self::Simd; - - /// SIMD version of equal - fn eq(left: Self::Simd, right: Self::Simd) -> Self::SimdMask; - - /// SIMD version of not equal - fn ne(left: Self::Simd, right: Self::Simd) -> Self::SimdMask; - - /// SIMD version of less than - fn lt(left: Self::Simd, right: Self::Simd) -> Self::SimdMask; - - /// SIMD version of less than or equal to - fn le(left: Self::Simd, right: Self::Simd) -> Self::SimdMask; - - /// SIMD version of greater than - fn gt(left: Self::Simd, right: Self::Simd) -> Self::SimdMask; - - /// SIMD version of greater than or equal to - fn ge(left: Self::Simd, right: Self::Simd) -> Self::SimdMask; - - /// Writes a SIMD result back to a slice - fn write(simd_result: Self::Simd, slice: &mut [Self::Native]); - - /// Performs a SIMD unary operation - fn unary_op Self::Simd>(a: Self::Simd, op: F) -> Self::Simd; -} - -/// A subtype of primitive type that represents numeric values. -#[cfg(not(feature = "simd"))] pub trait ArrowNumericType: ArrowPrimitiveType {} -macro_rules! make_numeric_type { - ($impl_ty:ty, $native_ty:ty, $simd_ty:ident, $simd_mask_ty:ident) => { - #[cfg(feature = "simd")] - impl ArrowNumericType for $impl_ty { - type Simd = $simd_ty; - - type SimdMask = $simd_mask_ty; - - #[inline] - fn lanes() -> usize { - Self::Simd::lanes() - } - - #[inline] - fn init(value: Self::Native) -> Self::Simd { - Self::Simd::splat(value) - } - - #[inline] - fn load(slice: &[Self::Native]) -> Self::Simd { - unsafe { Self::Simd::from_slice_unaligned_unchecked(slice) } - } - - #[inline] - fn mask_init(value: bool) -> Self::SimdMask { - Self::SimdMask::splat(value) - } - - #[inline] - fn mask_from_u64(mask: u64) -> Self::SimdMask { - // this match will get removed by the compiler since the number of lanes is known at - // compile-time for each concrete numeric type - match Self::lanes() { - 4 => { - // the bit position in each lane indicates the index of that lane - let vecidx = i128x4::new(1, 2, 4, 8); - - // broadcast the lowermost 8 bits of mask to each lane - let vecmask = i128x4::splat((mask & 0x0F) as i128); - // compute whether the bit corresponding to each lanes index is set - let vecmask = (vecidx & vecmask).eq(vecidx); - - // transmute is necessary because the different match arms return different - // mask types, at runtime only one of those expressions will exist per type, - // with the type being equal to `SimdMask`. - unsafe { std::mem::transmute(vecmask) } - } - 8 => { - // the bit position in each lane indicates the index of that lane - let vecidx = i64x8::new(1, 2, 4, 8, 16, 32, 64, 128); - - // broadcast the lowermost 8 bits of mask to each lane - let vecmask = i64x8::splat((mask & 0xFF) as i64); - // compute whether the bit corresponding to each lanes index is set - let vecmask = (vecidx & vecmask).eq(vecidx); - - // transmute is necessary because the different match arms return different - // mask types, at runtime only one of those expressions will exist per type, - // with the type being equal to `SimdMask`. - unsafe { std::mem::transmute(vecmask) } - } - 16 => { - // same general logic as for 8 lanes, extended to 16 bits - let vecidx = i32x16::new( - 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, - 32768, - ); - - let vecmask = i32x16::splat((mask & 0xFFFF) as i32); - let vecmask = (vecidx & vecmask).eq(vecidx); - - unsafe { std::mem::transmute(vecmask) } - } - 32 => { - // compute two separate m32x16 vector masks from from the lower-most 32 bits of `mask` - // and then combine them into one m16x32 vector mask by writing and reading a temporary - let tmp = &mut [0_i16; 32]; - - let vecidx = i32x16::new( - 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, - 32768, - ); - - let vecmask = i32x16::splat((mask & 0xFFFF) as i32); - let vecmask = (vecidx & vecmask).eq(vecidx); - - i16x16::from_cast(vecmask).write_to_slice_unaligned(&mut tmp[0..16]); - - let vecmask = i32x16::splat(((mask >> 16) & 0xFFFF) as i32); - let vecmask = (vecidx & vecmask).eq(vecidx); - - i16x16::from_cast(vecmask).write_to_slice_unaligned(&mut tmp[16..32]); - - unsafe { std::mem::transmute(i16x32::from_slice_unaligned(tmp)) } - } - 64 => { - // compute four m32x16 vector masks from from all 64 bits of `mask` - // and convert them into one m8x64 vector mask by writing and reading a temporary - let tmp = &mut [0_i8; 64]; - - let vecidx = i32x16::new( - 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, - 32768, - ); - - let vecmask = i32x16::splat((mask & 0xFFFF) as i32); - let vecmask = (vecidx & vecmask).eq(vecidx); - - i8x16::from_cast(vecmask).write_to_slice_unaligned(&mut tmp[0..16]); - - let vecmask = i32x16::splat(((mask >> 16) & 0xFFFF) as i32); - let vecmask = (vecidx & vecmask).eq(vecidx); - - i8x16::from_cast(vecmask).write_to_slice_unaligned(&mut tmp[16..32]); - - let vecmask = i32x16::splat(((mask >> 32) & 0xFFFF) as i32); - let vecmask = (vecidx & vecmask).eq(vecidx); - - i8x16::from_cast(vecmask).write_to_slice_unaligned(&mut tmp[32..48]); - - let vecmask = i32x16::splat(((mask >> 48) & 0xFFFF) as i32); - let vecmask = (vecidx & vecmask).eq(vecidx); - - i8x16::from_cast(vecmask).write_to_slice_unaligned(&mut tmp[48..64]); - - unsafe { std::mem::transmute(i8x64::from_slice_unaligned(tmp)) } - } - _ => panic!("Invalid number of vector lanes"), - } - } - - #[inline] - fn mask_to_u64(mask: &Self::SimdMask) -> u64 { - mask.bitmask() as u64 - } - - #[inline] - fn mask_get(mask: &Self::SimdMask, idx: usize) -> bool { - unsafe { mask.extract_unchecked(idx) } - } - - #[inline] - fn mask_set(mask: Self::SimdMask, idx: usize, value: bool) -> Self::SimdMask { - unsafe { mask.replace_unchecked(idx, value) } - } - - /// Selects elements of `a` and `b` using `mask` - #[inline] - fn mask_select(mask: Self::SimdMask, a: Self::Simd, b: Self::Simd) -> Self::Simd { - mask.select(a, b) - } - - #[inline] - fn mask_any(mask: Self::SimdMask) -> bool { - mask.any() - } - - #[inline] - fn bin_op Self::Simd>( - left: Self::Simd, - right: Self::Simd, - op: F, - ) -> Self::Simd { - op(left, right) - } - - #[inline] - fn eq(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.eq(right) - } - - #[inline] - fn ne(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.ne(right) - } - - #[inline] - fn lt(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.lt(right) - } - - #[inline] - fn le(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.le(right) - } - - #[inline] - fn gt(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.gt(right) - } - - #[inline] - fn ge(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.ge(right) - } - - #[inline] - fn write(simd_result: Self::Simd, slice: &mut [Self::Native]) { - unsafe { simd_result.write_to_slice_unaligned_unchecked(slice) }; - } - - #[inline] - fn unary_op Self::Simd>(a: Self::Simd, op: F) -> Self::Simd { - op(a) - } - } - - #[cfg(not(feature = "simd"))] - impl ArrowNumericType for $impl_ty {} - }; -} - -make_numeric_type!(Int8Type, i8, i8x64, m8x64); -make_numeric_type!(Int16Type, i16, i16x32, m16x32); -make_numeric_type!(Int32Type, i32, i32x16, m32x16); -make_numeric_type!(Int64Type, i64, i64x8, m64x8); -make_numeric_type!(UInt8Type, u8, u8x64, m8x64); -make_numeric_type!(UInt16Type, u16, u16x32, m16x32); -make_numeric_type!(UInt32Type, u32, u32x16, m32x16); -make_numeric_type!(UInt64Type, u64, u64x8, m64x8); -make_numeric_type!(Float32Type, f32, f32x16, m32x16); -make_numeric_type!(Float64Type, f64, f64x8, m64x8); - -make_numeric_type!(TimestampSecondType, i64, i64x8, m64x8); -make_numeric_type!(TimestampMillisecondType, i64, i64x8, m64x8); -make_numeric_type!(TimestampMicrosecondType, i64, i64x8, m64x8); -make_numeric_type!(TimestampNanosecondType, i64, i64x8, m64x8); -make_numeric_type!(Date32Type, i32, i32x16, m32x16); -make_numeric_type!(Date64Type, i64, i64x8, m64x8); -make_numeric_type!(Time32SecondType, i32, i32x16, m32x16); -make_numeric_type!(Time32MillisecondType, i32, i32x16, m32x16); -make_numeric_type!(Time64MicrosecondType, i64, i64x8, m64x8); -make_numeric_type!(Time64NanosecondType, i64, i64x8, m64x8); -make_numeric_type!(IntervalYearMonthType, i32, i32x16, m32x16); -make_numeric_type!(IntervalDayTimeType, i64, i64x8, m64x8); -make_numeric_type!(IntervalMonthDayNanoType, i128, i128x4, m128x4); -make_numeric_type!(DurationSecondType, i64, i64x8, m64x8); -make_numeric_type!(DurationMillisecondType, i64, i64x8, m64x8); -make_numeric_type!(DurationMicrosecondType, i64, i64x8, m64x8); -make_numeric_type!(DurationNanosecondType, i64, i64x8, m64x8); -make_numeric_type!(Decimal128Type, i128, i128x4, m128x4); - -#[cfg(not(feature = "simd"))] -impl ArrowNumericType for Float16Type {} - -#[cfg(feature = "simd")] -impl ArrowNumericType for Float16Type { - type Simd = ::Simd; - type SimdMask = ::SimdMask; - - fn lanes() -> usize { - Float32Type::lanes() - } - - fn init(value: Self::Native) -> Self::Simd { - Float32Type::init(value.to_f32()) - } - - fn load(slice: &[Self::Native]) -> Self::Simd { - let mut s = [0_f32; Self::Simd::lanes()]; - s.iter_mut().zip(slice).for_each(|(o, a)| *o = a.to_f32()); - Float32Type::load(&s) - } - - fn mask_init(value: bool) -> Self::SimdMask { - Float32Type::mask_init(value) - } - - fn mask_from_u64(mask: u64) -> Self::SimdMask { - Float32Type::mask_from_u64(mask) - } - - fn mask_to_u64(mask: &Self::SimdMask) -> u64 { - Float32Type::mask_to_u64(mask) - } - - fn mask_get(mask: &Self::SimdMask, idx: usize) -> bool { - Float32Type::mask_get(mask, idx) - } - - fn mask_set(mask: Self::SimdMask, idx: usize, value: bool) -> Self::SimdMask { - Float32Type::mask_set(mask, idx, value) - } - - fn mask_select(mask: Self::SimdMask, a: Self::Simd, b: Self::Simd) -> Self::Simd { - Float32Type::mask_select(mask, a, b) - } - - fn mask_any(mask: Self::SimdMask) -> bool { - Float32Type::mask_any(mask) - } - - fn bin_op Self::Simd>( - left: Self::Simd, - right: Self::Simd, - op: F, - ) -> Self::Simd { - op(left, right) - } - - fn eq(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - Float32Type::eq(left, right) - } - - fn ne(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - Float32Type::ne(left, right) - } - - fn lt(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - Float32Type::lt(left, right) - } - - fn le(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - Float32Type::le(left, right) - } - - fn gt(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - Float32Type::gt(left, right) - } - - fn ge(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - Float32Type::ge(left, right) - } - - fn write(simd_result: Self::Simd, slice: &mut [Self::Native]) { - let mut s = [0_f32; Self::Simd::lanes()]; - Float32Type::write(simd_result, &mut s); - slice - .iter_mut() - .zip(s) - .for_each(|(o, i)| *o = half::f16::from_f32(i)) - } - - fn unary_op Self::Simd>(a: Self::Simd, op: F) -> Self::Simd { - Float32Type::unary_op(a, op) - } -} - -#[cfg(not(feature = "simd"))] -impl ArrowNumericType for Decimal256Type {} - -#[cfg(feature = "simd")] -impl ArrowNumericType for Decimal256Type { - type Simd = arrow_buffer::i256; - type SimdMask = bool; - - fn lanes() -> usize { - 1 - } - - fn init(value: Self::Native) -> Self::Simd { - value - } - - fn load(slice: &[Self::Native]) -> Self::Simd { - slice[0] - } - - fn mask_init(value: bool) -> Self::SimdMask { - value - } - - fn mask_from_u64(mask: u64) -> Self::SimdMask { - mask != 0 - } - - fn mask_to_u64(mask: &Self::SimdMask) -> u64 { - *mask as u64 - } - - fn mask_get(mask: &Self::SimdMask, _idx: usize) -> bool { - *mask - } - - fn mask_set(_mask: Self::SimdMask, _idx: usize, value: bool) -> Self::SimdMask { - value - } - - fn mask_select(mask: Self::SimdMask, a: Self::Simd, b: Self::Simd) -> Self::Simd { - match mask { - true => a, - false => b, - } - } - - fn mask_any(mask: Self::SimdMask) -> bool { - mask - } - - fn bin_op Self::Simd>( - left: Self::Simd, - right: Self::Simd, - op: F, - ) -> Self::Simd { - op(left, right) - } - - fn eq(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.eq(&right) - } - - fn ne(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.ne(&right) - } - - fn lt(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.lt(&right) - } - - fn le(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.le(&right) - } - - fn gt(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.gt(&right) - } - - fn ge(left: Self::Simd, right: Self::Simd) -> Self::SimdMask { - left.ge(&right) - } - - fn write(simd_result: Self::Simd, slice: &mut [Self::Native]) { - slice[0] = simd_result - } - - fn unary_op Self::Simd>(a: Self::Simd, op: F) -> Self::Simd { - op(a) - } -} - -#[cfg(all(test, feature = "simd"))] -mod tests { - use super::*; - use FromCast; - - /// calculate the expected mask by iterating over all bits - macro_rules! expected_mask { - ($T:ty, $MASK:expr) => {{ - let mask = $MASK; - // simd width of all types is currently 64 bytes -> 512 bits - let lanes = 64 / std::mem::size_of::<$T>(); - // translate each set bit into a value of all ones (-1) of the correct type - (0..lanes) - .map(|i| (if (mask & (1 << i)) != 0 { -1 } else { 0 })) - .collect::>() - }}; - } - - #[test] - fn test_mask_i128() { - let mask = 0b1101; - let actual = IntervalMonthDayNanoType::mask_from_u64(mask); - let expected = expected_mask!(i128, mask); - let expected = m128x4::from_cast(i128x4::from_slice_unaligned(expected.as_slice())); - - assert_eq!(expected, actual); - } - - #[test] - fn test_mask_f64() { - let mask = 0b10101010; - let actual = Float64Type::mask_from_u64(mask); - let expected = expected_mask!(i64, mask); - let expected = m64x8::from_cast(i64x8::from_slice_unaligned(expected.as_slice())); - - assert_eq!(expected, actual); - } - - #[test] - fn test_mask_u64() { - let mask = 0b01010101; - let actual = Int64Type::mask_from_u64(mask); - let expected = expected_mask!(i64, mask); - let expected = m64x8::from_cast(i64x8::from_slice_unaligned(expected.as_slice())); - - assert_eq!(expected, actual); - } - - #[test] - fn test_mask_f32() { - let mask = 0b10101010_10101010; - let actual = Float32Type::mask_from_u64(mask); - let expected = expected_mask!(i32, mask); - let expected = m32x16::from_cast(i32x16::from_slice_unaligned(expected.as_slice())); - - assert_eq!(expected, actual); - } - - #[test] - fn test_mask_i32() { - let mask = 0b01010101_01010101; - let actual = Int32Type::mask_from_u64(mask); - let expected = expected_mask!(i32, mask); - let expected = m32x16::from_cast(i32x16::from_slice_unaligned(expected.as_slice())); - - assert_eq!(expected, actual); - } - - #[test] - fn test_mask_u16() { - let mask = 0b01010101_01010101_10101010_10101010; - let actual = UInt16Type::mask_from_u64(mask); - let expected = expected_mask!(i16, mask); - let expected = m16x32::from_cast(i16x32::from_slice_unaligned(expected.as_slice())); - - assert_eq!(expected, actual); - } - - #[test] - fn test_mask_i8() { - let mask = 0b01010101_01010101_10101010_10101010_01010101_01010101_10101010_10101010; - let actual = Int8Type::mask_from_u64(mask); - let expected = expected_mask!(i8, mask); - let expected = m8x64::from_cast(i8x64::from_slice_unaligned(expected.as_slice())); - - assert_eq!(expected, actual); - } -} +impl ArrowNumericType for T {} diff --git a/arrow-buffer/src/util/bit_util.rs b/arrow-buffer/src/util/bit_util.rs index b27931f4cc85..36e08e4e7681 100644 --- a/arrow-buffer/src/util/bit_util.rs +++ b/arrow-buffer/src/util/bit_util.rs @@ -17,9 +17,6 @@ //! Utils for working with bits -#[cfg(feature = "simd")] -use packed_simd::u8x64; - const BIT_MASK: [u8; 8] = [1, 2, 4, 8, 16, 32, 64, 128]; const UNSET_BIT_MASK: [u8; 8] = [ 255 - 1, @@ -104,31 +101,13 @@ pub fn ceil(value: usize, divisor: usize) -> usize { value / divisor + (0 != value % divisor) as usize } -/// Performs SIMD bitwise binary operations. -/// -/// # Safety -/// -/// Note that each slice should be 64 bytes and it is the callers responsibility to ensure -/// that this is the case. If passed slices larger than 64 bytes the operation will only -/// be performed on the first 64 bytes. Slices less than 64 bytes will panic. -#[cfg(feature = "simd")] -pub unsafe fn bitwise_bin_op_simd(left: &[u8], right: &[u8], result: &mut [u8], op: F) -where - F: Fn(u8x64, u8x64) -> u8x64, -{ - let left_simd = u8x64::from_slice_unaligned_unchecked(left); - let right_simd = u8x64::from_slice_unaligned_unchecked(right); - let simd_result = op(left_simd, right_simd); - simd_result.write_to_slice_unaligned_unchecked(result); -} - -#[cfg(all(test, feature = "test_utils"))] +#[cfg(test)] mod tests { use std::collections::HashSet; use super::*; - use crate::util::test_util::seedable_rng; - use rand::Rng; + use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; #[test] fn test_round_upto_multiple_of_64() { @@ -167,6 +146,10 @@ mod tests { assert!(!get_bit(&[0b01001001, 0b01010010], 15)); } + pub fn seedable_rng() -> StdRng { + StdRng::seed_from_u64(42) + } + #[test] fn test_get_bit_raw() { const NUM_BYTE: usize = 10; @@ -278,7 +261,6 @@ mod tests { } #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_ceil() { assert_eq!(ceil(0, 1), 0); assert_eq!(ceil(1, 1), 1); @@ -292,28 +274,4 @@ mod tests { assert_eq!(ceil(10, 10000000000), 1); assert_eq!(ceil(10000000000, 1000000000), 10); } - - #[test] - #[cfg(feature = "simd")] - fn test_bitwise_and_simd() { - let buf1 = [0b00110011u8; 64]; - let buf2 = [0b11110000u8; 64]; - let mut buf3 = [0b00000000; 64]; - unsafe { bitwise_bin_op_simd(&buf1, &buf2, &mut buf3, |a, b| a & b) }; - for i in buf3.iter() { - assert_eq!(&0b00110000u8, i); - } - } - - #[test] - #[cfg(feature = "simd")] - fn test_bitwise_or_simd() { - let buf1 = [0b00110011u8; 64]; - let buf2 = [0b11110000u8; 64]; - let mut buf3 = [0b00000000; 64]; - unsafe { bitwise_bin_op_simd(&buf1, &buf2, &mut buf3, |a, b| a | b) }; - for i in buf3.iter() { - assert_eq!(&0b11110011u8, i); - } - } } diff --git a/arrow-ord/src/comparison.rs b/arrow-ord/src/comparison.rs index 021ecdf0e658..eb3181cc7e95 100644 --- a/arrow-ord/src/comparison.rs +++ b/arrow-ord/src/comparison.rs @@ -271,36 +271,6 @@ where Ok(BooleanArray::from_unary(left, op)) } -/// Evaluate `op(left, right)` for [`PrimitiveArray`]s using a specified -/// comparison function. -#[deprecated(note = "Use BooleanArray::from_binary")] -pub fn no_simd_compare_op( - left: &PrimitiveArray, - right: &PrimitiveArray, - op: F, -) -> Result -where - T: ArrowPrimitiveType, - F: Fn(T::Native, T::Native) -> bool, -{ - compare_op(left, right, op) -} - -/// Evaluate `op(left, right)` for [`PrimitiveArray`] and scalar using -/// a specified comparison function. -#[deprecated(note = "Use BooleanArray::from_unary")] -pub fn no_simd_compare_op_scalar( - left: &PrimitiveArray, - right: T::Native, - op: F, -) -> Result -where - T: ArrowPrimitiveType, - F: Fn(T::Native, T::Native) -> bool, -{ - compare_op_scalar(left, |l| op(l, right)) -} - /// Perform `left == right` operation on [`StringArray`] / [`LargeStringArray`]. #[deprecated(note = "Use arrow_ord::cmp::eq")] pub fn eq_utf8( @@ -610,7 +580,6 @@ pub fn gt_eq_utf8_scalar( /// Perform `left == right` operation on an array and a numeric scalar /// value. Supports PrimitiveArrays, and DictionaryArrays that have primitive values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary @@ -628,7 +597,6 @@ where /// Perform `left < right` operation on an array and a numeric scalar /// value. Supports PrimitiveArrays, and DictionaryArrays that have primitive values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary @@ -646,7 +614,6 @@ where /// Perform `left <= right` operation on an array and a numeric scalar /// value. Supports PrimitiveArrays, and DictionaryArrays that have primitive values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary @@ -664,7 +631,6 @@ where /// Perform `left > right` operation on an array and a numeric scalar /// value. Supports PrimitiveArrays, and DictionaryArrays that have primitive values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary @@ -682,7 +648,6 @@ where /// Perform `left >= right` operation on an array and a numeric scalar /// value. Supports PrimitiveArrays, and DictionaryArrays that have primitive values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary @@ -700,7 +665,6 @@ where /// Perform `left != right` operation on an array and a numeric scalar /// value. Supports PrimitiveArrays, and DictionaryArrays that have primitive values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary @@ -1015,7 +979,6 @@ pub fn gt_eq_dyn(left: &dyn Array, right: &dyn Array) -> Result right` operation on two [`PrimitiveArray`]s. Non-null values are greater than null /// values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary @@ -1204,7 +1160,6 @@ where /// Perform `left > right` operation on a [`PrimitiveArray`] and a scalar value. /// Non-null values are greater than null values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary @@ -1223,7 +1178,6 @@ where /// Perform `left >= right` operation on two [`PrimitiveArray`]s. Non-null values are greater than null /// values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary @@ -1244,7 +1198,6 @@ where /// Perform `left >= right` operation on a [`PrimitiveArray`] and a scalar value. /// Non-null values are greater than null values. /// -/// If `simd` feature flag is not enabled: /// For floating values like f32 and f64, this comparison produces an ordering in accordance to /// the totalOrder predicate as defined in the IEEE 754 (2008 revision) floating point standard. /// Note that totalOrder treats positive and negative zeros are different. If it is necessary diff --git a/arrow/CONTRIBUTING.md b/arrow/CONTRIBUTING.md index 5b84bc2d3bdb..0c795d6b9cbd 100644 --- a/arrow/CONTRIBUTING.md +++ b/arrow/CONTRIBUTING.md @@ -67,18 +67,6 @@ the impossibility of the compiler to derive the invariants (such as lifetime, nu The arrow format declares a IPC protocol, which this crate supports. IPC is equivalent to a FFI in that the rust compiler can't reason about the contract's invariants. -#### SIMD - -The API provided by the [packed_simd_2](https://docs.rs/packed_simd_2/latest/packed_simd_2/) crate is currently `unsafe`. However, -SIMD offers a significant performance improvement over non-SIMD operations. A related crate in development is -[portable-simd](https://rust-lang.github.io/portable-simd/core_simd/) which has a nice -[beginners guide](https://github.com/rust-lang/portable-simd/blob/master/beginners-guide.md). These crates provide the ability -for code on x86 and ARM architectures to use some of the available parallel register operations. As an example if two arrays -of numbers are added, [1,2,3,4] + [5,6,7,8], rather than using four instructions to add each of the elements of the arrays, -one instruction can be used to all all four elements at the same time, which leads to improved time to solution. SIMD instructions -are typically most effective when data is aligned to allow a single load instruction to bring multiple consecutive data elements -to the registers, before use of a SIMD instruction. - #### Performance Some operations are significantly faster when `unsafe` is used. diff --git a/arrow/Cargo.toml b/arrow/Cargo.toml index 6ca218f5f658..a6b4ddf51dfb 100644 --- a/arrow/Cargo.toml +++ b/arrow/Cargo.toml @@ -65,7 +65,6 @@ ipc_compression = ["ipc", "arrow-ipc/lz4", "arrow-ipc/zstd"] csv = ["arrow-csv"] ipc = ["arrow-ipc"] json = ["arrow-json"] -simd = ["arrow-array/simd", "arrow-arith/simd"] prettyprint = ["arrow-cast/prettyprint"] # The test utils feature enables code used in benchmarks and tests but # not the core arrow code itself. Be aware that `rand` must be kept as diff --git a/arrow/README.md b/arrow/README.md index 6a91bc951cc1..bc95b91a9a4a 100644 --- a/arrow/README.md +++ b/arrow/README.md @@ -48,9 +48,7 @@ The `arrow` crate provides the following features which may be enabled in your ` - `ipc` (default) - support for reading [Arrow IPC Format](https://arrow.apache.org/docs/format/Columnar.html#serialization-and-interprocess-communication-ipc), also used as the wire protocol in [arrow-flight](https://crates.io/crates/arrow-flight) - `ipc_compression` - Enables reading and writing compressed IPC streams (also enables `ipc`) - `prettyprint` - support for formatting record batches as textual columns -- `simd` - (_Requires Nightly Rust_) Use alternate hand optimized implementations of some [compute](https://github.com/apache/arrow-rs/tree/master/arrow/src/compute/kernels) - kernels using explicit SIMD instructions via [packed_simd_2](https://docs.rs/packed_simd_2/latest/packed_simd_2/). - `chrono-tz` - support of parsing timezone using [chrono-tz](https://docs.rs/chrono-tz/0.6.0/chrono_tz/) - `ffi` - bindings for the Arrow C [C Data Interface](https://arrow.apache.org/docs/format/CDataInterface.html) - `pyarrow` - bindings for pyo3 to call arrow-rs from python @@ -75,7 +73,6 @@ In particular there are a number of scenarios where `unsafe` is largely unavoida - Invariants that cannot be statically verified by the compiler and unlock non-trivial performance wins, e.g. values in a StringArray are UTF-8, [TrustedLen](https://doc.rust-lang.org/std/iter/trait.TrustedLen.html) iterators, etc... - FFI -- SIMD Additionally, this crate exposes a number of `unsafe` APIs, allowing downstream crates to explicitly opt-out of potentially expensive invariant checking where appropriate. @@ -95,7 +92,7 @@ In order to compile Arrow for `wasm32-unknown-unknown` you will need to disable ```toml [dependencies] -arrow = { version = "5.0", default-features = false, features = ["csv", "ipc", "simd"] } +arrow = { version = "5.0", default-features = false, features = ["csv", "ipc"] } ``` ## Examples