This is an automated email from the ASF dual-hosted git repository.

agrove pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new a65798a  ARROW-4196: [Rust] Add explicit SIMD vectorization for 
arithmetic ops in "array_ops"
a65798a is described below

commit a65798a0ed3a96cfcd765e84615321da25ae17c8
Author: Paddy Horan <[email protected]>
AuthorDate: Mon Feb 18 11:15:30 2019 -0700

    ARROW-4196: [Rust] Add explicit SIMD vectorization for arithmetic ops in 
"array_ops"
    
    Notes:
    
    I moved the `divide` kernel but did not update it.  I will address this as 
part of 
[ARROW-4590](https://issues.apache.org/jira/browse/ARROW-4590?filter=-1) as I 
need to check for zero.  Up to this point I could ignore this and read/write to 
the padded region.
    
    I tried to define a `ArrowSIMDType` trait but I felt it was worse than 
conditionally compiling the SIMD methods into `ArrowNumericType`.
    
    Author: Paddy Horan <[email protected]>
    
    Closes #3680 from paddyhoran/arithmetic-kernels and squashes the following 
commits:
    
    d447bda <Paddy Horan> Fixed code comment lints
    5e0bbd1 <Paddy Horan> Added benchmark for `arithmetic_kernels`
    0f90968 <Paddy Horan> Added `arithmetic_kernels`
    577d9c2 <Paddy Horan> Add SIMD associated type to `ArrowNumericType`
---
 rust/arrow/Cargo.toml                        |   4 +
 rust/arrow/benches/arithmetic_kernels.rs     |  80 +++++++
 rust/arrow/src/compute/arithmetic_kernels.rs | 311 +++++++++++++++++++++++++++
 rust/arrow/src/compute/array_ops.rs          | 203 +----------------
 rust/arrow/src/compute/boolean_kernels.rs    |   6 +-
 rust/arrow/src/compute/mod.rs                |   2 +
 rust/arrow/src/datatypes.rs                  |  91 ++++++--
 rust/arrow/src/util/bit_util.rs              |   6 +-
 rust/datafusion/examples/csv_sql.rs          |   3 +-
 rust/datafusion/src/dfparser.rs              |   4 +-
 rust/datafusion/src/execution/aggregate.rs   |  21 +-
 rust/datafusion/src/execution/expression.rs  |  13 +-
 rust/datafusion/src/execution/filter.rs      |   3 +-
 rust/datafusion/src/lib.rs                   |   4 +-
 rust/datafusion/src/logicalplan.rs           |   4 +-
 rust/datafusion/src/sqlplanner.rs            |   6 +-
 16 files changed, 517 insertions(+), 244 deletions(-)

diff --git a/rust/arrow/Cargo.toml b/rust/arrow/Cargo.toml
index 04e8ac0..ae09ee8 100644
--- a/rust/arrow/Cargo.toml
+++ b/rust/arrow/Cargo.toml
@@ -62,3 +62,7 @@ harness = false
 [[bench]]
 name = "boolean_kernels"
 harness = false
+
+[[bench]]
+name = "arithmetic_kernels"
+harness = false
diff --git a/rust/arrow/benches/arithmetic_kernels.rs 
b/rust/arrow/benches/arithmetic_kernels.rs
new file mode 100644
index 0000000..2271bd5
--- /dev/null
+++ b/rust/arrow/benches/arithmetic_kernels.rs
@@ -0,0 +1,80 @@
+// 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.
+
+#[macro_use]
+extern crate criterion;
+use criterion::Criterion;
+
+extern crate arrow;
+
+use arrow::array::*;
+use arrow::builder::*;
+use arrow::compute::arithmetic_kernels::*;
+use arrow::error::Result;
+
+fn create_array(size: usize) -> Float32Array {
+    let mut builder = Float32Builder::new(size);
+    for _i in 0..size {
+        builder.append_value(1.0).unwrap();
+    }
+    builder.finish()
+}
+
+fn bin_op_no_simd<F>(size: usize, op: F)
+where
+    F: Fn(f32, f32) -> Result<f32>,
+{
+    let arr_a = create_array(size);
+    let arr_b = create_array(size);
+    criterion::black_box(math_op(&arr_a, &arr_b, op).unwrap());
+}
+
+fn add_simd(size: usize) {
+    let arr_a = create_array(size);
+    let arr_b = create_array(size);
+    criterion::black_box(add(&arr_a, &arr_b).unwrap());
+}
+
+fn subtract_simd(size: usize) {
+    let arr_a = create_array(size);
+    let arr_b = create_array(size);
+    criterion::black_box(subtract(&arr_a, &arr_b).unwrap());
+}
+
+fn multiply_simd(size: usize) {
+    let arr_a = create_array(size);
+    let arr_b = create_array(size);
+    criterion::black_box(multiply(&arr_a, &arr_b).unwrap());
+}
+
+fn add_benchmark(c: &mut Criterion) {
+    c.bench_function("add 512", |b| {
+        b.iter(|| bin_op_no_simd(512, |a, b| Ok(a + b)))
+    });
+    c.bench_function("add 512 simd", |b| b.iter(|| add_simd(512)));
+    c.bench_function("subtract 512", |b| {
+        b.iter(|| bin_op_no_simd(512, |a, b| Ok(a - b)))
+    });
+    c.bench_function("subtract 512 simd", |b| b.iter(|| subtract_simd(512)));
+    c.bench_function("multiply 512", |b| {
+        b.iter(|| bin_op_no_simd(512, |a, b| Ok(a * b)))
+    });
+    c.bench_function("multiply 512 simd", |b| b.iter(|| multiply_simd(512)));
+}
+
+criterion_group!(benches, add_benchmark);
+criterion_main!(benches);
diff --git a/rust/arrow/src/compute/arithmetic_kernels.rs 
b/rust/arrow/src/compute/arithmetic_kernels.rs
new file mode 100644
index 0000000..2566002
--- /dev/null
+++ b/rust/arrow/src/compute/arithmetic_kernels.rs
@@ -0,0 +1,311 @@
+// 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.
+
+//! Defines basic arithmetic kernels for `PrimitiveArrays`.
+//!
+//! These kernels can leverage SIMD if available on your system.  Currently no 
runtime
+//! detection is provided, you should enable the specific SIMD intrinsics using
+//! `RUSTFLAGS="-C target-feature=+avx2"` for example.  See the documentation
+//! [here] (https://doc.rust-lang.org/stable/std/arch/) for more information.
+
+use std::mem;
+use std::ops::{Add, Div, Mul, Sub};
+use std::slice::from_raw_parts_mut;
+use std::sync::Arc;
+
+use num::Zero;
+
+use crate::array::*;
+use crate::array_data::ArrayData;
+use crate::buffer::MutableBuffer;
+use crate::builder::PrimitiveBuilder;
+use crate::compute::util::apply_bin_op_to_option_bitmap;
+use crate::datatypes;
+use crate::error::{ArrowError, Result};
+
+/// Helper function to perform math lambda function on values from two arrays. 
If either
+/// left or right value is null then the output value is also null, so `1 + 
null` is
+/// `null`.
+pub fn math_op<T, F>(
+    left: &PrimitiveArray<T>,
+    right: &PrimitiveArray<T>,
+    op: F,
+) -> Result<PrimitiveArray<T>>
+where
+    T: datatypes::ArrowNumericType,
+    F: Fn(T::Native, T::Native) -> Result<T::Native>,
+{
+    if left.len() != right.len() {
+        return Err(ArrowError::ComputeError(
+            "Cannot perform math operation on arrays of different 
length".to_string(),
+        ));
+    }
+    let mut b = PrimitiveBuilder::<T>::new(left.len());
+    for i in 0..left.len() {
+        let index = i;
+        if left.is_null(i) || right.is_null(i) {
+            b.append_null()?;
+        } else {
+            b.append_value(op(left.value(index), right.value(index))?)?;
+        }
+    }
+    Ok(b.finish())
+}
+
+/// SIMD vectorized version of `math_op` above.
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+fn simd_math_op<T, F>(
+    left: &PrimitiveArray<T>,
+    right: &PrimitiveArray<T>,
+    op: F,
+) -> Result<PrimitiveArray<T>>
+where
+    T: datatypes::ArrowNumericType,
+    T::Simd: Add<Output = T::Simd>
+        + Sub<Output = T::Simd>
+        + Mul<Output = T::Simd>
+        + Div<Output = T::Simd>,
+    F: Fn(T::Simd, T::Simd) -> T::Simd,
+{
+    if left.len() != right.len() {
+        return Err(ArrowError::ComputeError(
+            "Cannot perform math operation on arrays of different 
length".to_string(),
+        ));
+    }
+
+    let null_bit_buffer = apply_bin_op_to_option_bitmap(
+        left.data().null_bitmap(),
+        right.data().null_bitmap(),
+        |a, b| a & b,
+    )?;
+
+    let lanes = T::lanes();
+    let buffer_size = left.len() * mem::size_of::<T::Native>();
+    let mut result = MutableBuffer::new(buffer_size).with_bitset(buffer_size, 
false);
+
+    for i in (0..left.len()).step_by(lanes) {
+        let simd_left = T::load(left.value_slice(i, lanes));
+        let simd_right = T::load(right.value_slice(i, lanes));
+        let simd_result = T::bin_op(simd_left, simd_right, &op);
+
+        let result_slice: &mut [T::Native] = unsafe {
+            from_raw_parts_mut(
+                (result.data_mut().as_mut_ptr() as *mut T::Native).offset(i as 
isize),
+                lanes,
+            )
+        };
+        T::write(simd_result, result_slice);
+    }
+
+    let data = ArrayData::new(
+        T::get_data_type(),
+        left.len(),
+        None,
+        null_bit_buffer,
+        left.offset(),
+        vec![result.freeze()],
+        vec![],
+    );
+    Ok(PrimitiveArray::<T>::from(Arc::new(data)))
+}
+
+/// Perform `left + right` operation on two arrays. If either left or right 
value is null
+/// then the result is also null.
+pub fn add<T>(
+    left: &PrimitiveArray<T>,
+    right: &PrimitiveArray<T>,
+) -> Result<PrimitiveArray<T>>
+where
+    T: datatypes::ArrowNumericType,
+    T::Native: Add<Output = T::Native>
+        + Sub<Output = T::Native>
+        + Mul<Output = T::Native>
+        + Div<Output = T::Native>
+        + Zero,
+{
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    return simd_math_op(&left, &right, |a, b| a + b);
+
+    #[allow(unreachable_code)]
+    math_op(left, right, |a, b| Ok(a + b))
+}
+
+/// Perform `left - right` operation on two arrays. If either left or right 
value is null
+/// then the result is also null.
+pub fn subtract<T>(
+    left: &PrimitiveArray<T>,
+    right: &PrimitiveArray<T>,
+) -> Result<PrimitiveArray<T>>
+where
+    T: datatypes::ArrowNumericType,
+    T::Native: Add<Output = T::Native>
+        + Sub<Output = T::Native>
+        + Mul<Output = T::Native>
+        + Div<Output = T::Native>
+        + Zero,
+{
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    return simd_math_op(&left, &right, |a, b| a - b);
+
+    #[allow(unreachable_code)]
+    math_op(left, right, |a, b| Ok(a - b))
+}
+
+/// Perform `left * right` operation on two arrays. If either left or right 
value is null
+/// then the result is also null.
+pub fn multiply<T>(
+    left: &PrimitiveArray<T>,
+    right: &PrimitiveArray<T>,
+) -> Result<PrimitiveArray<T>>
+where
+    T: datatypes::ArrowNumericType,
+    T::Native: Add<Output = T::Native>
+        + Sub<Output = T::Native>
+        + Mul<Output = T::Native>
+        + Div<Output = T::Native>
+        + Zero,
+{
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    return simd_math_op(&left, &right, |a, b| a * b);
+
+    #[allow(unreachable_code)]
+    math_op(left, right, |a, b| Ok(a * b))
+}
+
+/// Perform `left / right` operation on two arrays. If either left or right 
value is null
+/// then the result is also null. If any right hand value is zero then the 
result of this
+/// operation will be `Err(ArrowError::DivideByZero)`.
+pub fn divide<T>(
+    left: &PrimitiveArray<T>,
+    right: &PrimitiveArray<T>,
+) -> Result<PrimitiveArray<T>>
+where
+    T: datatypes::ArrowNumericType,
+    T::Native: Add<Output = T::Native>
+        + Sub<Output = T::Native>
+        + Mul<Output = T::Native>
+        + Div<Output = T::Native>
+        + Zero,
+{
+    math_op(left, right, |a, b| {
+        if b.is_zero() {
+            Err(ArrowError::DivideByZero)
+        } else {
+            Ok(a / b)
+        }
+    })
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::array::Int32Array;
+
+    #[test]
+    fn test_primitive_array_add() {
+        let a = Int32Array::from(vec![5, 6, 7, 8, 9]);
+        let b = Int32Array::from(vec![6, 7, 8, 9, 8]);
+        let c = add(&a, &b).unwrap();
+        assert_eq!(11, c.value(0));
+        assert_eq!(13, c.value(1));
+        assert_eq!(15, c.value(2));
+        assert_eq!(17, c.value(3));
+        assert_eq!(17, c.value(4));
+    }
+
+    #[test]
+    fn test_primitive_array_add_mismatched_length() {
+        let a = Int32Array::from(vec![5, 6, 7, 8, 9]);
+        let b = Int32Array::from(vec![6, 7, 8]);
+        let e = add(&a, &b)
+            .err()
+            .expect("should have failed due to different lengths");
+        assert_eq!(
+            "ComputeError(\"Cannot perform math operation on arrays of 
different length\")",
+            format!("{:?}", e)
+        );
+    }
+
+    #[test]
+    fn test_primitive_array_subtract() {
+        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
+        let b = Int32Array::from(vec![5, 4, 3, 2, 1]);
+        let c = subtract(&a, &b).unwrap();
+        assert_eq!(-4, c.value(0));
+        assert_eq!(-2, c.value(1));
+        assert_eq!(0, c.value(2));
+        assert_eq!(2, c.value(3));
+        assert_eq!(4, c.value(4));
+    }
+
+    #[test]
+    fn test_primitive_array_multiply() {
+        let a = Int32Array::from(vec![5, 6, 7, 8, 9]);
+        let b = Int32Array::from(vec![6, 7, 8, 9, 8]);
+        let c = multiply(&a, &b).unwrap();
+        assert_eq!(30, c.value(0));
+        assert_eq!(42, c.value(1));
+        assert_eq!(56, c.value(2));
+        assert_eq!(72, c.value(3));
+        assert_eq!(72, c.value(4));
+    }
+
+    #[test]
+    fn test_primitive_array_divide() {
+        let a = Int32Array::from(vec![15, 15, 8, 1, 9]);
+        let b = Int32Array::from(vec![5, 6, 8, 9, 1]);
+        let c = divide(&a, &b).unwrap();
+        assert_eq!(3, c.value(0));
+        assert_eq!(2, c.value(1));
+        assert_eq!(1, c.value(2));
+        assert_eq!(0, c.value(3));
+        assert_eq!(9, c.value(4));
+    }
+
+    #[test]
+    fn test_primitive_array_divide_by_zero() {
+        let a = Int32Array::from(vec![15]);
+        let b = Int32Array::from(vec![0]);
+        assert_eq!(
+            ArrowError::DivideByZero,
+            divide(&a, &b).err().expect("divide by zero should fail")
+        );
+    }
+
+    #[test]
+    fn test_primitive_array_divide_f64() {
+        let a = Float64Array::from(vec![15.0, 15.0, 8.0]);
+        let b = Float64Array::from(vec![5.0, 6.0, 8.0]);
+        let c = divide(&a, &b).unwrap();
+        assert_eq!(3.0, c.value(0));
+        assert_eq!(2.5, c.value(1));
+        assert_eq!(1.0, c.value(2));
+    }
+
+    #[test]
+    fn test_primitive_array_add_with_nulls() {
+        let a = Int32Array::from(vec![Some(5), None, Some(7), None]);
+        let b = Int32Array::from(vec![None, None, Some(6), Some(7)]);
+        let c = add(&a, &b).unwrap();
+        assert_eq!(true, c.is_null(0));
+        assert_eq!(true, c.is_null(1));
+        assert_eq!(false, c.is_null(2));
+        assert_eq!(true, c.is_null(3));
+        assert_eq!(13, c.value(2));
+    }
+
+}
diff --git a/rust/arrow/src/compute/array_ops.rs 
b/rust/arrow/src/compute/array_ops.rs
index 1a2d5fa..0d6ccbe 100644
--- a/rust/arrow/src/compute/array_ops.rs
+++ b/rust/arrow/src/compute/array_ops.rs
@@ -17,120 +17,12 @@
 
 //! Defines primitive computations on arrays, e.g. addition, equality, boolean 
logic.
 
-use std::ops::{Add, Div, Mul, Sub};
-
-use num::Zero;
+use std::ops::Add;
 
 use crate::array::{Array, BooleanArray, PrimitiveArray};
-use crate::builder::PrimitiveBuilder;
-use crate::datatypes;
 use crate::datatypes::ArrowNumericType;
 use crate::error::{ArrowError, Result};
 
-/// Perform `left + right` operation on two arrays. If either left or right 
value is null
-/// then the result is also null.
-pub fn add<T>(
-    left: &PrimitiveArray<T>,
-    right: &PrimitiveArray<T>,
-) -> Result<PrimitiveArray<T>>
-where
-    T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Zero,
-{
-    math_op(left, right, |a, b| Ok(a + b))
-}
-
-/// Perform `left - right` operation on two arrays. If either left or right 
value is null
-/// then the result is also null.
-pub fn subtract<T>(
-    left: &PrimitiveArray<T>,
-    right: &PrimitiveArray<T>,
-) -> Result<PrimitiveArray<T>>
-where
-    T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Zero,
-{
-    math_op(left, right, |a, b| Ok(a - b))
-}
-
-/// Perform `left * right` operation on two arrays. If either left or right 
value is null
-/// then the result is also null.
-pub fn multiply<T>(
-    left: &PrimitiveArray<T>,
-    right: &PrimitiveArray<T>,
-) -> Result<PrimitiveArray<T>>
-where
-    T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Zero,
-{
-    math_op(left, right, |a, b| Ok(a * b))
-}
-
-/// Perform `left / right` operation on two arrays. If either left or right 
value is null
-/// then the result is also null. If any right hand value is zero then the 
result of this
-/// operation will be `Err(ArrowError::DivideByZero)`.
-pub fn divide<T>(
-    left: &PrimitiveArray<T>,
-    right: &PrimitiveArray<T>,
-) -> Result<PrimitiveArray<T>>
-where
-    T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Zero,
-{
-    math_op(left, right, |a, b| {
-        if b.is_zero() {
-            Err(ArrowError::DivideByZero)
-        } else {
-            Ok(a / b)
-        }
-    })
-}
-
-/// Helper function to perform math lambda function on values from two arrays. 
If either
-/// left or right value is null then the output value is also null, so `1 + 
null` is
-/// `null`.
-fn math_op<T, F>(
-    left: &PrimitiveArray<T>,
-    right: &PrimitiveArray<T>,
-    op: F,
-) -> Result<PrimitiveArray<T>>
-where
-    T: datatypes::ArrowNumericType,
-    F: Fn(T::Native, T::Native) -> Result<T::Native>,
-{
-    if left.len() != right.len() {
-        return Err(ArrowError::ComputeError(
-            "Cannot perform math operation on arrays of different 
length".to_string(),
-        ));
-    }
-    let mut b = PrimitiveBuilder::<T>::new(left.len());
-    for i in 0..left.len() {
-        let index = i;
-        if left.is_null(i) || right.is_null(i) {
-            b.append_null()?;
-        } else {
-            b.append_value(op(left.value(index), right.value(index))?)?;
-        }
-    }
-    Ok(b.finish())
-}
-
 /// Returns the minimum value in the array, according to the natural order.
 pub fn min<T>(array: &PrimitiveArray<T>) -> Option<T::Native>
 where
@@ -318,99 +210,6 @@ mod tests {
     use crate::array::{Float64Array, Int32Array};
 
     #[test]
-    fn test_primitive_array_add() {
-        let a = Int32Array::from(vec![5, 6, 7, 8, 9]);
-        let b = Int32Array::from(vec![6, 7, 8, 9, 8]);
-        let c = add(&a, &b).unwrap();
-        assert_eq!(11, c.value(0));
-        assert_eq!(13, c.value(1));
-        assert_eq!(15, c.value(2));
-        assert_eq!(17, c.value(3));
-        assert_eq!(17, c.value(4));
-    }
-
-    #[test]
-    fn test_primitive_array_add_mismatched_length() {
-        let a = Int32Array::from(vec![5, 6, 7, 8, 9]);
-        let b = Int32Array::from(vec![6, 7, 8]);
-        let e = add(&a, &b)
-            .err()
-            .expect("should have failed due to different lengths");
-        assert_eq!(
-            "ComputeError(\"Cannot perform math operation on arrays of 
different length\")",
-            format!("{:?}", e)
-        );
-    }
-
-    #[test]
-    fn test_primitive_array_subtract() {
-        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
-        let b = Int32Array::from(vec![5, 4, 3, 2, 1]);
-        let c = subtract(&a, &b).unwrap();
-        assert_eq!(-4, c.value(0));
-        assert_eq!(-2, c.value(1));
-        assert_eq!(0, c.value(2));
-        assert_eq!(2, c.value(3));
-        assert_eq!(4, c.value(4));
-    }
-
-    #[test]
-    fn test_primitive_array_multiply() {
-        let a = Int32Array::from(vec![5, 6, 7, 8, 9]);
-        let b = Int32Array::from(vec![6, 7, 8, 9, 8]);
-        let c = multiply(&a, &b).unwrap();
-        assert_eq!(30, c.value(0));
-        assert_eq!(42, c.value(1));
-        assert_eq!(56, c.value(2));
-        assert_eq!(72, c.value(3));
-        assert_eq!(72, c.value(4));
-    }
-
-    #[test]
-    fn test_primitive_array_divide() {
-        let a = Int32Array::from(vec![15, 15, 8, 1, 9]);
-        let b = Int32Array::from(vec![5, 6, 8, 9, 1]);
-        let c = divide(&a, &b).unwrap();
-        assert_eq!(3, c.value(0));
-        assert_eq!(2, c.value(1));
-        assert_eq!(1, c.value(2));
-        assert_eq!(0, c.value(3));
-        assert_eq!(9, c.value(4));
-    }
-
-    #[test]
-    fn test_primitive_array_divide_by_zero() {
-        let a = Int32Array::from(vec![15]);
-        let b = Int32Array::from(vec![0]);
-        assert_eq!(
-            ArrowError::DivideByZero,
-            divide(&a, &b).err().expect("divide by zero should fail")
-        );
-    }
-
-    #[test]
-    fn test_primitive_array_divide_f64() {
-        let a = Float64Array::from(vec![15.0, 15.0, 8.0]);
-        let b = Float64Array::from(vec![5.0, 6.0, 8.0]);
-        let c = divide(&a, &b).unwrap();
-        assert_eq!(3.0, c.value(0));
-        assert_eq!(2.5, c.value(1));
-        assert_eq!(1.0, c.value(2));
-    }
-
-    #[test]
-    fn test_primitive_array_add_with_nulls() {
-        let a = Int32Array::from(vec![Some(5), None, Some(7), None]);
-        let b = Int32Array::from(vec![None, None, Some(6), Some(7)]);
-        let c = add(&a, &b).unwrap();
-        assert_eq!(true, c.is_null(0));
-        assert_eq!(true, c.is_null(1));
-        assert_eq!(false, c.is_null(2));
-        assert_eq!(true, c.is_null(3));
-        assert_eq!(13, c.value(2));
-    }
-
-    #[test]
     fn test_primitive_array_sum() {
         let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
         assert_eq!(15, sum(&a).unwrap());
diff --git a/rust/arrow/src/compute/boolean_kernels.rs 
b/rust/arrow/src/compute/boolean_kernels.rs
index ededea9..f95d3d6 100644
--- a/rust/arrow/src/compute/boolean_kernels.rs
+++ b/rust/arrow/src/compute/boolean_kernels.rs
@@ -17,8 +17,10 @@
 
 //! Defines boolean kernels on Arrow `BooleanArray`'s, e.g. `AND`, `OR` and 
`NOT`.
 //!
-//! Kernels support SIMD using [static CPU feature 
detection](https://doc.rust-lang.org/stable/std/arch/#static-cpu-feature-detection)
-//! .
+//! These kernels can leverage SIMD if available on your system.  Currently no 
runtime
+//! detection is provided, you should enable the specific SIMD intrinsics using
+//! `RUSTFLAGS="-C target-feature=+avx2"` for example.  See the documentation
+//! [here] (https://doc.rust-lang.org/stable/std/arch/) for more information.
 
 use std::sync::Arc;
 
diff --git a/rust/arrow/src/compute/mod.rs b/rust/arrow/src/compute/mod.rs
index 5aee2b3..612ffcf 100644
--- a/rust/arrow/src/compute/mod.rs
+++ b/rust/arrow/src/compute/mod.rs
@@ -17,10 +17,12 @@
 
 //! Computation kernels on Arrow Arrays
 
+pub mod arithmetic_kernels;
 pub mod array_ops;
 pub mod boolean_kernels;
 
 mod util;
 
+pub use self::arithmetic_kernels::*;
 pub use self::array_ops::*;
 pub use self::boolean_kernels::*;
diff --git a/rust/arrow/src/datatypes.rs b/rust/arrow/src/datatypes.rs
index 4dc55d6..3baa5b9 100644
--- a/rust/arrow/src/datatypes.rs
+++ b/rust/arrow/src/datatypes.rs
@@ -23,9 +23,11 @@
 
 use std::fmt;
 use std::mem::size_of;
+use std::ops::{Add, Div, Mul, Sub};
 use std::slice::from_raw_parts;
 use std::str::FromStr;
 
+use packed_simd::*;
 use serde_derive::{Deserialize, Serialize};
 use serde_json::{json, Value};
 
@@ -155,18 +157,83 @@ make_type!(Float32Type, f32, DataType::Float32, 32, 
0.0f32);
 make_type!(Float64Type, f64, DataType::Float64, 64, 0.0f64);
 
 /// A subtype of primitive type that represents numeric values.
-pub trait ArrowNumericType: ArrowPrimitiveType {}
-
-impl ArrowNumericType for Int8Type {}
-impl ArrowNumericType for Int16Type {}
-impl ArrowNumericType for Int32Type {}
-impl ArrowNumericType for Int64Type {}
-impl ArrowNumericType for UInt8Type {}
-impl ArrowNumericType for UInt16Type {}
-impl ArrowNumericType for UInt32Type {}
-impl ArrowNumericType for UInt64Type {}
-impl ArrowNumericType for Float32Type {}
-impl ArrowNumericType for Float64Type {}
+///
+/// SIMD operations are defined in this trait if available on the target 
system.
+pub trait ArrowNumericType: ArrowPrimitiveType
+where
+    Self::Simd: Add<Output = Self::Simd>
+        + Sub<Output = Self::Simd>
+        + Mul<Output = Self::Simd>
+        + Div<Output = Self::Simd>,
+{
+    /// Defines the SIMD type that should be used for this numeric type
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    type Simd;
+
+    /// The number of SIMD lanes available
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn lanes() -> usize;
+
+    /// Loads a slice into a SIMD register
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn load(slice: &[Self::Native]) -> Self::Simd;
+
+    /// Performs a SIMD binary operation
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn bin_op<F: Fn(Self::Simd, Self::Simd) -> Self::Simd>(
+        left: Self::Simd,
+        right: Self::Simd,
+        op: F,
+    ) -> Self::Simd;
+
+    /// Writes a SIMD result back to a slice
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn write(simd_result: Self::Simd, slice: &mut [Self::Native]);
+}
+
+macro_rules! make_numeric_type {
+    ($impl_ty:ty, $native_ty:ty, $simd_ty:ident) => {
+        impl ArrowNumericType for $impl_ty {
+            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+            type Simd = $simd_ty;
+
+            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+            fn lanes() -> usize {
+                $simd_ty::lanes()
+            }
+
+            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+            fn load(slice: &[$native_ty]) -> $simd_ty {
+                unsafe { $simd_ty::from_slice_unaligned_unchecked(slice) }
+            }
+
+            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+            fn bin_op<F: Fn($simd_ty, $simd_ty) -> $simd_ty>(
+                left: $simd_ty,
+                right: $simd_ty,
+                op: F,
+            ) -> $simd_ty {
+                op(left, right)
+            }
+
+            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+            fn write(simd_result: $simd_ty, slice: &mut [$native_ty]) {
+                unsafe { simd_result.write_to_slice_unaligned_unchecked(slice) 
};
+            }
+        }
+    };
+}
+
+make_numeric_type!(Int8Type, i8, i8x64);
+make_numeric_type!(Int16Type, i16, i16x32);
+make_numeric_type!(Int32Type, i32, i32x16);
+make_numeric_type!(Int64Type, i64, i64x8);
+make_numeric_type!(UInt8Type, u8, u8x64);
+make_numeric_type!(UInt16Type, u16, u16x32);
+make_numeric_type!(UInt32Type, u32, u32x16);
+make_numeric_type!(UInt64Type, u64, u64x8);
+make_numeric_type!(Float32Type, f32, f32x16);
+make_numeric_type!(Float64Type, f64, f64x8);
 
 /// Allows conversion from supported Arrow types to a byte slice.
 pub trait ToByteSlice {
diff --git a/rust/arrow/src/util/bit_util.rs b/rust/arrow/src/util/bit_util.rs
index 23bc3d5..89ebd95 100644
--- a/rust/arrow/src/util/bit_util.rs
+++ b/rust/arrow/src/util/bit_util.rs
@@ -121,9 +121,9 @@ pub fn ceil(value: usize, divisor: usize) -> usize {
 
 /// Performs SIMD bitwise binary operations.
 ///
-/// 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.
+/// 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(any(target_arch = "x86", target_arch = "x86_64"))]
 pub unsafe fn bitwise_bin_op_simd<F>(left: &[u8], right: &[u8], result: &mut 
[u8], op: F)
 where
diff --git a/rust/datafusion/examples/csv_sql.rs 
b/rust/datafusion/examples/csv_sql.rs
index 40959cb..86f0f7f 100644
--- a/rust/datafusion/examples/csv_sql.rs
+++ b/rust/datafusion/examples/csv_sql.rs
@@ -28,7 +28,8 @@ use arrow::datatypes::{DataType, Field, Schema};
 use datafusion::execution::context::ExecutionContext;
 use datafusion::execution::datasource::CsvDataSource;
 
-/// This example demonstrates executing a simple query against an Arrow data 
source and fetching results
+/// This example demonstrates executing a simple query against an Arrow data 
source and
+/// fetching results
 fn main() {
     // create local execution context
     let mut ctx = ExecutionContext::new();
diff --git a/rust/datafusion/src/dfparser.rs b/rust/datafusion/src/dfparser.rs
index 7abbd8d..f438c03 100644
--- a/rust/datafusion/src/dfparser.rs
+++ b/rust/datafusion/src/dfparser.rs
@@ -17,8 +17,8 @@
 
 //! SQL Parser
 //!
-//! Note that most SQL parsing is now delegated to the sqlparser crate, which 
handles ANSI SQL but
-//! this module contains DataFusion-specific SQL extensions.
+//! Note that most SQL parsing is now delegated to the sqlparser crate, which 
handles ANSI
+//! SQL but this module contains DataFusion-specific SQL extensions.
 
 use sqlparser::dialect::*;
 use sqlparser::sqlast::*;
diff --git a/rust/datafusion/src/execution/aggregate.rs 
b/rust/datafusion/src/execution/aggregate.rs
index d8cbfdf..127a1ab 100644
--- a/rust/datafusion/src/execution/aggregate.rs
+++ b/rust/datafusion/src/execution/aggregate.rs
@@ -15,8 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
-//! Execution of a simple aggregate relation containing MIN, MAX, COUNT, SUM 
aggregate functions
-//! with optional GROUP BY columns
+//! Execution of a simple aggregate relation containing MIN, MAX, COUNT, SUM 
aggregate
+//! functions with optional GROUP BY columns
 
 use std::cell::RefCell;
 use std::rc::Rc;
@@ -63,8 +63,8 @@ impl AggregateRelation {
     }
 }
 
-/// Enumeration of types that can be used in a GROUP BY expression (all 
primitives except for
-/// floating point numerics)
+/// Enumeration of types that can be used in a GROUP BY expression (all 
primitives except
+/// for floating point numerics)
 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
 enum GroupByScalar {
     UInt8(u8),
@@ -692,7 +692,8 @@ macro_rules! group_array_from_map_entries {
     }};
 }
 
-/// Create array from `value` attribute in map entry (representing an 
aggregate scalar value)
+/// Create array from `value` attribute in map entry (representing an 
aggregate scalar
+/// value)
 macro_rules! aggr_array_from_map_entries {
     ($BUILDER:ident, $TY:ident, $ENTRIES:expr, $COL_INDEX:expr) => {{
         let mut builder = $BUILDER::new($ENTRIES.len());
@@ -803,9 +804,10 @@ impl AggregateRelation {
     }
 
     fn with_group_by(&mut self) -> Result<Option<RecordBatch>> {
-        //NOTE this whole method is currently very inefficient with too many 
per-row operations
-        // involving pattern matching and downcasting ... I'm sure this can be 
re-implemented in
-        // a much more efficient way that takes better advantage of Arrow
+        //NOTE this whole method is currently very inefficient with too many 
per-row
+        // operations involving pattern matching and downcasting ... I'm sure 
this
+        // can be re-implemented in a much more efficient way that takes better
+        // advantage of Arrow
 
         // create map to store aggregate results
         let mut map: FnvHashMap<Vec<GroupByScalar>, 
Rc<RefCell<AccumulatorSet>>> =
@@ -877,7 +879,8 @@ impl AggregateRelation {
                     })
                     .collect::<Result<Vec<GroupByScalar>>>()?;
 
-                //TODO: find more elegant way to write this instead of hacking 
around ownership issues
+                //TODO: find more elegant way to write this instead of hacking 
around
+                // ownership issues
 
                 let updated = match map.get(&key) {
                     Some(entry) => {
diff --git a/rust/datafusion/src/execution/expression.rs 
b/rust/datafusion/src/execution/expression.rs
index fa6201a..48a90a3 100644
--- a/rust/datafusion/src/execution/expression.rs
+++ b/rust/datafusion/src/execution/expression.rs
@@ -298,12 +298,13 @@ pub fn compile_scalar_expr(
 ) -> Result<RuntimeExpr> {
     match expr {
         &Expr::Literal(ref value) => match value {
-            //NOTE: this is a temporary hack .. due to the way expressions 
like 'a > 1' are
-            // evaluated, currently the left and right are evaluated 
separately and must result
-            // in arrays and then the '>' operator is evaluated against the 
two arrays. This works
-            // but is dumb ... I intend to optimize this soon to add special 
handling for
-            // binary expressions that involve literal values to avoid 
creating arrays of literals
-            // filed as https://github.com/andygrove/datafusion/issues/191
+            //NOTE: this is a temporary hack .. due to the way expressions 
like 'a > 1'
+            // are evaluated, currently the left and right are evaluated
+            // separately and must result in arrays and then the '>' operator
+            // is evaluated against the two arrays. This works but is dumb ...
+            // I intend to optimize this soon to add special handling for
+            // binary expressions that involve literal values to avoid 
creating arrays of
+            // literals filed as 
https://github.com/andygrove/datafusion/issues/191
             ScalarValue::Int8(n) => literal_array!(n, Int8Array, Int8),
             ScalarValue::Int16(n) => literal_array!(n, Int16Array, Int16),
             ScalarValue::Int32(n) => literal_array!(n, Int32Array, Int32),
diff --git a/rust/datafusion/src/execution/filter.rs 
b/rust/datafusion/src/execution/filter.rs
index ba20dca..32c1628 100644
--- a/rust/datafusion/src/execution/filter.rs
+++ b/rust/datafusion/src/execution/filter.rs
@@ -192,7 +192,8 @@ fn filter(array: &Arc<Array>, filter: &BooleanArray) -> 
Result<ArrayRef> {
             Ok(Arc::new(builder.finish()))
         }
         DataType::Utf8 => {
-            //TODO: this is inefficient and we should improve the Arrow impl 
to help make this more concise
+            //TODO: this is inefficient and we should improve the Arrow impl 
to help make
+            // this more concise
             let b = a.as_any().downcast_ref::<BinaryArray>().unwrap();
             let mut values: Vec<String> = Vec::with_capacity(b.len());
             for i in 0..b.len() {
diff --git a/rust/datafusion/src/lib.rs b/rust/datafusion/src/lib.rs
index 6ea2a36..a93872c 100644
--- a/rust/datafusion/src/lib.rs
+++ b/rust/datafusion/src/lib.rs
@@ -15,8 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
-//! DataFusion is a modern distributed compute platform implemented in Rust 
that uses Apache Arrow
-//! as the memory model
+//! DataFusion is a modern distributed compute platform implemented in Rust 
that uses
+//! Apache Arrow as the memory model
 
 extern crate arrow;
 #[macro_use]
diff --git a/rust/datafusion/src/logicalplan.rs 
b/rust/datafusion/src/logicalplan.rs
index 7dd4602..5e3b17e 100644
--- a/rust/datafusion/src/logicalplan.rs
+++ b/rust/datafusion/src/logicalplan.rs
@@ -316,8 +316,8 @@ impl fmt::Debug for Expr {
     }
 }
 
-/// The LogicalPlan represents different types of relations (such as 
Projection, Selection, etc) and
-/// can be created by the SQL query planner and the DataFrame API.
+/// The LogicalPlan represents different types of relations (such as 
Projection,
+/// Selection, etc) and can be created by the SQL query planner and the 
DataFrame API.
 #[derive(Serialize, Deserialize, Clone)]
 pub enum LogicalPlan {
     /// A Projection (essentially a SELECT with an expression list)
diff --git a/rust/datafusion/src/sqlplanner.rs 
b/rust/datafusion/src/sqlplanner.rs
index fc8048f..a45d5a4 100644
--- a/rust/datafusion/src/sqlplanner.rs
+++ b/rust/datafusion/src/sqlplanner.rs
@@ -313,7 +313,8 @@ impl SqlToRel {
                             .map(|a| self.sql_to_rex(a, schema))
                             .collect::<Result<Vec<Expr>>>()?;
 
-                        // return type is same as the argument type for these 
aggregate functions
+                        // return type is same as the argument type for these 
aggregate
+                        // functions
                         let return_type = rex_args[0].get_type(schema).clone();
 
                         Ok(Expr::AggregateFunction {
@@ -326,7 +327,8 @@ impl SqlToRel {
                         let rex_args = args
                             .iter()
                             .map(|a| match a {
-                                // this feels hacky but translate 
COUNT(1)/COUNT(*) to COUNT(first_column)
+                                // this feels hacky but translate 
COUNT(1)/COUNT(*) to
+                                // COUNT(first_column)
                                 
ASTNode::SQLValue(sqlparser::sqlast::Value::Long(1)) => {
                                     Ok(Expr::Column(0))
                                 }

Reply via email to