You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/04/27 17:07:50 UTC
(datafusion) 01/01: perf: Use bulk-NULL builder in `chr` (#21847)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-21847-26762977499c3ea581a8c3f698c855c4d607a2c6
in repository https://gitbox.apache.org/repos/asf/datafusion.git
commit 62ad66beb1e13490980dc5e53e5542f6f07cce65
Author: Neil Conway <ne...@gmail.com>
AuthorDate: Mon Apr 27 13:07:27 2026 -0400
perf: Use bulk-NULL builder in `chr` (#21847)
## Which issue does this PR close?
- Closes #21846.
## Rationale for this change
Optimize `chr` by avoiding per-row NULL bitmap maintenance, and also
split the hot loop to avoid taking a branch when no NULL bitmap is
given.
Benchmarks:
```
chr/array: 3.8768 µs → 3.1548 µs, −18.57% (`p < 0.05`)
```
## What changes are included in this PR?
* Optimize `chr` to reduce NULL-handling overhead
## Are these changes tested?
Yes.
## Are there any user-facing changes?
No.
---------
Co-authored-by: Andrew Lamb <an...@nerdnetworks.org>
---
datafusion/functions/src/string/chr.rs | 58 +++++++++++++++++++++++-----------
1 file changed, 40 insertions(+), 18 deletions(-)
diff --git a/datafusion/functions/src/string/chr.rs b/datafusion/functions/src/string/chr.rs
index 058bbe10b2..60df0c47cf 100644
--- a/datafusion/functions/src/string/chr.rs
+++ b/datafusion/functions/src/string/chr.rs
@@ -17,11 +17,12 @@
use std::sync::Arc;
-use arrow::array::{ArrayRef, GenericStringBuilder, Int64Array};
+use arrow::array::{Array, ArrayRef, Int64Array};
use arrow::datatypes::DataType;
use arrow::datatypes::DataType::Int64;
use arrow::datatypes::DataType::Utf8;
+use crate::strings::GenericStringArrayBuilder;
use datafusion_common::cast::as_int64_array;
use datafusion_common::utils::take_function_args;
use datafusion_common::{Result, ScalarValue, exec_err, internal_err};
@@ -32,31 +33,46 @@ use datafusion_macros::user_doc;
/// Returns the character with the given code.
/// chr(65) = 'A'
fn chr_array(integer_array: &Int64Array) -> Result<ArrayRef> {
- let mut builder = GenericStringBuilder::<i32>::with_capacity(
- integer_array.len(),
- // 1 byte per character, assuming that is the common case
- integer_array.len(),
+ let len = integer_array.len();
+ let mut builder = GenericStringArrayBuilder::<i32>::with_capacity(
+ len, // 1 byte per character, assuming that is the common case
+ len,
);
let mut buf = [0u8; 4];
+ let nulls = integer_array.nulls();
- for integer in integer_array {
- match integer {
- Some(integer) => {
- if let Ok(u) = u32::try_from(integer)
- && let Some(c) = core::char::from_u32(u)
- {
- builder.append_value(c.encode_utf8(&mut buf));
- continue;
- }
-
- return exec_err!("invalid Unicode scalar value: {integer}");
+ if let Some(n) = nulls {
+ for i in 0..len {
+ if n.is_null(i) {
+ builder.append_placeholder();
+ continue;
}
- None => builder.append_null(),
+ // SAFETY: bounds + null check above.
+ let integer = unsafe { integer_array.value_unchecked(i) };
+ if let Ok(u) = u32::try_from(integer)
+ && let Some(c) = core::char::from_u32(u)
+ {
+ builder.append_value(c.encode_utf8(&mut buf));
+ continue;
+ }
+ return exec_err!("invalid Unicode scalar value: {integer}");
+ }
+ } else {
+ for i in 0..len {
+ // SAFETY: no null buffer means every index is valid.
+ let integer = unsafe { integer_array.value_unchecked(i) };
+ if let Ok(u) = u32::try_from(integer)
+ && let Some(c) = core::char::from_u32(u)
+ {
+ builder.append_value(c.encode_utf8(&mut buf));
+ continue;
+ }
+ return exec_err!("invalid Unicode scalar value: {integer}");
}
}
- Ok(Arc::new(builder.finish()) as ArrayRef)
+ Ok(Arc::new(builder.finish(nulls.cloned())?) as ArrayRef)
}
#[user_doc(
@@ -198,7 +214,13 @@ mod tests {
];
assert_eq!(string_array.len(), expected.len());
+ assert_eq!(string_array.null_count(), 1);
+ assert!(string_array.is_null(7));
for (i, e) in expected.iter().enumerate() {
+ if i == 7 {
+ continue;
+ }
+ assert!(!string_array.is_null(i));
assert_eq!(string_array.value(i), *e);
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org