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

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new c254b8bb88 Improve stats convert performance for Binary/String/Boolean 
arrays (#11319)
c254b8bb88 is described below

commit c254b8bb885e2e17a175ecb2e92174cfb23a2ceb
Author: kamille <[email protected]>
AuthorDate: Mon Jul 8 17:58:24 2024 +0800

    Improve stats convert performance for Binary/String/Boolean arrays (#11319)
    
    * add u64 poc.
    
    * use env to support the quick bench.
    
    * use flatten in builder mode yet.
    
    * add new mode.
    
    * use Builder in Utf8 and LargeUtf8 page level stats' convert.
    
    * use Builder in row group level stats.
    
    * eliminate some unnecessary tmp `Vec`s.
    
    * remove the quick modify in bench.
    
    * process the result return from append_value of FixedSizeBinaryBuilder.
    
    * remove the modification of FixedSizeBinary&Bool, and fix the String case.
    
    * fix and re-enable the modification of FixedSizeBinary.
    
    * fix comments.
    
    * use BooleanBuilder to eliminate the collect in BooleanArray case.
    
    * fix compile.
---
 .../datasource/physical_plan/parquet/statistics.rs | 215 +++++++++++++--------
 1 file changed, 130 insertions(+), 85 deletions(-)

diff --git a/datafusion/core/src/datasource/physical_plan/parquet/statistics.rs 
b/datafusion/core/src/datasource/physical_plan/parquet/statistics.rs
index b9aca2ac2c..59369aba57 100644
--- a/datafusion/core/src/datasource/physical_plan/parquet/statistics.rs
+++ b/datafusion/core/src/datasource/physical_plan/parquet/statistics.rs
@@ -19,17 +19,19 @@
 
 // TODO: potentially move this to arrow-rs: 
https://github.com/apache/arrow-rs/issues/4328
 
-use arrow::array::builder::FixedSizeBinaryBuilder;
+use arrow::array::{
+    BooleanBuilder, FixedSizeBinaryBuilder, LargeStringBuilder, StringBuilder,
+};
 use arrow::datatypes::i256;
 use arrow::{array::ArrayRef, datatypes::DataType};
 use arrow_array::{
     new_empty_array, new_null_array, BinaryArray, BooleanArray, Date32Array, 
Date64Array,
-    Decimal128Array, Decimal256Array, FixedSizeBinaryArray, Float16Array, 
Float32Array,
-    Float64Array, Int16Array, Int32Array, Int64Array, Int8Array, 
LargeBinaryArray,
-    LargeStringArray, StringArray, Time32MillisecondArray, Time32SecondArray,
-    Time64MicrosecondArray, Time64NanosecondArray, TimestampMicrosecondArray,
-    TimestampMillisecondArray, TimestampNanosecondArray, TimestampSecondArray,
-    UInt16Array, UInt32Array, UInt64Array, UInt8Array,
+    Decimal128Array, Decimal256Array, Float16Array, Float32Array, Float64Array,
+    Int16Array, Int32Array, Int64Array, Int8Array, LargeBinaryArray,
+    Time32MillisecondArray, Time32SecondArray, Time64MicrosecondArray,
+    Time64NanosecondArray, TimestampMicrosecondArray, 
TimestampMillisecondArray,
+    TimestampNanosecondArray, TimestampSecondArray, UInt16Array, UInt32Array,
+    UInt64Array, UInt8Array,
 };
 use arrow_schema::{Field, FieldRef, Schema, TimeUnit};
 use datafusion_common::{internal_datafusion_err, internal_err, plan_err, 
Result};
@@ -393,51 +395,73 @@ macro_rules! get_statistics {
                 })
             },
             DataType::Binary => Ok(Arc::new(BinaryArray::from_iter(
-                [<$stat_type_prefix 
ByteArrayStatsIterator>]::new($iterator).map(|x| x.map(|x| x.to_vec())),
+                [<$stat_type_prefix ByteArrayStatsIterator>]::new($iterator)
             ))),
             DataType::LargeBinary => Ok(Arc::new(LargeBinaryArray::from_iter(
-                [<$stat_type_prefix 
ByteArrayStatsIterator>]::new($iterator).map(|x| x.map(|x|x.to_vec())),
-            ))),
-            DataType::Utf8 => Ok(Arc::new(StringArray::from_iter(
-                [<$stat_type_prefix 
ByteArrayStatsIterator>]::new($iterator).map(|x| {
-                    x.and_then(|x| {
-                        let res = std::str::from_utf8(x).map(|s| 
s.to_string()).ok();
-                        if res.is_none() {
-                            log::debug!("Utf8 statistics is a non-UTF8 value, 
ignoring it.");
-                        }
-                        res
-                    })
-                }),
+                [<$stat_type_prefix ByteArrayStatsIterator>]::new($iterator)
             ))),
+            DataType::Utf8 => {
+                let iterator = [<$stat_type_prefix 
ByteArrayStatsIterator>]::new($iterator);
+                let mut builder = StringBuilder::new();
+                for x in iterator {
+                    let Some(x) = x else {
+                        builder.append_null(); // no statistics value
+                        continue;
+                    };
+
+                    let Ok(x) = std::str::from_utf8(x) else {
+                        log::debug!("Utf8 statistics is a non-UTF8 value, 
ignoring it.");
+                        builder.append_null();
+                        continue;
+                    };
+
+                    builder.append_value(x);
+                }
+                Ok(Arc::new(builder.finish()))
+            },
             DataType::LargeUtf8 => {
-                Ok(Arc::new(LargeStringArray::from_iter(
-                    [<$stat_type_prefix 
ByteArrayStatsIterator>]::new($iterator).map(|x| {
-                        x.and_then(|x| {
-                            let res = std::str::from_utf8(x).map(|s| 
s.to_string()).ok();
-                            if res.is_none() {
-                                log::debug!("LargeUtf8 statistics is a 
non-UTF8 value, ignoring it.");
-                            }
-                            res
-                        })
-                    }),
-                )))
-            }
-            DataType::FixedSizeBinary(size) => 
Ok(Arc::new(FixedSizeBinaryArray::from(
-                [<$stat_type_prefix 
FixedLenByteArrayStatsIterator>]::new($iterator).map(|x| {
-                    x.and_then(|x| {
-                        if x.len().try_into() == Ok(*size) {
-                            Some(x)
-                        } else {
-                            log::debug!(
-                                "FixedSizeBinary({}) statistics is a binary of 
size {}, ignoring it.",
-                                size,
-                                x.len(),
-                            );
-                            None
-                        }
-                    })
-                }).collect::<Vec<_>>(),
-            ))),
+                let iterator = [<$stat_type_prefix 
ByteArrayStatsIterator>]::new($iterator);
+                let mut builder = LargeStringBuilder::new();
+                for x in iterator {
+                    let Some(x) = x else {
+                        builder.append_null(); // no statistics value
+                        continue;
+                    };
+
+                    let Ok(x) = std::str::from_utf8(x) else {
+                        log::debug!("Utf8 statistics is a non-UTF8 value, 
ignoring it.");
+                        builder.append_null();
+                        continue;
+                    };
+
+                    builder.append_value(x);
+                }
+                Ok(Arc::new(builder.finish()))
+            },
+            DataType::FixedSizeBinary(size) => {
+                let iterator = [<$stat_type_prefix 
FixedLenByteArrayStatsIterator>]::new($iterator);
+                let mut builder = FixedSizeBinaryBuilder::new(*size);
+                for x in iterator {
+                    let Some(x) = x else {
+                        builder.append_null(); // no statistics value
+                        continue;
+                    };
+
+                    // ignore invalid values
+                    if x.len().try_into() != Ok(*size){
+                        log::debug!(
+                            "FixedSizeBinary({}) statistics is a binary of 
size {}, ignoring it.",
+                            size,
+                            x.len(),
+                        );
+                        builder.append_null();
+                        continue;
+                    }
+
+                    builder.append_value(x).expect("ensure to append 
successfully here, because size have been checked before");
+                }
+                Ok(Arc::new(builder.finish()))
+            },
             DataType::Decimal128(precision, scale) => {
                 let arr = Decimal128Array::from_iter(
                     [<$stat_type_prefix 
Decimal128StatsIterator>]::new($iterator)
@@ -740,15 +764,20 @@ macro_rules! get_data_page_statistics {
     ($stat_type_prefix: ident, $data_type: ident, $iterator: ident) => {
         paste! {
             match $data_type {
-                Some(DataType::Boolean) => Ok(Arc::new(
-                    BooleanArray::from_iter(
-                        [<$stat_type_prefix 
BooleanDataPageStatsIterator>]::new($iterator)
-                            .flatten()
-                            // BooleanArray::from_iter required a sized 
iterator, so collect into Vec first
-                            .collect::<Vec<_>>()
-                            .into_iter()
-                    )
-                )),
+                Some(DataType::Boolean) => {
+                    let iterator = [<$stat_type_prefix 
BooleanDataPageStatsIterator>]::new($iterator);
+                    let mut builder = BooleanBuilder::new();
+                    for x in iterator {
+                        for x in x.into_iter() {
+                            let Some(x) = x else {
+                                builder.append_null(); // no statistics value
+                                continue;
+                            };
+                            builder.append_value(x);
+                        }
+                    }
+                    Ok(Arc::new(builder.finish()))
+                },
                 Some(DataType::UInt8) => Ok(Arc::new(
                     UInt8Array::from_iter(
                         [<$stat_type_prefix 
Int32DataPageStatsIterator>]::new($iterator)
@@ -830,32 +859,48 @@ macro_rules! get_data_page_statistics {
                 Some(DataType::Float64) => 
Ok(Arc::new(Float64Array::from_iter([<$stat_type_prefix 
Float64DataPageStatsIterator>]::new($iterator).flatten()))),
                 Some(DataType::Binary) => 
Ok(Arc::new(BinaryArray::from_iter([<$stat_type_prefix 
ByteArrayDataPageStatsIterator>]::new($iterator).flatten()))),
                 Some(DataType::LargeBinary) => 
Ok(Arc::new(LargeBinaryArray::from_iter([<$stat_type_prefix 
ByteArrayDataPageStatsIterator>]::new($iterator).flatten()))),
-                Some(DataType::Utf8) => Ok(Arc::new(StringArray::from(
-                    [<$stat_type_prefix 
ByteArrayDataPageStatsIterator>]::new($iterator).map(|x| {
-                        x.into_iter().map(|x| {
-                        x.and_then(|x| {
-                            let res = std::str::from_utf8(x.data()).map(|s| 
s.to_string()).ok();
-                            if res.is_none() {
+                Some(DataType::Utf8) => {
+                    let mut builder = StringBuilder::new();
+                    let iterator = [<$stat_type_prefix 
ByteArrayDataPageStatsIterator>]::new($iterator);
+                    for x in iterator {
+                        for x in x.into_iter() {
+                            let Some(x) = x else {
+                                builder.append_null(); // no statistics value
+                                continue;
+                            };
+
+                            let Ok(x) = std::str::from_utf8(x.data()) else {
                                 log::debug!("Utf8 statistics is a non-UTF8 
value, ignoring it.");
-                            }
-                            res
-                        })
-                    })
-                    }).flatten().collect::<Vec<_>>(),
-                ))),
-                Some(DataType::LargeUtf8) => 
Ok(Arc::new(LargeStringArray::from(
-                    [<$stat_type_prefix 
ByteArrayDataPageStatsIterator>]::new($iterator).map(|x| {
-                        x.into_iter().map(|x| {
-                            x.and_then(|x| {
-                                let res = 
std::str::from_utf8(x.data()).map(|s| s.to_string()).ok();
-                                if res.is_none() {
-                                    log::debug!("LargeUtf8 statistics is a 
non-UTF8 value, ignoring it.");
-                                }
-                                res
-                            })
-                    })
-                    }).flatten().collect::<Vec<_>>(),
-                ))),
+                                builder.append_null();
+                                continue;
+                            };
+
+                            builder.append_value(x);
+                        }
+                    }
+                    Ok(Arc::new(builder.finish()))
+                },
+                Some(DataType::LargeUtf8) => {
+                    let mut builder = LargeStringBuilder::new();
+                    let iterator = [<$stat_type_prefix 
ByteArrayDataPageStatsIterator>]::new($iterator);
+                    for x in iterator {
+                        for x in x.into_iter() {
+                            let Some(x) = x else {
+                                builder.append_null(); // no statistics value
+                                continue;
+                            };
+
+                            let Ok(x) = std::str::from_utf8(x.data()) else {
+                                log::debug!("LargeUtf8 statistics is a 
non-UTF8 value, ignoring it.");
+                                builder.append_null();
+                                continue;
+                            };
+
+                            builder.append_value(x);
+                        }
+                    }
+                    Ok(Arc::new(builder.finish()))
+                },
                 Some(DataType::Dictionary(_, value_type)) => {
                     [<$stat_type_prefix:lower _ 
page_statistics>](Some(value_type), $iterator)
                 },
@@ -871,14 +916,14 @@ macro_rules! get_data_page_statistics {
                 Some(DataType::Date32) => 
Ok(Arc::new(Date32Array::from_iter([<$stat_type_prefix 
Int32DataPageStatsIterator>]::new($iterator).flatten()))),
                 Some(DataType::Date64) => Ok(
                     Arc::new(
-                        Date64Array::from([<$stat_type_prefix 
Int32DataPageStatsIterator>]::new($iterator)
+                        Date64Array::from_iter([<$stat_type_prefix 
Int32DataPageStatsIterator>]::new($iterator)
                             .map(|x| {
                                 x.into_iter()
                                 .map(|x| {
                                     x.and_then(|x| i64::try_from(x).ok())
-                                    .map(|x| x * 24 * 60 * 60 * 1000)
                                 })
-                            }).flatten().collect::<Vec<_>>()
+                                .map(|x| x.map(|x| x * 24 * 60 * 60 * 1000))
+                            }).flatten()
                         )
                     )
                 ),


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to