Skip to content

perf: optimize array_replace for scalar needle#22387

Merged
Jefffrey merged 11 commits into
apache:mainfrom
lyne7-sc:perf/replace
May 25, 2026
Merged

perf: optimize array_replace for scalar needle#22387
Jefffrey merged 11 commits into
apache:mainfrom
lyne7-sc:perf/replace

Conversation

@lyne7-sc
Copy link
Copy Markdown
Contributor

Which issue does this PR close?

  • Closes #.

Rationale for this change

Currently, array_replace / array_replace_n / array_replace_all perform element-wise comparison by invoking compare_element_to_list against each row's sub-array individually. When the needle is a scalar, this can be optimized by performing a single vectorized not_distinct comparison over the entire flattened values buffer.

What changes are included in this PR?

  • Add a specialized replacement kernel that uses arrow_ord::cmp::not_distinct with Scalar wrapper for a single bulk comparison pass over the flat values buffer.
  • Extend SLT tests with multi-row scalar-argument coverage, empty-array edge cases, NULL needle replacement, and boundary n values for LargeList/FixedSizeList types.

Benchmarks

group                                                                      baseline                                optimized
-----                                                                      --------                                ---------
array_replace_all_int64/replace/list size: 10, num_rows: 4000              5.04  1124.5±146.98µs        ? ?/sec    1.00    223.1±2.79µs        ? ?/sec
array_replace_all_int64/replace/list size: 100, num_rows: 10000            1.64      7.2±0.59ms        ? ?/sec     1.00      4.4±0.12ms        ? ?/sec
array_replace_all_int64/replace/list size: 500, num_rows: 10000            1.16     25.3±4.09ms        ? ?/sec     1.00     21.8±0.69ms        ? ?/sec
array_replace_all_int64_nested/replace/list size: 10, num_rows: 4000       1.00      7.5±0.30ms        ? ?/sec     1.01      7.5±0.24ms        ? ?/sec
array_replace_all_int64_nested/replace/list size: 100, num_rows: 3000      1.00     38.5±0.52ms        ? ?/sec     1.02     39.2±1.02ms        ? ?/sec
array_replace_all_int64_nested/replace/list size: 300, num_rows: 1500      1.00     55.4±1.73ms        ? ?/sec     1.02     56.5±2.13ms        ? ?/sec
array_replace_boolean/replace/list size: 10, num_rows: 4000                4.57  1072.4±82.05µs        ? ?/sec     1.00    234.6±7.55µs        ? ?/sec
array_replace_boolean/replace/list size: 100, num_rows: 10000              2.38      3.7±0.43ms        ? ?/sec     1.00  1536.5±47.67µs        ? ?/sec
array_replace_boolean/replace/list size: 500, num_rows: 10000              1.51      6.5±0.51ms        ? ?/sec     1.00      4.3±0.12ms        ? ?/sec
array_replace_fixed_size_binary/replace/list size: 10, num_rows: 4000      3.61  1174.3±90.82µs        ? ?/sec     1.00   325.2±26.75µs        ? ?/sec
array_replace_fixed_size_binary/replace/list size: 100, num_rows: 10000    1.45      7.2±0.88ms        ? ?/sec     1.00      4.9±0.11ms        ? ?/sec
array_replace_fixed_size_binary/replace/list size: 500, num_rows: 10000    1.05     25.9±2.34ms        ? ?/sec     1.00     24.6±0.71ms        ? ?/sec
array_replace_int64/replace/list size: 10, num_rows: 4000                  5.49  1025.4±24.08µs        ? ?/sec     1.00   186.7±18.10µs        ? ?/sec
array_replace_int64/replace/list size: 100, num_rows: 10000                2.46      3.6±0.13ms        ? ?/sec     1.00  1455.7±138.70µs        ? ?/sec
array_replace_int64/replace/list size: 500, num_rows: 10000                1.26      7.0±0.75ms        ? ?/sec     1.00      5.6±0.77ms        ? ?/sec
array_replace_int64_nested/replace/list size: 10, num_rows: 4000           1.03      7.3±0.14ms        ? ?/sec     1.00      7.2±0.21ms        ? ?/sec
array_replace_int64_nested/replace/list size: 100, num_rows: 3000          1.03     37.8±1.62ms        ? ?/sec     1.00     36.7±0.43ms        ? ?/sec
array_replace_int64_nested/replace/list size: 300, num_rows: 1500          1.03     53.2±1.16ms        ? ?/sec     1.00     51.7±1.87ms        ? ?/sec
array_replace_n_int64/replace/list size: 10, num_rows: 4000                5.02  1074.4±30.92µs        ? ?/sec     1.00    214.1±2.22µs        ? ?/sec
array_replace_n_int64/replace/list size: 100, num_rows: 10000              1.83      5.0±0.15ms        ? ?/sec     1.00      2.7±0.06ms        ? ?/sec
array_replace_n_int64/replace/list size: 500, num_rows: 10000              1.17     15.5±1.11ms        ? ?/sec     1.00     13.3±0.24ms        ? ?/sec
array_replace_n_int64_nested/replace/list size: 10, num_rows: 4000         1.05      7.5±0.45ms        ? ?/sec     1.00      7.1±0.07ms        ? ?/sec
array_replace_n_int64_nested/replace/list size: 100, num_rows: 3000        1.02     37.4±0.51ms        ? ?/sec     1.00     36.5±0.62ms        ? ?/sec
array_replace_n_int64_nested/replace/list size: 300, num_rows: 1500        1.02     54.9±4.97ms        ? ?/sec     1.00     53.8±3.15ms        ? ?/sec
array_replace_strings/replace/list size: 10, num_rows: 4000                2.78  1408.8±44.99µs        ? ?/sec     1.00   506.6±16.32µs        ? ?/sec
array_replace_strings/replace/list size: 100, num_rows: 10000              1.32     11.0±1.25ms        ? ?/sec     1.00      8.3±0.37ms        ? ?/sec
array_replace_strings/replace/list size: 500, num_rows: 10000              1.14     42.4±6.39ms        ? ?/sec     1.00     37.2±0.74ms        ? ?/sec

Are these changes tested?

Yes, existing and new slt edge-case tests in array_replace.slt.

Are there any user-facing changes?

No.

@github-actions github-actions Bot added sqllogictest SQL Logic Tests (.slt) functions Changes to functions implementation labels May 20, 2026
Copy link
Copy Markdown
Contributor

@Jefffrey Jefffrey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One observation I have is this is a fast path for if from is a scalar, but it would be likely that to (and max too) might also be scalars in that case 🤔

Should we have the paths just be:

-- scalar fast path
select array_replace(array, scalar, scalar[, scalar]);
-- general fallback
select array_replace(array, scalar, array[, scalar]);
-- or any other combination

Thoughts?

Comment thread datafusion/functions-nested/src/replace.rs Outdated
Comment thread datafusion/functions-nested/src/replace.rs Outdated
Comment thread datafusion/functions-nested/src/replace.rs Outdated
@lyne7-sc
Copy link
Copy Markdown
Contributor Author

@Jefffrey Thanks for the review! I agree that to and max are also commonly scalar in practice and have already specialized them. but I think the performance gains here are relatively minor.

The other suggestions have already been fixed.

@Jefffrey
Copy link
Copy Markdown
Contributor

I was more thinking along the lines of explicitly expecting from/to/max to be scalars in the scalar path, instead of allowing them to be both scalar and array and having those code paths within the fast path. I guess what I'm trying to get at, is what behaviour is this optimization targeting? Like is there a common query pattern that this accelerates (where only from is scalar but to and max may be non-scalar)? Knowing this would help us know which code paths to support (otherwise we might introduce a lot of complexity to handle a code path that rarely occurs in practice 🤔)

@lyne7-sc
Copy link
Copy Markdown
Contributor Author

I see your concern, all scalars is the most common pattern in practice, and scoping the optimization to just that case makes sense. Weighing in the code complexity of supporting those edge cases, I agree it's better to keep things simple — I'll narrow the fast path to all-scalars and let the general path handle the rest.

Comment thread datafusion/functions-nested/src/replace.rs Outdated
Comment thread datafusion/functions-nested/src/replace.rs Outdated
Comment thread datafusion/functions-nested/src/replace.rs
Comment thread datafusion/functions-nested/src/replace.rs Outdated
Comment thread datafusion/functions-nested/src/replace.rs Outdated
@Jefffrey
Copy link
Copy Markdown
Contributor

run benchmark array_replace

@adriangbot
Copy link
Copy Markdown

🤖 Criterion benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c4526922152-305-wcmsk 6.12.68+ #1 SMP Wed Apr 1 02:23:28 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing perf/replace (7e348c2) to 4f45193 (merge-base) diff
BENCH_NAME=array_replace
BENCH_COMMAND=cargo bench --features=parquet --bench array_replace
BENCH_FILTER=
Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot
Copy link
Copy Markdown

🤖 Criterion benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

group                                                                      main                                   perf_replace
-----                                                                      ----                                   ------------
array_replace_all_int64/replace/list size: 10, num_rows: 4000              7.05  1697.9±11.78µs        ? ?/sec    1.00    240.7±1.63µs        ? ?/sec
array_replace_all_int64/replace/list size: 100, num_rows: 10000            2.65      8.4±0.04ms        ? ?/sec    1.00      3.2±0.02ms        ? ?/sec
array_replace_all_int64/replace/list size: 500, num_rows: 10000            1.69     26.6±0.16ms        ? ?/sec    1.00     15.7±0.13ms        ? ?/sec
array_replace_all_int64_nested/replace/list size: 10, num_rows: 4000       1.00     12.3±0.10ms        ? ?/sec    1.02     12.5±0.15ms        ? ?/sec
array_replace_all_int64_nested/replace/list size: 100, num_rows: 3000      1.06     72.7±1.16ms        ? ?/sec    1.00     68.9±0.84ms        ? ?/sec
array_replace_all_int64_nested/replace/list size: 300, num_rows: 1500      1.00     99.2±1.21ms        ? ?/sec    1.00     99.3±1.61ms        ? ?/sec
array_replace_boolean/replace/list size: 10, num_rows: 4000                4.81   1525.8±4.68µs        ? ?/sec    1.00    317.2±1.59µs        ? ?/sec
array_replace_boolean/replace/list size: 100, num_rows: 10000              2.90      4.8±0.01ms        ? ?/sec    1.00   1672.6±4.64µs        ? ?/sec
array_replace_boolean/replace/list size: 500, num_rows: 10000              1.58      8.4±0.02ms        ? ?/sec    1.00      5.3±0.01ms        ? ?/sec
array_replace_fixed_size_binary/replace/list size: 10, num_rows: 4000      5.56   1665.8±8.51µs        ? ?/sec    1.00    299.8±1.91µs        ? ?/sec
array_replace_fixed_size_binary/replace/list size: 100, num_rows: 10000    2.41      8.7±0.15ms        ? ?/sec    1.00      3.6±0.14ms        ? ?/sec
array_replace_fixed_size_binary/replace/list size: 500, num_rows: 10000    1.12     37.6±0.22ms        ? ?/sec    1.00     33.6±0.23ms        ? ?/sec
array_replace_int64/replace/list size: 10, num_rows: 4000                  7.25   1593.3±6.46µs        ? ?/sec    1.00    219.8±2.17µs        ? ?/sec
array_replace_int64/replace/list size: 100, num_rows: 10000                3.02      5.5±0.11ms        ? ?/sec    1.00  1822.2±88.37µs        ? ?/sec
array_replace_int64/replace/list size: 500, num_rows: 10000                1.00      9.8±0.19ms        ? ?/sec    1.44     14.2±0.12ms        ? ?/sec
array_replace_int64_nested/replace/list size: 10, num_rows: 4000           1.00     12.3±0.12ms        ? ?/sec    1.00     12.2±0.10ms        ? ?/sec
array_replace_int64_nested/replace/list size: 100, num_rows: 3000          1.00     67.8±0.83ms        ? ?/sec    1.00     67.9±0.78ms        ? ?/sec
array_replace_int64_nested/replace/list size: 300, num_rows: 1500          1.00     96.9±1.77ms        ? ?/sec    1.00     96.6±1.75ms        ? ?/sec
array_replace_n_int64/replace/list size: 10, num_rows: 4000                7.22   1705.5±7.10µs        ? ?/sec    1.00    236.2±1.74µs        ? ?/sec
array_replace_n_int64/replace/list size: 100, num_rows: 10000              3.19      6.8±0.10ms        ? ?/sec    1.00      2.1±0.03ms        ? ?/sec
array_replace_n_int64/replace/list size: 500, num_rows: 10000              1.72     19.1±0.11ms        ? ?/sec    1.00     11.1±0.20ms        ? ?/sec
array_replace_n_int64_nested/replace/list size: 10, num_rows: 4000         1.00     12.4±0.12ms        ? ?/sec    1.00     12.3±0.12ms        ? ?/sec
array_replace_n_int64_nested/replace/list size: 100, num_rows: 3000        1.00     68.1±0.82ms        ? ?/sec    1.00     68.2±1.04ms        ? ?/sec
array_replace_n_int64_nested/replace/list size: 300, num_rows: 1500        1.03    100.9±1.24ms        ? ?/sec    1.00     98.3±1.72ms        ? ?/sec
array_replace_strings/replace/list size: 10, num_rows: 4000                4.77   1996.2±6.94µs        ? ?/sec    1.00    418.1±1.47µs        ? ?/sec
array_replace_strings/replace/list size: 100, num_rows: 10000              1.66     11.3±0.08ms        ? ?/sec    1.00      6.8±0.06ms        ? ?/sec
array_replace_strings/replace/list size: 500, num_rows: 10000              1.16     43.0±1.64ms        ? ?/sec    1.00     37.1±0.37ms        ? ?/sec

Resource Usage

base (merge-base)

Metric Value
Wall time 310.1s
Peak memory 4.7 GiB
Avg memory 4.6 GiB
CPU user 358.1s
CPU sys 10.2s
Peak spill 0 B

branch

Metric Value
Wall time 295.1s
Peak memory 4.8 GiB
Avg memory 4.6 GiB
CPU user 337.0s
CPU sys 18.2s
Peak spill 0 B

File an issue against this benchmark runner

@Jefffrey
Copy link
Copy Markdown
Contributor

run benchmark array_replace

@adriangbot
Copy link
Copy Markdown

🤖 Criterion benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c4526965097-306-z8468 6.12.68+ #1 SMP Wed Apr 1 02:23:28 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing perf/replace (7e348c2) to 4f45193 (merge-base) diff
BENCH_NAME=array_replace
BENCH_COMMAND=cargo bench --features=parquet --bench array_replace
BENCH_FILTER=
Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot
Copy link
Copy Markdown

🤖 Criterion benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

group                                                                      main                                   perf_replace
-----                                                                      ----                                   ------------
array_replace_all_int64/replace/list size: 10, num_rows: 4000              7.09   1711.3±8.51µs        ? ?/sec    1.00    241.3±1.89µs        ? ?/sec
array_replace_all_int64/replace/list size: 100, num_rows: 10000            2.79      8.6±0.32ms        ? ?/sec    1.00      3.1±0.02ms        ? ?/sec
array_replace_all_int64/replace/list size: 500, num_rows: 10000            2.33     35.3±0.21ms        ? ?/sec    1.00     15.2±0.14ms        ? ?/sec
array_replace_all_int64_nested/replace/list size: 10, num_rows: 4000       1.00     12.4±0.12ms        ? ?/sec    1.00     12.4±0.13ms        ? ?/sec
array_replace_all_int64_nested/replace/list size: 100, num_rows: 3000      1.01     69.2±0.89ms        ? ?/sec    1.00     68.9±0.92ms        ? ?/sec
array_replace_all_int64_nested/replace/list size: 300, num_rows: 1500      1.02    100.4±2.51ms        ? ?/sec    1.00     98.9±1.08ms        ? ?/sec
array_replace_boolean/replace/list size: 10, num_rows: 4000                4.90  1552.6±14.01µs        ? ?/sec    1.00    316.9±1.61µs        ? ?/sec
array_replace_boolean/replace/list size: 100, num_rows: 10000              2.92      4.9±0.01ms        ? ?/sec    1.00   1672.1±5.03µs        ? ?/sec
array_replace_boolean/replace/list size: 500, num_rows: 10000              1.58      8.4±0.02ms        ? ?/sec    1.00      5.3±0.01ms        ? ?/sec
array_replace_fixed_size_binary/replace/list size: 10, num_rows: 4000      5.73  1684.1±11.23µs        ? ?/sec    1.00    293.7±2.16µs        ? ?/sec
array_replace_fixed_size_binary/replace/list size: 100, num_rows: 10000    2.23      8.0±0.19ms        ? ?/sec    1.00      3.6±0.06ms        ? ?/sec
array_replace_fixed_size_binary/replace/list size: 500, num_rows: 10000    1.08     35.6±0.16ms        ? ?/sec    1.00     32.9±0.53ms        ? ?/sec
array_replace_int64/replace/list size: 10, num_rows: 4000                  7.34  1609.7±14.74µs        ? ?/sec    1.00    219.3±2.34µs        ? ?/sec
array_replace_int64/replace/list size: 100, num_rows: 10000                4.52      5.2±0.04ms        ? ?/sec    1.00  1153.7±15.17µs        ? ?/sec
array_replace_int64/replace/list size: 500, num_rows: 10000                1.32     17.1±0.23ms        ? ?/sec    1.00     13.0±0.17ms        ? ?/sec
array_replace_int64_nested/replace/list size: 10, num_rows: 4000           1.01     12.3±0.12ms        ? ?/sec    1.00     12.2±0.10ms        ? ?/sec
array_replace_int64_nested/replace/list size: 100, num_rows: 3000          1.00     67.7±0.92ms        ? ?/sec    1.03     69.6±0.92ms        ? ?/sec
array_replace_int64_nested/replace/list size: 300, num_rows: 1500          1.01     98.1±1.73ms        ? ?/sec    1.00     97.0±1.71ms        ? ?/sec
array_replace_n_int64/replace/list size: 10, num_rows: 4000                7.27  1710.2±10.04µs        ? ?/sec    1.00    235.2±1.77µs        ? ?/sec
array_replace_n_int64/replace/list size: 100, num_rows: 10000              3.01      6.7±0.05ms        ? ?/sec    1.00      2.2±0.03ms        ? ?/sec
array_replace_n_int64/replace/list size: 500, num_rows: 10000              1.00     18.4±0.12ms        ? ?/sec    1.03     19.0±0.15ms        ? ?/sec
array_replace_n_int64_nested/replace/list size: 10, num_rows: 4000         1.00     12.4±0.13ms        ? ?/sec    1.00     12.4±0.14ms        ? ?/sec
array_replace_n_int64_nested/replace/list size: 100, num_rows: 3000        1.00     68.2±0.81ms        ? ?/sec    1.04     71.3±1.80ms        ? ?/sec
array_replace_n_int64_nested/replace/list size: 300, num_rows: 1500        1.01     98.2±1.52ms        ? ?/sec    1.00     97.5±1.19ms        ? ?/sec
array_replace_strings/replace/list size: 10, num_rows: 4000                4.87      2.0±0.01ms        ? ?/sec    1.00    412.8±1.37µs        ? ?/sec
array_replace_strings/replace/list size: 100, num_rows: 10000              1.62     10.9±0.04ms        ? ?/sec    1.00      6.8±0.04ms        ? ?/sec
array_replace_strings/replace/list size: 500, num_rows: 10000              1.25     47.2±2.12ms        ? ?/sec    1.00     37.7±0.99ms        ? ?/sec

Resource Usage

base (merge-base)

Metric Value
Wall time 300.1s
Peak memory 4.7 GiB
Avg memory 4.6 GiB
CPU user 341.5s
CPU sys 19.1s
Peak spill 0 B

branch

Metric Value
Wall time 295.1s
Peak memory 4.8 GiB
Avg memory 4.6 GiB
CPU user 333.8s
CPU sys 20.5s
Peak spill 0 B

File an issue against this benchmark runner

@lyne7-sc
Copy link
Copy Markdown
Contributor Author

All comments addressed, thanks for your review! @Jefffrey

@Jefffrey Jefffrey added this pull request to the merge queue May 25, 2026
@Jefffrey
Copy link
Copy Markdown
Contributor

Thanks @lyne7-sc

Merged via the queue into apache:main with commit de41306 May 25, 2026
35 checks passed
pull Bot pushed a commit to buraksenn/datafusion that referenced this pull request May 26, 2026
## Which issue does this PR close?

<!--
We generally require a GitHub issue to be filed for all bug fixes and
enhancements and this helps us generate change logs for our releases.
You can link an issue to this PR using the GitHub syntax. For example
`Closes #123` indicates that this PR will close issue #123.
-->

- Closes #.

## Rationale for this change

<!--
Why are you proposing this change? If this is already explained clearly
in the issue then this section is not needed.
Explaining clearly why changes are proposed helps reviewers understand
your changes and offer better suggestions for fixes.
-->

Similar to apache#22387 (array_replace scalar optimization)

`array_remove` / `array_remove_n` / `array_remove_all` perform
element-wise comparison by invoking `compare_element_to_list` against
each row's sub-array individually. When the needle is a scalar, this can
be optimized by performing a single vectorized `distinct` comparison
over the entire flattened values buffer.


## What changes are included in this PR?


- Add a specialized removal kernel (`general_remove_with_scalar`) that
uses `arrow_ord::cmp::distinct` with `Scalar` wrapper for a single bulk
comparison pass over the flat values buffer.
- Extend SLT tests with multi-row scalar-argument coverage,
NULL-containing arrays, empty-array edge cases, boundary `n` values, and
LargeList type coverage.

<!--
There is no need to duplicate the description in the issue here but it
is sometimes worth providing a summary of the individual changes in this
PR.
-->

### Benchmarks
```
group                                                                    main                                   optimized
-----                                                                    ----                                   ---------
array_remove_all_int64/remove/list size: 10, num_rows: 4000              4.35   856.8±97.81µs        ? ?/sec    1.00    196.9±4.48µs        ? ?/sec
array_remove_all_int64/remove/list size: 100, num_rows: 10000            1.90      5.5±0.09ms        ? ?/sec    1.00      2.9±0.09ms        ? ?/sec
array_remove_all_int64/remove/list size: 500, num_rows: 10000            1.35     19.2±0.21ms        ? ?/sec    1.00     14.2±0.48ms        ? ?/sec
array_remove_all_int64_nested/remove/list size: 10, num_rows: 4000       1.00      7.1±0.12ms        ? ?/sec    1.04      7.4±0.12ms        ? ?/sec
array_remove_all_int64_nested/remove/list size: 100, num_rows: 3000      1.00     36.5±0.39ms        ? ?/sec    1.05     38.3±2.61ms        ? ?/sec
array_remove_all_int64_nested/remove/list size: 300, num_rows: 1500      1.01     53.5±2.26ms        ? ?/sec    1.00     53.0±0.99ms        ? ?/sec
array_remove_boolean/remove/list size: 10, num_rows: 4000                3.83    813.9±7.08µs        ? ?/sec    1.00    212.4±2.28µs        ? ?/sec
array_remove_boolean/remove/list size: 100, num_rows: 10000              2.73      3.7±0.03ms        ? ?/sec    1.00  1364.7±177.83µs        ? ?/sec
array_remove_boolean/remove/list size: 500, num_rows: 10000              2.34      9.8±0.14ms        ? ?/sec    1.00      4.2±0.25ms        ? ?/sec
array_remove_fixed_size_binary/remove/list size: 10, num_rows: 4000      3.16   918.2±16.76µs        ? ?/sec    1.00    290.6±9.79µs        ? ?/sec
array_remove_fixed_size_binary/remove/list size: 100, num_rows: 10000    1.56      6.9±0.13ms        ? ?/sec    1.00      4.4±0.15ms        ? ?/sec
array_remove_fixed_size_binary/remove/list size: 500, num_rows: 10000    1.17     27.7±0.84ms        ? ?/sec    1.00     23.6±2.04ms        ? ?/sec
array_remove_int64/remove/list size: 10, num_rows: 4000                  4.55    825.7±6.30µs        ? ?/sec    1.00    181.3±4.32µs        ? ?/sec
array_remove_int64/remove/list size: 100, num_rows: 10000                3.35      3.8±0.11ms        ? ?/sec    1.00  1135.6±54.87µs        ? ?/sec
array_remove_int64/remove/list size: 500, num_rows: 10000                2.04     10.3±0.35ms        ? ?/sec    1.00      5.1±0.39ms        ? ?/sec
array_remove_int64_nested/remove/list size: 10, num_rows: 4000           1.00      7.1±0.18ms        ? ?/sec    1.02      7.2±0.07ms        ? ?/sec
array_remove_int64_nested/remove/list size: 100, num_rows: 3000          1.00     36.1±1.35ms        ? ?/sec    1.07     38.5±3.67ms        ? ?/sec
array_remove_int64_nested/remove/list size: 300, num_rows: 1500          1.00     51.7±0.57ms        ? ?/sec    1.05     54.1±2.13ms        ? ?/sec
array_remove_n_int64/remove/list size: 10, num_rows: 4000                4.43    845.3±5.00µs        ? ?/sec    1.00    190.6±2.84µs        ? ?/sec
array_remove_n_int64/remove/list size: 100, num_rows: 10000              2.29      4.7±0.11ms        ? ?/sec    1.00      2.0±0.12ms        ? ?/sec
array_remove_n_int64/remove/list size: 500, num_rows: 10000              1.63     14.8±0.42ms        ? ?/sec    1.00      9.0±0.51ms        ? ?/sec
array_remove_n_int64_nested/remove/list size: 10, num_rows: 4000         1.00      7.0±0.09ms        ? ?/sec    1.29      8.9±3.44ms        ? ?/sec
array_remove_n_int64_nested/remove/list size: 100, num_rows: 3000        1.00     36.6±0.42ms        ? ?/sec    1.03     37.7±0.68ms        ? ?/sec
array_remove_n_int64_nested/remove/list size: 300, num_rows: 1500        1.00     52.7±3.68ms        ? ?/sec    1.03     54.5±4.49ms        ? ?/sec
array_remove_strings/remove/list size: 10, num_rows: 4000                2.50  1144.6±21.95µs        ? ?/sec    1.00   457.0±14.15µs        ? ?/sec
array_remove_strings/remove/list size: 100, num_rows: 10000              1.42     10.5±1.16ms        ? ?/sec    1.00      7.4±0.34ms        ? ?/sec
array_remove_strings/remove/list size: 500, num_rows: 10000              1.12     39.8±0.91ms        ? ?/sec    1.00     35.5±1.51ms        ? ?/sec
```

## Are these changes tested?

<!--
We typically require tests for all PRs in order to:
1. Prevent the code from being accidentally broken by subsequent changes
2. Serve as another way to document the expected behavior of the code

If tests are not included in your PR, please explain why (for example,
are they covered by existing tests)?
-->

Yes, existing and new SLT edge-case tests in `array_remove.slt`.

## 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.
-->
@lyne7-sc lyne7-sc deleted the perf/replace branch May 26, 2026 01:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

functions Changes to functions implementation sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants