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-23762-f33dcec6dfd0b900ddf89b75b46e547f7923c0e7
in repository https://gitbox.apache.org/repos/asf/datafusion.git

commit e271f65c9f8188cb6b1876da099646f396e34e00
Author: Andy Grove <[email protected]>
AuthorDate: Wed Jul 22 10:32:56 2026 -0600

    perf: optimize left_right in datafusion-functions (#23762)
    
    ## Which issue does this PR close?
    
    N/A
    
    ## Rationale for this change
    
    Optimize existing expression.
    
    ## What changes are included in this PR?
    
    Added an ASCII fast path to left_right_byte_length so left/right compute
    the n-th codepoint byte offset via O(1) arithmetic instead of a per-row
    char_indices() UTF-8 scan.
    
    ## Are these changes tested?
    
    Existing tests.
    
    Benchmark (criterion):
    
    - string_view short_result: 4.848% faster (base 56784ns -> cand 54031ns)
    - string long_result: 38.098% faster (base 141926ns -> cand 87855ns)
    - string_view long_result: 44.817% faster (base 94346ns -> cand 52063ns)
    - string short_result: 13.184% faster (base 88532ns -> cand 76860ns)
    - string_view short_result: 15.165% faster (base 57136ns -> cand
    48472ns)
    - string long_result: 39.278% faster (base 140969ns -> cand 85600ns)
    - string_view long_result: 40.29% faster (base 81808ns -> cand 48847ns)
    - string short_result: 14.369% faster (base 86809ns -> cand 74336ns)
    
    Full criterion output:
    
    ```text
    left/string short_result
                            time:   [73.406 µs 73.726 µs 74.067 µs]
                            change: [−15.026% −14.369% −13.742%] (p = 0.00 < 
0.05)
                            Performance has improved.
    Found 1 outliers among 100 measurements (1.00%)
      1 (1.00%) low mild
    left/string long_result time:   [85.227 µs 85.519 µs 85.827 µs]
                            change: [−39.588% −39.278% −38.969%] (p = 0.00 < 
0.05)
                            Performance has improved.
    Found 3 outliers among 100 measurements (3.00%)
      1 (1.00%) low mild
      2 (2.00%) high mild
    left/string_view short_result
                            time:   [48.206 µs 48.411 µs 48.613 µs]
                            change: [−17.549% −15.165% −13.033%] (p = 0.00 < 
0.05)
                            Performance has improved.
    left/string_view long_result
                            time:   [48.383 µs 48.691 µs 49.038 µs]
                            change: [−40.884% −40.290% −39.714%] (p = 0.00 < 
0.05)
                            Performance has improved.
    
    right/string short_result
                            time:   [76.804 µs 76.986 µs 77.159 µs]
                            change: [−13.955% −13.184% −12.465%] (p = 0.00 < 
0.05)
                            Performance has improved.
    Found 6 outliers among 100 measurements (6.00%)
      3 (3.00%) low severe
      3 (3.00%) low mild
    right/string long_result
                            time:   [88.219 µs 88.597 µs 88.938 µs]
                            change: [−38.351% −38.098% −37.853%] (p = 0.00 < 
0.05)
                            Performance has improved.
    right/string_view short_result
                            time:   [53.380 µs 53.545 µs 53.728 µs]
                            change: [−5.5061% −4.8482% −4.1872%] (p = 0.00 < 
0.05)
                            Performance has improved.
    Found 3 outliers among 100 measurements (3.00%)
      3 (3.00%) high mild
    right/string_view long_result
                            time:   [52.049 µs 52.332 µs 52.633 µs]
                            change: [−45.883% −44.817% −43.850%] (p = 0.00 < 
0.05)
                            Performance has improved.
    Found 6 outliers among 100 measurements (6.00%)
      6 (6.00%) high mild
    ```
    
    
    ## Are there any user-facing changes?
    
    No
    
    <!--
    If there are user-facing changes then we may require documentation to be
    updated before approving the PR.
    -->
    
    <!--
    If there are any breaking changes to public APIs, please add the `api
    change` label.
    -->
---
 datafusion/functions/src/unicode/common.rs | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/datafusion/functions/src/unicode/common.rs 
b/datafusion/functions/src/unicode/common.rs
index 9f91e4f1b0..5dc8f334da 100644
--- a/datafusion/functions/src/unicode/common.rs
+++ b/datafusion/functions/src/unicode/common.rs
@@ -125,16 +125,22 @@ pub(crate) enum StringCharLen {
 /// Calculate the byte length of the substring of `n` chars from string 
`string`
 #[inline]
 fn left_right_byte_length(string: &str, n: i64) -> usize {
+    let abs = n.unsigned_abs().min(usize::MAX as u64) as usize;
+    // For ASCII input every character is exactly one byte, so the byte offset 
of
+    // the n-th codepoint is just the (clamped) character count. This avoids 
the
+    // per-character `char_indices()` scan of the general path.
     match n.cmp(&0) {
+        Ordering::Equal => 0,
+        // `abs` chars trimmed from the end: keep the leading `len - abs`.
+        Ordering::Less if string.is_ascii() => 
string.len().saturating_sub(abs),
         Ordering::Less => string
             .char_indices()
-            .nth_back((n.unsigned_abs().min(usize::MAX as u64) - 1) as usize)
+            .nth_back(abs - 1)
             .map(|(index, _)| index)
             .unwrap_or(0),
-        Ordering::Equal => 0,
-        Ordering::Greater => {
-            byte_offset_of_char(string, n.unsigned_abs().min(usize::MAX as 
u64) as usize)
-        }
+        // First `abs` chars, but never past the end of the string.
+        Ordering::Greater if string.is_ascii() => abs.min(string.len()),
+        Ordering::Greater => byte_offset_of_char(string, abs),
     }
 }
 


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

Reply via email to