From d972932373c846bde1cbb840388448d80fd77e6e Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Wed, 1 Jul 2026 18:01:37 -0400 Subject: [PATCH 1/4] Add validated row decode benchmark --- arrow-row/src/lib.rs | 2 +- arrow/benches/row_format.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/arrow-row/src/lib.rs b/arrow-row/src/lib.rs index 182481f42b4d..17ede5a0c466 100644 --- a/arrow-row/src/lib.rs +++ b/arrow-row/src/lib.rs @@ -1221,7 +1221,7 @@ struct RowConfig { /// A row-oriented representation of arrow data, that is normalized for comparison. /// /// See the [module level documentation](self) and [`RowConverter`] for more details. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Rows { /// Underlying row bytes buffer: Vec, diff --git a/arrow/benches/row_format.rs b/arrow/benches/row_format.rs index f33d8d16b81f..041cb681579d 100644 --- a/arrow/benches/row_format.rs +++ b/arrow/benches/row_format.rs @@ -61,6 +61,25 @@ fn do_bench(c: &mut Criterion, name: &str, cols: Vec) { b.iter(|| hint::black_box(converter.convert_rows(&rows).unwrap())); }); + let binary_rows = rows.clone().try_into_binary().expect("reasonable size"); + let parser = converter.parser(); + // RowParser marks rows as requiring UTF-8 validation when they are decoded + // back into Arrow arrays by RowConverter::convert_rows. + let parsed_rows: Vec<_> = binary_rows + .iter() + .flatten() + .map(|row| parser.parse(row).owned()) + .collect(); + c.bench_function(&format!("convert_rows_validated {name}"), |b| { + b.iter(|| { + hint::black_box( + converter + .convert_rows(parsed_rows.iter().map(|row| row.row())) + .unwrap(), + ) + }); + }); + let mut rows = converter.empty_rows(0, 0); c.bench_function(&format!("append_rows {name}"), |b| { let cols = cols.clone(); From 56fb84797890685a9dce12a17de62657118d62f7 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Thu, 2 Jul 2026 07:18:33 -0400 Subject: [PATCH 2/4] Cleanup --- arrow/benches/row_format.rs | 46 ++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/arrow/benches/row_format.rs b/arrow/benches/row_format.rs index 041cb681579d..db9085b2a69b 100644 --- a/arrow/benches/row_format.rs +++ b/arrow/benches/row_format.rs @@ -37,6 +37,14 @@ use arrow_schema::{DataType, Field}; use criterion::Criterion; use std::{hint, sync::Arc}; +fn is_string_like(data_type: &DataType) -> bool { + match data_type { + DataType::Utf8 | DataType::LargeUtf8 | DataType::Utf8View => true, + DataType::Dictionary(_, values) => is_string_like(values), + _ => false, + } +} + fn do_bench(c: &mut Criterion, name: &str, cols: Vec) { let fields: Vec<_> = cols .iter() @@ -61,24 +69,30 @@ fn do_bench(c: &mut Criterion, name: &str, cols: Vec) { b.iter(|| hint::black_box(converter.convert_rows(&rows).unwrap())); }); - let binary_rows = rows.clone().try_into_binary().expect("reasonable size"); - let parser = converter.parser(); - // RowParser marks rows as requiring UTF-8 validation when they are decoded - // back into Arrow arrays by RowConverter::convert_rows. - let parsed_rows: Vec<_> = binary_rows + // Benchmark parsing strings (which may need utf8 validation), + let no_nulls = cols .iter() - .flatten() - .map(|row| parser.parse(row).owned()) - .collect(); - c.bench_function(&format!("convert_rows_validated {name}"), |b| { - b.iter(|| { - hint::black_box( - converter - .convert_rows(parsed_rows.iter().map(|row| row.row())) - .unwrap(), - ) + .all(|array| array.null_count() == 0); + + if is_string_like(array.data_type()) && no_nulls + { + // RowParser marks rows as requiring UTF-8 validation when they are decoded + // back into Arrow arrays by RowConverter::convert_rows. + let parser = converter.parser(); + let parsed_rows: Vec<_> = rows + .iter() + .map(|row| parser.parse(row.as_ref()).owned()) + .collect(); + c.bench_function(&format!("convert_rows_parsed {name}"), |b| { + b.iter(|| { + hint::black_box( + converter + .convert_rows(parsed_rows.iter().map(|row| row.row())) + .unwrap(), + ) + }); }); - }); + } let mut rows = converter.empty_rows(0, 0); c.bench_function(&format!("append_rows {name}"), |b| { From 16e9012f1f0adc3d7bd2081d73767c6075220afa Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Thu, 2 Jul 2026 07:21:32 -0400 Subject: [PATCH 3/4] cleanup --- arrow/benches/row_format.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/arrow/benches/row_format.rs b/arrow/benches/row_format.rs index db9085b2a69b..d457901871d8 100644 --- a/arrow/benches/row_format.rs +++ b/arrow/benches/row_format.rs @@ -69,13 +69,11 @@ fn do_bench(c: &mut Criterion, name: &str, cols: Vec) { b.iter(|| hint::black_box(converter.convert_rows(&rows).unwrap())); }); - // Benchmark parsing strings (which may need utf8 validation), - let no_nulls = cols - .iter() - .all(|array| array.null_count() == 0); - - if is_string_like(array.data_type()) && no_nulls - { + // Benchmark parsing strings, which may need utf8 validation. + fn non_null_string(array: &ArrayRef) -> bool { + array.null_count() == 0 && is_string_like(array.data_type()) + } + if cols.iter().all(non_null_string) { // RowParser marks rows as requiring UTF-8 validation when they are decoded // back into Arrow arrays by RowConverter::convert_rows. let parser = converter.parser(); From 1bd1731c7d514900a0a9d03039a8e8f4e92b34cd Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Thu, 2 Jul 2026 07:27:34 -0400 Subject: [PATCH 4/4] Tighten parsed row benchmark coverage --- arrow/benches/row_format.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/arrow/benches/row_format.rs b/arrow/benches/row_format.rs index d457901871d8..6615a4d2307c 100644 --- a/arrow/benches/row_format.rs +++ b/arrow/benches/row_format.rs @@ -37,14 +37,6 @@ use arrow_schema::{DataType, Field}; use criterion::Criterion; use std::{hint, sync::Arc}; -fn is_string_like(data_type: &DataType) -> bool { - match data_type { - DataType::Utf8 | DataType::LargeUtf8 | DataType::Utf8View => true, - DataType::Dictionary(_, values) => is_string_like(values), - _ => false, - } -} - fn do_bench(c: &mut Criterion, name: &str, cols: Vec) { let fields: Vec<_> = cols .iter() @@ -69,11 +61,11 @@ fn do_bench(c: &mut Criterion, name: &str, cols: Vec) { b.iter(|| hint::black_box(converter.convert_rows(&rows).unwrap())); }); - // Benchmark parsing strings, which may need utf8 validation. - fn non_null_string(array: &ArrayRef) -> bool { - array.null_count() == 0 && is_string_like(array.data_type()) + // Benchmark parsing strings which may need utf8 validation. + fn is_stringlike(array: &ArrayRef) -> bool { + matches!(array.data_type(), DataType::Utf8 | DataType::Utf8View) } - if cols.iter().all(non_null_string) { + if cols.iter().all(is_stringlike) { // RowParser marks rows as requiring UTF-8 validation when they are decoded // back into Arrow arrays by RowConverter::convert_rows. let parser = converter.parser();