Skip to content

perf(arrow-buffer): faster count_set_bits on aarch64 by summing byte popcounts before widening - #11818

Open
mbutrovich wants to merge 7 commits into
apache:mainfrom
mbutrovich:count-set-bits-aarch64
Open

mbutrovich wants to merge 7 commits into
apache:mainfrom
mbutrovich:count-set-bits-aarch64

Conversation

@mbutrovich

@mbutrovich mbutrovich commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

See #11776.

What changes are included in this PR?

On aarch64, UnalignedBitChunk::count_ones sums byte popcounts in u8 lanes before widening them, as the issue proposes. The words after the last 64-byte chunk are counted with u64::count_ones. Other targets keep the u64::count_ones sum for all words. On all targets, count_ones now counts the prefix and suffix words separately from the aligned middle.

Are these changes tested?

Adds test_unaligned_bit_chunk_count_ones in arrow-buffer/src/util/bit_chunk_iterator.rs. It covers sizes around the 64-byte chunk and the u16 lane flush, with uniform and seeded random bytes.

The true_count cases of the boolean_array bench measure the change. This PR adds 448-bit and 960-bit cases, which end in 7 words after the last 64-byte chunk. On the bot's default runner (Google Axion, Neoverse V2), true_count without nulls is 1.87x faster at 65536 bits (results). A run with -C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6 gave the same result, so code layout doesn't explain it (results).

count-set-bits-bot

On amd64 (Xeon Platinum 8581C), the 65536-bit cases are unchanged within noise and the generated x86_64 inner loop is the same before and after (first, second).

The 64-bit and 1024-bit cases are faster on both architectures in the bot's build (thin LTO, 16 codegen units), where main copies the UnalignedBitChunk into an out-of-line Chain::fold. With codegen-units = 1, as DataFusion and Comet build, main inlines that fold, so I don't expect consumers to see most of that gain.

Are there any significant user-facing changes?

No.

AI Disclosure

I wrote this PR with help from Claude but understand and support the core approach, implementation, and test coverage.

@github-actions github-actions Bot added the arrow-core Core crates related to representing arrow data label Oct 22, 2026
@mbutrovich

Copy link
Copy Markdown
Contributor Author

run benchmark boolean_array

@mbutrovich

Copy link
Copy Markdown
Contributor Author

run benchmark boolean_array

resources:
  arch: amd64

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: boolean_array

Sharding factor: 1 (1 workers).

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
shards: 1

Results will be posted when all workers finish.


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: boolean_array

Sharding factor: 1 (1 workers).

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
resources:
  arch: amd64
shards: 1

Results will be posted when all workers finish.


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Target: boolean_array

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
shards: 1
Details
group                                base                                   changed
-----                                ----                                   -------
has_false(all_false, 1024)           1.00      6.5±0.01ns        ? ?/sec    1.01      6.6±0.04ns        ? ?/sec
has_false(all_false, 64)             1.00      7.2±0.10ns        ? ?/sec    1.00      7.3±0.14ns        ? ?/sec
has_false(all_false, 65536)          1.00      6.5±0.01ns        ? ?/sec    1.01      6.5±0.02ns        ? ?/sec
has_false(all_true, 1024)            1.00      7.0±0.01ns        ? ?/sec    1.02      7.1±0.02ns        ? ?/sec
has_false(all_true, 64)              1.00      7.8±0.09ns        ? ?/sec    1.03      8.0±0.10ns        ? ?/sec
has_false(all_true, 65536)           1.00     87.8±0.05ns        ? ?/sec    1.00     87.5±0.08ns        ? ?/sec
has_false(mixed_early, 1024)         1.04      6.8±0.02ns        ? ?/sec    1.00      6.6±0.01ns        ? ?/sec
has_false(mixed_early, 64)           1.04      7.5±0.15ns        ? ?/sec    1.00      7.2±0.12ns        ? ?/sec
has_false(mixed_early, 65536)        1.04      6.8±0.04ns        ? ?/sec    1.00      6.6±0.04ns        ? ?/sec
has_false(nulls_all_true, 1024)      1.00     17.9±0.02ns        ? ?/sec    1.01     18.0±0.01ns        ? ?/sec
has_false(nulls_all_true, 64)        1.00      5.0±0.00ns        ? ?/sec    1.00      5.0±0.01ns        ? ?/sec
has_false(nulls_all_true, 65536)     1.00    924.1±1.66ns        ? ?/sec    1.01    936.5±1.55ns        ? ?/sec
has_true(all_false, 1024)            1.00      6.5±0.05ns        ? ?/sec    1.01      6.6±0.01ns        ? ?/sec
has_true(all_false, 64)              1.00      7.1±0.15ns        ? ?/sec    1.00      7.1±0.11ns        ? ?/sec
has_true(all_false, 65536)           1.01     87.8±0.05ns        ? ?/sec    1.00     86.9±0.09ns        ? ?/sec
has_true(all_true, 1024)             1.00      6.7±0.04ns        ? ?/sec    1.00      6.7±0.03ns        ? ?/sec
has_true(all_true, 64)               1.00      7.1±0.10ns        ? ?/sec    1.02      7.2±0.09ns        ? ?/sec
has_true(all_true, 65536)            1.00      6.7±0.05ns        ? ?/sec    1.01      6.7±0.02ns        ? ?/sec
has_true(nulls_all_true, 1024)       1.00      3.7±0.00ns        ? ?/sec    1.01      3.7±0.01ns        ? ?/sec
has_true(nulls_all_true, 64)         1.00      3.7±0.00ns        ? ?/sec    1.01      3.7±0.01ns        ? ?/sec
has_true(nulls_all_true, 65536)      1.00      3.7±0.00ns        ? ?/sec    1.02      3.7±0.01ns        ? ?/sec
take_n_true_shared(32)               1.00     95.9±1.49ns        ? ?/sec    1.00     95.8±2.26ns        ? ?/sec
take_n_true_shared(32768)            1.00     16.6±0.12µs        ? ?/sec    1.01     16.7±0.05µs        ? ?/sec
take_n_true_shared(512)              1.02    348.8±5.55ns        ? ?/sec    1.00    343.2±2.65ns        ? ?/sec
take_n_true_unique(32)               1.00     71.4±1.74ns        ? ?/sec    1.01     72.2±1.82ns        ? ?/sec
take_n_true_unique(32768)            1.00     16.3±0.03µs        ? ?/sec    1.01     16.4±0.04µs        ? ?/sec
take_n_true_unique(512)              1.00    307.3±1.62ns        ? ?/sec    1.01    310.6±2.50ns        ? ?/sec
true_count(all_false, 1024)          1.08      9.0±0.02ns        ? ?/sec    1.00      8.3±0.02ns        ? ?/sec
true_count(all_false, 64)            1.39      9.9±1.14ns        ? ?/sec    1.00      7.1±0.11ns        ? ?/sec
true_count(all_false, 65536)         1.86    227.3±0.18ns        ? ?/sec    1.00    121.9±0.07ns        ? ?/sec
true_count(all_true, 1024)           1.07      9.2±0.04ns        ? ?/sec    1.00      8.6±0.02ns        ? ?/sec
true_count(all_true, 64)             1.33      9.9±1.04ns        ? ?/sec    1.00      7.5±0.17ns        ? ?/sec
true_count(all_true, 65536)          1.87    228.1±0.21ns        ? ?/sec    1.00    121.7±0.15ns        ? ?/sec
true_count(mixed_early, 1024)        1.11      9.3±0.02ns        ? ?/sec    1.00      8.3±0.02ns        ? ?/sec
true_count(mixed_early, 64)          1.40      9.9±0.88ns        ? ?/sec    1.00      7.1±0.13ns        ? ?/sec
true_count(mixed_early, 65536)       1.87    228.2±0.10ns        ? ?/sec    1.00    121.8±0.10ns        ? ?/sec
true_count(nulls_all_true, 1024)     1.01     21.3±0.49ns        ? ?/sec    1.00     21.1±0.02ns        ? ?/sec
true_count(nulls_all_true, 64)       1.00      4.8±0.01ns        ? ?/sec    1.01      4.9±0.01ns        ? ?/sec
true_count(nulls_all_true, 65536)    1.00   1037.1±0.22ns        ? ?/sec    1.00   1037.5±0.79ns        ? ?/sec

Per-runner information
boolean_array — shard 1/1

Node: gk3-benchmark-cluster-nap-5fy40jly-b59e93e3-db75

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

uname:

Linux bench-c6342675970-3760-zptt5 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench boolean_array
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
Resource Usage

base (merge-base)

Metric Value
Wall time 375.1s
Peak memory 227.5 MiB
Avg memory 18.8 MiB
CPU user 367.0s
CPU sys 3.3s
Peak spill 0 B

branch

Metric Value
Wall time 370.1s
Peak memory 298.1 MiB
Avg memory 19.0 MiB
CPU user 362.1s
CPU sys 3.3s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Target: boolean_array

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
resources:
  arch: amd64
shards: 1
Details
group                                base                                    changed
-----                                ----                                    -------
has_false(all_false, 1024)           1.07      7.6±0.61ns        ? ?/sec     1.00      7.1±0.39ns        ? ?/sec
has_false(all_false, 64)             1.02      6.9±0.69ns        ? ?/sec     1.00      6.8±0.45ns        ? ?/sec
has_false(all_false, 65536)          1.12      7.9±0.47ns        ? ?/sec     1.00      7.0±0.45ns        ? ?/sec
has_false(all_true, 1024)            1.01      8.5±0.51ns        ? ?/sec     1.00      8.4±0.50ns        ? ?/sec
has_false(all_true, 64)              1.00      7.7±0.40ns        ? ?/sec     1.11      8.6±0.52ns        ? ?/sec
has_false(all_true, 65536)           1.09     69.6±4.21ns        ? ?/sec     1.00     64.1±2.34ns        ? ?/sec
has_false(mixed_early, 1024)         1.08      7.6±0.35ns        ? ?/sec     1.00      7.0±0.34ns        ? ?/sec
has_false(mixed_early, 64)           1.00      6.6±0.37ns        ? ?/sec     1.03      6.9±0.45ns        ? ?/sec
has_false(mixed_early, 65536)        1.10      7.5±0.39ns        ? ?/sec     1.00      6.8±0.20ns        ? ?/sec
has_false(nulls_all_true, 1024)      1.01     20.2±0.95ns        ? ?/sec     1.00     20.0±1.66ns        ? ?/sec
has_false(nulls_all_true, 64)        1.08      7.3±0.34ns        ? ?/sec     1.00      6.7±0.58ns        ? ?/sec
has_false(nulls_all_true, 65536)     1.50  1429.8±90.92ns        ? ?/sec     1.00   953.1±41.92ns        ? ?/sec
has_true(all_false, 1024)            1.00      8.1±0.35ns        ? ?/sec     1.07      8.6±0.85ns        ? ?/sec
has_true(all_false, 64)              1.00      7.5±0.38ns        ? ?/sec     1.14      8.5±0.77ns        ? ?/sec
has_true(all_false, 65536)           1.01     66.8±4.03ns        ? ?/sec     1.00     65.8±3.01ns        ? ?/sec
has_true(all_true, 1024)             1.00      7.0±0.32ns        ? ?/sec     1.05      7.4±0.50ns        ? ?/sec
has_true(all_true, 64)               1.05      6.5±0.55ns        ? ?/sec     1.00      6.2±0.28ns        ? ?/sec
has_true(all_true, 65536)            1.02      7.1±0.48ns        ? ?/sec     1.00      7.0±0.21ns        ? ?/sec
has_true(nulls_all_true, 1024)       1.20      6.3±0.41ns        ? ?/sec     1.00      5.2±0.29ns        ? ?/sec
has_true(nulls_all_true, 64)         1.22      6.3±0.49ns        ? ?/sec     1.00      5.1±0.20ns        ? ?/sec
has_true(nulls_all_true, 65536)      1.25      6.4±0.40ns        ? ?/sec     1.00      5.1±0.15ns        ? ?/sec
take_n_true_shared(32)               1.03     87.5±5.13ns        ? ?/sec     1.00     84.7±4.64ns        ? ?/sec
take_n_true_shared(32768)            1.07     21.1±2.06µs        ? ?/sec     1.00     19.7±0.49µs        ? ?/sec
take_n_true_shared(512)              1.03   407.1±21.85ns        ? ?/sec     1.00   396.4±44.22ns        ? ?/sec
take_n_true_unique(32)               1.12     58.8±4.29ns        ? ?/sec     1.00     52.3±2.81ns        ? ?/sec
take_n_true_unique(32768)            1.13     22.1±1.81µs        ? ?/sec     1.00     19.6±0.66µs        ? ?/sec
take_n_true_unique(512)              1.00   339.0±16.97ns        ? ?/sec     1.05   355.1±23.98ns        ? ?/sec
true_count(all_false, 1024)          1.27     14.9±0.61ns        ? ?/sec     1.00     11.7±0.68ns        ? ?/sec
true_count(all_false, 64)            1.89     14.1±0.71ns        ? ?/sec     1.00      7.5±0.41ns        ? ?/sec
true_count(all_false, 65536)         1.03   608.5±27.50ns        ? ?/sec     1.00   593.2±20.97ns        ? ?/sec
true_count(all_true, 1024)           1.30     15.2±0.94ns        ? ?/sec     1.00     11.7±0.70ns        ? ?/sec
true_count(all_true, 64)             1.79     14.1±0.64ns        ? ?/sec     1.00      7.9±0.49ns        ? ?/sec
true_count(all_true, 65536)          1.04   612.1±28.00ns        ? ?/sec     1.00   591.3±11.17ns        ? ?/sec
true_count(mixed_early, 1024)        1.32     15.5±0.89ns        ? ?/sec     1.00     11.8±0.81ns        ? ?/sec
true_count(mixed_early, 64)          1.91     14.6±0.94ns        ? ?/sec     1.00      7.6±0.47ns        ? ?/sec
true_count(mixed_early, 65536)       1.05   623.6±39.86ns        ? ?/sec     1.00   595.0±19.78ns        ? ?/sec
true_count(nulls_all_true, 1024)     1.01     34.1±1.91ns        ? ?/sec     1.00     33.7±2.08ns        ? ?/sec
true_count(nulls_all_true, 64)       1.00      7.8±0.36ns        ? ?/sec     1.17      9.1±0.74ns        ? ?/sec
true_count(nulls_all_true, 65536)    1.06  1853.8±133.67ns        ? ?/sec    1.00  1756.3±79.86ns        ? ?/sec

Per-runner information
boolean_array — shard 1/1

Node: gk3-benchmark-cluster-nap-vp60frle-472b4730-cln4

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

uname:

Linux bench-c6342678146-3761-5xk6p 6.12.94+ #1 SMP Fri Aug 21 07:56:42 UTC 2026 x86_64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench boolean_array
CPU Details (lscpu)
Architecture:                            x86_64
CPU op-mode(s):                          32-bit, 64-bit
Address sizes:                           52 bits physical, 57 bits virtual
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               GenuineIntel
Model name:                              INTEL(R) XEON(R) PLATINUM 8581C CPU @ 2.30GHz
CPU family:                              6
Model:                                   207
Thread(s) per core:                      2
Core(s) per socket:                      8
Socket(s):                               1
Stepping:                                2
BogoMIPS:                                4600.00
Flags:                                   fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves avx_vnni avx512_bf16 wbnoinvd arat avx512vbmi umip avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid cldemote movdiri movdir64b fsrm md_clear serialize tsxldtrk amx_bf16 avx512_fp16 amx_tile amx_int8 arch_capabilities
Hypervisor vendor:                       KVM
Virtualization type:                     full
L1d cache:                               384 KiB (8 instances)
L1i cache:                               256 KiB (8 instances)
L2 cache:                                16 MiB (8 instances)
L3 cache:                                260 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; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; Enhanced / Automatic IBRS; IBPB conditional; PBRSB-eIBRS SW sequence; BHI BHI_DIS_S
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 370.1s
Peak memory 286.5 MiB
Avg memory 25.1 MiB
CPU user 364.2s
CPU sys 4.0s
Peak spill 0 B

branch

Metric Value
Wall time 370.1s
Peak memory 324.3 MiB
Avg memory 20.0 MiB
CPU user 362.7s
CPU sys 4.8s
Peak spill 0 B

File an issue against this benchmark runner

@mbutrovich

Copy link
Copy Markdown
Contributor Author

run benchmark boolean_array

resources:
  arch: amd64

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: boolean_array

Sharding factor: 1 (1 workers).

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
resources:
  arch: amd64
shards: 1

Results will be posted when all workers finish.


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Target: boolean_array

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
resources:
  arch: amd64
shards: 1
Details
group                                base                                    changed
-----                                ----                                    -------
has_false(all_false, 1024)           1.05      7.8±0.46ns        ? ?/sec     1.00      7.4±0.43ns        ? ?/sec
has_false(all_false, 64)             1.00      6.6±0.26ns        ? ?/sec     1.19      7.8±0.15ns        ? ?/sec
has_false(all_false, 65536)          1.10      7.6±0.42ns        ? ?/sec     1.00      7.0±0.28ns        ? ?/sec
has_false(all_true, 1024)            1.00      8.6±0.60ns        ? ?/sec     1.05      9.1±0.49ns        ? ?/sec
has_false(all_true, 64)              1.00      7.8±0.51ns        ? ?/sec     1.24      9.7±0.27ns        ? ?/sec
has_false(all_true, 65536)           1.19     77.4±6.61ns        ? ?/sec     1.00     64.8±1.90ns        ? ?/sec
has_false(mixed_early, 1024)         1.00      7.8±0.54ns        ? ?/sec     1.03      8.0±0.37ns        ? ?/sec
has_false(mixed_early, 64)           1.00      6.7±0.32ns        ? ?/sec     1.19      7.9±0.18ns        ? ?/sec
has_false(mixed_early, 65536)        1.08      7.7±0.45ns        ? ?/sec     1.00      7.1±0.37ns        ? ?/sec
has_false(nulls_all_true, 1024)      1.00     20.9±1.30ns        ? ?/sec     1.06     22.1±0.93ns        ? ?/sec
has_false(nulls_all_true, 64)        1.02      7.6±0.46ns        ? ?/sec     1.00      7.5±0.42ns        ? ?/sec
has_false(nulls_all_true, 65536)     1.45  1405.6±60.58ns        ? ?/sec     1.00   967.5±60.78ns        ? ?/sec
has_true(all_false, 1024)            1.00      8.3±0.47ns        ? ?/sec     1.05      8.7±0.59ns        ? ?/sec
has_true(all_false, 64)              1.00      7.7±0.46ns        ? ?/sec     1.21      9.4±0.26ns        ? ?/sec
has_true(all_false, 65536)           1.00     66.3±3.56ns        ? ?/sec     1.01     67.2±3.28ns        ? ?/sec
has_true(all_true, 1024)             1.00      7.1±0.39ns        ? ?/sec     1.12      7.9±0.43ns        ? ?/sec
has_true(all_true, 64)               1.00      6.5±0.41ns        ? ?/sec     1.14      7.4±0.34ns        ? ?/sec
has_true(all_true, 65536)            1.00      7.3±0.43ns        ? ?/sec     1.00      7.3±0.37ns        ? ?/sec
has_true(nulls_all_true, 1024)       1.29      7.1±0.58ns        ? ?/sec     1.00      5.5±0.29ns        ? ?/sec
has_true(nulls_all_true, 64)         1.18      6.9±0.70ns        ? ?/sec     1.00      5.8±0.18ns        ? ?/sec
has_true(nulls_all_true, 65536)      1.27      6.5±0.43ns        ? ?/sec     1.00      5.1±0.22ns        ? ?/sec
take_n_true_shared(32)               1.00     86.1±5.37ns        ? ?/sec     1.08     93.2±3.59ns        ? ?/sec
take_n_true_shared(32768)            1.00     22.0±1.46µs        ? ?/sec     1.00     22.0±2.08µs        ? ?/sec
take_n_true_shared(512)              1.00   387.5±21.85ns        ? ?/sec     1.02   397.1±22.90ns        ? ?/sec
take_n_true_unique(32)               1.03     58.0±4.68ns        ? ?/sec     1.00     56.5±2.83ns        ? ?/sec
take_n_true_unique(32768)            1.06     21.1±1.60µs        ? ?/sec     1.00     19.9±0.88µs        ? ?/sec
take_n_true_unique(512)              1.00   346.2±20.74ns        ? ?/sec     1.02   353.9±20.68ns        ? ?/sec
true_count(all_false, 1024)          1.29     16.2±1.12ns        ? ?/sec     1.00     12.6±0.61ns        ? ?/sec
true_count(all_false, 64)            1.68     14.5±0.86ns        ? ?/sec     1.00      8.6±0.40ns        ? ?/sec
true_count(all_false, 65536)         1.01   613.0±27.21ns        ? ?/sec     1.00   609.1±31.88ns        ? ?/sec
true_count(all_true, 1024)           1.16     15.1±0.87ns        ? ?/sec     1.00     13.0±0.68ns        ? ?/sec
true_count(all_true, 64)             1.75     14.0±0.59ns        ? ?/sec     1.00      8.0±0.58ns        ? ?/sec
true_count(all_true, 65536)          1.00   623.5±36.46ns        ? ?/sec     1.09   680.8±36.87ns        ? ?/sec
true_count(mixed_early, 1024)        1.23     15.9±1.00ns        ? ?/sec     1.00     12.9±0.61ns        ? ?/sec
true_count(mixed_early, 64)          1.57     14.0±0.75ns        ? ?/sec     1.00      8.9±0.32ns        ? ?/sec
true_count(mixed_early, 65536)       1.00   610.7±25.26ns        ? ?/sec     1.11   678.3±51.79ns        ? ?/sec
true_count(nulls_all_true, 1024)     1.00     34.6±2.09ns        ? ?/sec     1.07     37.0±1.92ns        ? ?/sec
true_count(nulls_all_true, 64)       1.00      8.0±0.33ns        ? ?/sec     1.23      9.8±0.60ns        ? ?/sec
true_count(nulls_all_true, 65536)    1.05  1925.0±188.29ns        ? ?/sec    1.00  1826.2±105.05ns        ? ?/sec

Per-runner information
boolean_array — shard 1/1

Node: gk3-benchmark-cluster-nap-1uz6vgaz-77581093-zsnw

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

uname:

Linux bench-c6343143235-3762-hm5cn 6.12.94+ #1 SMP Fri Aug 21 07:56:42 UTC 2026 x86_64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench boolean_array
CPU Details (lscpu)
Architecture:                            x86_64
CPU op-mode(s):                          32-bit, 64-bit
Address sizes:                           52 bits physical, 57 bits virtual
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               GenuineIntel
Model name:                              INTEL(R) XEON(R) PLATINUM 8581C CPU @ 2.30GHz
CPU family:                              6
Model:                                   207
Thread(s) per core:                      2
Core(s) per socket:                      8
Socket(s):                               1
Stepping:                                2
BogoMIPS:                                4600.00
Flags:                                   fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves avx_vnni avx512_bf16 wbnoinvd arat avx512vbmi umip avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid cldemote movdiri movdir64b fsrm md_clear serialize tsxldtrk amx_bf16 avx512_fp16 amx_tile amx_int8 arch_capabilities
Hypervisor vendor:                       KVM
Virtualization type:                     full
L1d cache:                               384 KiB (8 instances)
L1i cache:                               256 KiB (8 instances)
L2 cache:                                16 MiB (8 instances)
L3 cache:                                260 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; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; Enhanced / Automatic IBRS; IBPB conditional; PBRSB-eIBRS SW sequence; BHI BHI_DIS_S
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 375.1s
Peak memory 320.7 MiB
Avg memory 24.9 MiB
CPU user 367.4s
CPU sys 3.8s
Peak spill 0 B

branch

Metric Value
Wall time 380.1s
Peak memory 350.0 MiB
Avg memory 19.5 MiB
CPU user 370.6s
CPU sys 4.8s
Peak spill 0 B

File an issue against this benchmark runner

@mbutrovich

Copy link
Copy Markdown
Contributor Author

run benchmark boolean_array

env:
  BENCH_FILTER: "true_count"
  RUSTFLAGS: "-C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6"

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: boolean_array

Sharding factor: 1 (1 workers).

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
env:
  BENCH_FILTER: true_count
  RUSTFLAGS: -C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6
shards: 1

Results will be posted when all workers finish.


File an issue against this benchmark runner

@mbutrovich

Copy link
Copy Markdown
Contributor Author

run benchmark boolean_array

env:
  BENCH_FILTER: "true_count"
  RUSTFLAGS: "-C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6"
resources:
  arch: amd64

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: boolean_array

Sharding factor: 1 (1 workers).

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
env:
  BENCH_FILTER: true_count
  RUSTFLAGS: -C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6
resources:
  arch: amd64
shards: 1

Results will be posted when all workers finish.


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Target: boolean_array

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
env:
  BENCH_FILTER: true_count
  RUSTFLAGS: -C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6
shards: 1
Details
group                                base                                   changed
-----                                ----                                   -------
true_count(all_false, 1024)          1.09      9.1±0.01ns        ? ?/sec    1.00      8.3±0.01ns        ? ?/sec
true_count(all_false, 64)            1.38      9.9±1.04ns        ? ?/sec    1.00      7.1±0.11ns        ? ?/sec
true_count(all_false, 65536)         1.89    229.6±0.17ns        ? ?/sec    1.00    121.5±0.04ns        ? ?/sec
true_count(all_true, 1024)           1.09      9.3±0.09ns        ? ?/sec    1.00      8.5±0.03ns        ? ?/sec
true_count(all_true, 64)             1.32      9.8±1.00ns        ? ?/sec    1.00      7.4±0.11ns        ? ?/sec
true_count(all_true, 65536)          1.88    228.5±0.17ns        ? ?/sec    1.00    121.6±0.05ns        ? ?/sec
true_count(mixed_early, 1024)        1.11      9.2±0.02ns        ? ?/sec    1.00      8.3±0.01ns        ? ?/sec
true_count(mixed_early, 64)          1.40     10.0±1.06ns        ? ?/sec    1.00      7.2±0.08ns        ? ?/sec
true_count(mixed_early, 65536)       1.88    228.8±0.08ns        ? ?/sec    1.00    121.4±0.03ns        ? ?/sec
true_count(nulls_all_true, 1024)     1.01     15.6±0.04ns        ? ?/sec    1.00     15.5±0.02ns        ? ?/sec
true_count(nulls_all_true, 64)       1.00      4.9±0.02ns        ? ?/sec    1.07      5.3±0.00ns        ? ?/sec
true_count(nulls_all_true, 65536)    1.00    721.0±0.33ns        ? ?/sec    1.00    719.8±0.27ns        ? ?/sec

Per-runner information
boolean_array — shard 1/1

Node: gk3-benchmark-cluster-nap-rw8ujupk-523dc0ee-zbk6

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

uname:

Linux bench-c6343835403-3763-whhjh 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench boolean_array
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
Resource Usage

base (merge-base)

Metric Value
Wall time 120.0s
Peak memory 11.5 MiB
Avg memory 10.4 MiB
CPU user 116.9s
CPU sys 0.0s
Peak spill 0 B

branch

Metric Value
Wall time 115.0s
Peak memory 11.0 MiB
Avg memory 10.2 MiB
CPU user 112.1s
CPU sys 0.0s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Target: boolean_array

Comparing count-set-bits-aarch64 (dcfd4da) to 01a9917 (merge-base) diff

Run configuration
run benchmark boolean_array
env:
  BENCH_FILTER: true_count
  RUSTFLAGS: -C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6
resources:
  arch: amd64
shards: 1
Details
group                                base                                   changed
-----                                ----                                   -------
true_count(all_false, 1024)          1.35     15.2±0.92ns        ? ?/sec    1.00     11.3±0.19ns        ? ?/sec
true_count(all_false, 64)            2.28     14.2±1.07ns        ? ?/sec    1.00      6.2±0.15ns        ? ?/sec
true_count(all_false, 65536)         1.00   597.4±22.39ns        ? ?/sec    1.03   616.7±37.37ns        ? ?/sec
true_count(all_true, 1024)           1.34     15.1±0.72ns        ? ?/sec    1.00     11.3±0.17ns        ? ?/sec
true_count(all_true, 64)             2.17     13.7±0.39ns        ? ?/sec    1.00      6.3±0.35ns        ? ?/sec
true_count(all_true, 65536)          1.06   629.2±38.17ns        ? ?/sec    1.00   592.4±14.89ns        ? ?/sec
true_count(mixed_early, 1024)        1.31     14.9±0.59ns        ? ?/sec    1.00     11.4±0.37ns        ? ?/sec
true_count(mixed_early, 64)          2.23     14.6±0.89ns        ? ?/sec    1.00      6.5±0.48ns        ? ?/sec
true_count(mixed_early, 65536)       1.01   601.9±25.43ns        ? ?/sec    1.00   594.3±24.01ns        ? ?/sec
true_count(nulls_all_true, 1024)     1.00     31.6±1.18ns        ? ?/sec    1.00     31.7±1.48ns        ? ?/sec
true_count(nulls_all_true, 64)       1.04      7.4±0.46ns        ? ?/sec    1.00      7.1±0.22ns        ? ?/sec
true_count(nulls_all_true, 65536)    1.00  1678.6±37.45ns        ? ?/sec    1.00  1678.1±28.79ns        ? ?/sec

Per-runner information
boolean_array — shard 1/1

Node: gk3-benchmark-cluster-nap-1bw8wop1-2d2ce690-zt4s

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

uname:

Linux bench-c6343839257-3764-b4t5z 6.12.94+ #1 SMP Fri Aug 21 07:56:42 UTC 2026 x86_64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench boolean_array
CPU Details (lscpu)
Architecture:                            x86_64
CPU op-mode(s):                          32-bit, 64-bit
Address sizes:                           52 bits physical, 57 bits virtual
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               GenuineIntel
Model name:                              INTEL(R) XEON(R) PLATINUM 8581C CPU @ 2.30GHz
CPU family:                              6
Model:                                   207
Thread(s) per core:                      2
Core(s) per socket:                      8
Socket(s):                               1
Stepping:                                2
BogoMIPS:                                4600.00
Flags:                                   fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves avx_vnni avx512_bf16 wbnoinvd arat avx512vbmi umip avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid cldemote movdiri movdir64b fsrm md_clear serialize tsxldtrk amx_bf16 avx512_fp16 amx_tile amx_int8 arch_capabilities
Hypervisor vendor:                       KVM
Virtualization type:                     full
L1d cache:                               384 KiB (8 instances)
L1i cache:                               256 KiB (8 instances)
L2 cache:                                16 MiB (8 instances)
L3 cache:                                260 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; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; Enhanced / Automatic IBRS; IBPB conditional; PBRSB-eIBRS SW sequence; BHI BHI_DIS_S
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Resource Usage

base (merge-base)

Metric Value
Wall time 115.0s
Peak memory 12.3 MiB
Avg memory 11.2 MiB
CPU user 112.0s
CPU sys 0.0s
Peak spill 0 B

branch

Metric Value
Wall time 110.0s
Peak memory 12.4 MiB
Avg memory 11.5 MiB
CPU user 107.1s
CPU sys 0.0s
Peak spill 0 B

File an issue against this benchmark runner

@mbutrovich

Copy link
Copy Markdown
Contributor Author

Marking draft so I can open some bug-fix PRs. Reviews are still welcome, I just can't have more PRs open.

@mbutrovich
mbutrovich marked this pull request as draft October 22, 2026 19:18
@mbutrovich
mbutrovich marked this pull request as ready for review October 23, 2026 23:00
@mbutrovich

Copy link
Copy Markdown
Contributor Author

cc @devanbenz @mightsleep since you gave such great feedback on #11467.

@devanbenz

Copy link
Copy Markdown
Contributor

I should have some time tomorrow or in the next few days to take a look at this. 🫡

@andygrove

Copy link
Copy Markdown
Member

I relied on AI to help with this review, and I ran the checks below myself. Please push back if I've got something wrong.

Thanks @mbutrovich. The approach looks right to me.

Correctness

  • A u8 lane sums at most four byte popcounts (4 × 8 = 32), so it can't overflow. CHUNKS_PER_FLUSH = u16::MAX / 32 = 2047 keeps each u16 lane at or below 65504, so one flush per 2047 chunks is safe.
  • Counting prefix/suffix separately from chunks covers the same words as iter().
  • I checked that test_unaligned_bit_chunk_count_ones actually guards the flush boundary. I bumped CHUNKS_PER_FLUSH by one and the test failed in debug (add overflow) and in release (it returned 0 instead of 1048576), thanks to the 131_008/131_072/131_136-byte sizes.

Benchmark on Apple Silicon
boolean_array true_count, compared against the PR's parent commit:

Case Change
65536 bits, no nulls ~24% faster
1024 bits, no nulls ~4% faster
64 bits, no nulls (all_true, all_false, mixed_early) ~5% slower (5.69 → 5.96 ns)

Suggestion: count the tail in words, not bytes
count_ones_words counts the remainder after the 64-byte chunks one byte at a time. words is always a whole number of u64s, so the remainder is at most 7 words. A 64-bit input is a single word, so it goes entirely through the byte loop and loses its single popcnt. That's my best explanation for the 64-bit regression above.

I tried this instead:

let tail = &words[words.len() - remainder.len() / 8..];
total + tail.iter().map(|w| w.count_ones() as usize).sum::<usize>()

test_unaligned_bit_chunk_count_ones still passes. The 64-bit cases went back to baseline (no significant change), and the 1024-bit cases improved from ~4% to ~12% faster. This was one run on one machine, and your bot results show the 64-bit cases as faster on Axion, so some of what I saw may be code layout. The change is small and reads more directly, so I think it's worth taking either way.

Nits

  • The test only uses uniform bytes (0xFF, 0xA5, 0x00). That's fine today because the sum doesn't depend on lane order, but one case with random bytes would guard against a future change to the lane indexing.
  • You note that with codegen-units = 1 consumers won't see most of the 64-bit/1024-bit gain. That seems right, so I'd treat the 65536-bit numbers as the real benefit.

I couldn't test the non-aarch64 count_ones_words here (no x86 target), but it looks like the old code moved into a helper.

@mbutrovich

Copy link
Copy Markdown
Contributor Author

run benchmark boolean_array

baseline:
  ref: f6c3989363
env:
  BENCH_FILTER: "true_count"

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: boolean_array

Sharding factor: 1 (1 workers).

Comparing count-set-bits-aarch64 (110c2e8) to f6c3989 diff

Run configuration
run benchmark boolean_array
baseline:
  ref: f6c3989363
env:
  BENCH_FILTER: true_count
shards: 1

Results will be posted when all workers finish.


File an issue against this benchmark runner

@mbutrovich

Copy link
Copy Markdown
Contributor Author

run benchmark boolean_array

baseline:
  ref: f6c3989363
env:
  BENCH_FILTER: "true_count"
  RUSTFLAGS: "-C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6"

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark starting (GKE) | trigger

Target: boolean_array

Sharding factor: 1 (1 workers).

Comparing count-set-bits-aarch64 (110c2e8) to f6c3989 diff

Run configuration
run benchmark boolean_array
baseline:
  ref: f6c3989363
env:
  BENCH_FILTER: true_count
  RUSTFLAGS: -C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6
shards: 1

Results will be posted when all workers finish.


File an issue against this benchmark runner

@mbutrovich

Copy link
Copy Markdown
Contributor Author

Thanks @andygrove. I switched the tail to count words. I think it reads better, and it gets a bit of speedup.

A 64-bit input is a single word, so it goes entirely through the byte loop and loses its single popcnt. That's my best explanation for the 64-bit regression above.

A 64-bit input doesn't reach count_ones_words. UnalignedBitChunk::new puts a buffer of at most 8 bytes in prefix (bit_chunk_iterator.rs), so chunks is empty. The 1024-bit and 65536-bit arrays in the bench are aligned and a whole number of 64-byte chunks, so their remainder is empty too. None of the existing true_count cases run the tail, so I read the 64-bit and 1024-bit differences as code layout. On an M5 Max, rerunning the PR head against its own baseline moved the 64-bit cases by 3.5%.

The tail runs when the aligned middle isn't a multiple of 8 words. I added 448-bit (7 tail words, no full chunk) and 960-bit (one chunk and 7 tail words) cases to the bench. Per word, the byte tail compiles to cnt.8b, two tbl, and four uaddw.2d. The word tail compiles to cnt.16b, udot, and uadalp per two words. These are the all_true cases on an M5 Max with the default bench profile:

bits byte tail word tail change
448 5.67 ns 4.50 ns -20.5%
960 6.63 ns 5.12 ns -22.4%

The 64, 1024, and 65536-bit cases moved by less than 3.1%. With -C llvm-args=-align-all-functions=7 -C llvm-args=-align-all-nofallthru-blocks=7, the 448-bit and 960-bit cases were 26.5% and 24.4% faster.

I split with words.as_chunks::<8>() instead of slicing words by remainder.len() / 8, so the tail comes from the split. Both compile to the same loops.

one case with random bytes would guard against a future change to the lane indexing.

I added a seeded random input to test_unaligned_bit_chunk_count_ones. Changing chunk[16 + lane] to chunk[lane] fails on the random input and passes on the uniform ones.

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Target: boolean_array

Comparing count-set-bits-aarch64 (110c2e8) to f6c3989 diff

Run configuration
run benchmark boolean_array
baseline:
  ref: f6c3989363
env:
  BENCH_FILTER: true_count
  RUSTFLAGS: -C llvm-args=-align-all-functions=6 -C llvm-args=-align-all-nofallthru-blocks=6
shards: 1
Details
group                                base                                   changed
-----                                ----                                   -------
true_count(all_false, 1024)          1.00      8.3±0.01ns        ? ?/sec    1.00      8.3±0.02ns        ? ?/sec
true_count(all_false, 448)           1.42     11.6±0.04ns        ? ?/sec    1.00      8.2±0.04ns        ? ?/sec
true_count(all_false, 64)            1.00      7.1±0.11ns        ? ?/sec    1.00      7.1±0.13ns        ? ?/sec
true_count(all_false, 65536)         1.00    121.7±0.05ns        ? ?/sec    1.00    121.4±0.05ns        ? ?/sec
true_count(all_false, 960)           1.42     14.9±0.15ns        ? ?/sec    1.00     10.5±0.03ns        ? ?/sec
true_count(all_true, 1024)           1.00      8.3±0.01ns        ? ?/sec    1.03      8.5±0.01ns        ? ?/sec
true_count(all_true, 448)            1.39     11.6±0.03ns        ? ?/sec    1.00      8.4±0.04ns        ? ?/sec
true_count(all_true, 64)             1.00      7.1±0.12ns        ? ?/sec    1.01      7.2±0.13ns        ? ?/sec
true_count(all_true, 65536)          1.00    121.5±0.04ns        ? ?/sec    1.00    121.6±0.07ns        ? ?/sec
true_count(all_true, 960)            1.41     14.9±0.14ns        ? ?/sec    1.00     10.6±0.04ns        ? ?/sec
true_count(mixed_early, 1024)        1.01      8.6±0.01ns        ? ?/sec    1.00      8.5±0.03ns        ? ?/sec
true_count(mixed_early, 448)         1.39     11.6±0.02ns        ? ?/sec    1.00      8.4±0.03ns        ? ?/sec
true_count(mixed_early, 64)          1.00      7.3±0.11ns        ? ?/sec    1.01      7.4±0.15ns        ? ?/sec
true_count(mixed_early, 65536)       1.00    121.6±0.13ns        ? ?/sec    1.00    121.6±0.04ns        ? ?/sec
true_count(mixed_early, 960)         1.41     15.0±0.15ns        ? ?/sec    1.00     10.6±0.04ns        ? ?/sec
true_count(nulls_all_true, 1024)     1.01     15.7±0.03ns        ? ?/sec    1.00     15.6±0.06ns        ? ?/sec
true_count(nulls_all_true, 448)      1.02      9.6±0.01ns        ? ?/sec    1.00      9.5±0.02ns        ? ?/sec
true_count(nulls_all_true, 64)       1.01      4.9±0.02ns        ? ?/sec    1.00      4.9±0.01ns        ? ?/sec
true_count(nulls_all_true, 65536)    1.00    719.6±0.45ns        ? ?/sec    1.00    719.9±0.35ns        ? ?/sec
true_count(nulls_all_true, 960)      1.04     15.7±0.45ns        ? ?/sec    1.00     15.2±0.28ns        ? ?/sec

Per-runner information
boolean_array — shard 1/1

Node: gk3-benchmark-cluster-nap-1mvvlccl-0352b74c-j27r

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

uname:

Linux bench-c6511086795-4129-mwf9s 6.12.105+ #1 SMP Sat Sep 19 07:55:25 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench boolean_array
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
Resource Usage

base (merge-base)

Metric Value
Wall time 190.0s
Peak memory 11.5 MiB
Avg memory 10.7 MiB
CPU user 185.0s
CPU sys 0.0s
Peak spill 0 B

branch

Metric Value
Wall time 195.0s
Peak memory 11.5 MiB
Avg memory 10.9 MiB
CPU user 193.2s
CPU sys 0.0s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Target: boolean_array

Comparing count-set-bits-aarch64 (110c2e8) to f6c3989 diff

Run configuration
run benchmark boolean_array
baseline:
  ref: f6c3989363
env:
  BENCH_FILTER: true_count
shards: 1
Details
group                                base                                   changed
-----                                ----                                   -------
true_count(all_false, 1024)          1.01      8.3±0.02ns        ? ?/sec    1.00      8.2±0.04ns        ? ?/sec
true_count(all_false, 448)           1.42     11.6±0.04ns        ? ?/sec    1.00      8.2±0.03ns        ? ?/sec
true_count(all_false, 64)            1.03      7.2±0.08ns        ? ?/sec    1.00      7.0±0.11ns        ? ?/sec
true_count(all_false, 65536)         1.00    121.4±0.04ns        ? ?/sec    1.00    121.5±0.05ns        ? ?/sec
true_count(all_false, 960)           1.41     14.8±0.08ns        ? ?/sec    1.00     10.5±0.01ns        ? ?/sec
true_count(all_true, 1024)           1.01      8.3±0.02ns        ? ?/sec    1.00      8.2±0.04ns        ? ?/sec
true_count(all_true, 448)            1.45     12.0±0.04ns        ? ?/sec    1.00      8.2±0.01ns        ? ?/sec
true_count(all_true, 64)             1.02      7.2±0.07ns        ? ?/sec    1.00      7.1±0.11ns        ? ?/sec
true_count(all_true, 65536)          1.00    121.6±0.05ns        ? ?/sec    1.00    121.6±0.04ns        ? ?/sec
true_count(all_true, 960)            1.49     15.7±0.15ns        ? ?/sec    1.00     10.5±0.03ns        ? ?/sec
true_count(mixed_early, 1024)        1.04      8.6±0.03ns        ? ?/sec    1.00      8.3±0.01ns        ? ?/sec
true_count(mixed_early, 448)         1.42     11.6±0.02ns        ? ?/sec    1.00      8.2±0.03ns        ? ?/sec
true_count(mixed_early, 64)          1.03      7.2±0.14ns        ? ?/sec    1.00      7.0±0.11ns        ? ?/sec
true_count(mixed_early, 65536)       1.00    121.6±0.06ns        ? ?/sec    1.00    121.6±0.03ns        ? ?/sec
true_count(mixed_early, 960)         1.41     14.9±0.09ns        ? ?/sec    1.00     10.5±0.01ns        ? ?/sec
true_count(nulls_all_true, 1024)     1.00     21.2±0.01ns        ? ?/sec    1.00     21.1±0.00ns        ? ?/sec
true_count(nulls_all_true, 448)      1.04     12.2±0.20ns        ? ?/sec    1.00     11.7±0.00ns        ? ?/sec
true_count(nulls_all_true, 64)       1.01      4.9±0.02ns        ? ?/sec    1.00      4.9±0.01ns        ? ?/sec
true_count(nulls_all_true, 65536)    1.00   1037.3±0.43ns        ? ?/sec    1.00   1037.2±0.35ns        ? ?/sec
true_count(nulls_all_true, 960)      1.06     21.5±0.05ns        ? ?/sec    1.00     20.3±0.53ns        ? ?/sec

Per-runner information
boolean_array — shard 1/1

Node: gk3-benchmark-cluster-nap-1fboxlbk-f8e512a2-rbc7

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

uname:

Linux bench-c6511084958-4128-sdb6h 6.12.105+ #1 SMP Sat Sep 19 07:55:25 UTC 2026 aarch64 GNU/Linux

BENCH_COMMAND:

cargo bench --features=arrow,async,test_common,experimental,object_store --bench boolean_array
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
Resource Usage

base (merge-base)

Metric Value
Wall time 190.0s
Peak memory 10.6 MiB
Avg memory 10.0 MiB
CPU user 188.0s
CPU sys 0.0s
Peak spill 0 B

branch

Metric Value
Wall time 200.0s
Peak memory 10.6 MiB
Avg memory 9.8 MiB
CPU user 194.2s
CPU sys 0.0s
Peak spill 0 B

File an issue against this benchmark runner

@mbutrovich

Copy link
Copy Markdown
Contributor Author

@andygrove the bot ran the tail change on Axion against the bench commit. The 448-bit and 960-bit cases are 1.39x to 1.49x faster, and the 64, 1024, and 65536-bit cases moved by less than 1.04x (default, aligned).

Looks like a good improvement!

@devanbenz devanbenz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just a thought experiment on exciting ARM intrinsics. I'm not really a huge ARM user so it's mostly research with an LLM helper. The resulting code I worked on didn't really improve much just the amount of instructions generated. The actual throughput seems to be the same. Overall this looks good to me!

/// widening per 64 bytes. A `u64::count_ones` sum widens every 16 bytes. On x86
/// the `u64` sum measured faster (#11776), so this is only used on aarch64.
#[cfg(target_arch = "aarch64")]
fn count_ones_words(words: &[u64]) -> usize {

@devanbenz devanbenz Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I was able to inspect this code on my M2 Macbook and didn't notice any issues. Taking a peek at the llvm IR gen:

vector.body:                                      ; preds = %bb15.preheader, %vector.body
  %lane_counts.sroa.0.1 = phi <16 x i16> [ zeroinitializer, %bb15.preheader ], [ %21, %vector.body ]
  %iter1.sroa.0.027 = phi ptr [ %iter.sroa.0.029, %bb15.preheader ], [ %_75, %vector.body ]
  %wide.load = load <16 x i8>, ptr %iter1.sroa.0.027, align 1
  %10 = tail call range(i8 0, 9) <16 x i8> @llvm.ctpop.v16i8(<16 x i8> %wide.load)
  %11 = getelementptr inbounds nuw i8, ptr %iter1.sroa.0.027, i64 16
  %wide.load48 = load <16 x i8>, ptr %11, align 1
  %12 = tail call range(i8 0, 9) <16 x i8> @llvm.ctpop.v16i8(<16 x i8> %wide.load48)
  %13 = add nuw nsw <16 x i8> %12, %10
  %14 = getelementptr inbounds nuw i8, ptr %iter1.sroa.0.027, i64 32
  %wide.load49 = load <16 x i8>, ptr %14, align 1
  %15 = tail call range(i8 0, 9) <16 x i8> @llvm.ctpop.v16i8(<16 x i8> %wide.load49)
  %16 = add nuw nsw <16 x i8> %13, %15
  %17 = getelementptr inbounds nuw i8, ptr %iter1.sroa.0.027, i64 48
  %wide.load50 = load <16 x i8>, ptr %17, align 1
  %18 = tail call range(i8 0, 9) <16 x i8> @llvm.ctpop.v16i8(<16 x i8> %wide.load50)
  %19 = add nuw nsw <16 x i8> %16, %18
  %20 = zext nneg <16 x i8> %19 to <16 x i16>
  %21 = add <16 x i16> %lane_counts.sroa.0.1, %20
  %_75 = getelementptr inbounds nuw i8, ptr %iter1.sroa.0.027, i64 64
  %_69 = icmp eq ptr %_75, %_53
  br i1 %_69, label %bb8.i12.preheader, label %vector.body
}

Looks like there is consistent vector lane usage as expected %wide.load48 = load <16 x i8>, ptr %11, align 1. Doubt we can squeeze a ton more performance out of it.

Checking in on the main loop assembly

LBB85_3:
        ldp q2, q3, [x13]
        cnt.16b v2, v2
        cnt.16b v3, v3
        add.16b v2, v3, v2
        ldp q3, q4, [x13, #32]
        cnt.16b v3, v3
        cnt.16b v4, v4
        add.16b v3, v3, v4
        add.16b v2, v2, v3
        uaddw2.8h v1, v1, v2
        uaddw.8h v0, v0, v2
        add x13, x13, #64
        cmp x13, x12
        b.ne LBB85_3
        ushll.4s v2, v0, #0
        ushll.4s v3, v1, #0
        ushll2.4s v0, v0, #0
        ushll2.4s v1, v1, #0
        uaddl2.2d v4, v0, v1
        uaddl2.2d v5, v2, v3
        add.2d v4, v5, v4
        uaddl.2d v0, v0, v1
        uaddl.2d v1, v2, v3
        add.2d v0, v1, v0
        add.2d v0, v0, v4
        addp.2d d0, v0
        fmov x13, d0
        add x8, x13, x8
        cbnz x11, LBB85_2

This next portion you'll need to double check since I did this experiment with the help of LLMs.

There is an operation within ARM called UADALP, it could take the uaddw2 and uaddw to combine them. Also, this shrinks the epilogue. Because the old version ends with two accumulators, it needs the 13-instruction ushll / uaddl / add / addp tree to fold 16 lanes down to one number. The new version has 8 lanes in one register, so a single uaddlv.8h does it.

The code requires lots of intrinsics and is heavily AI generated:

fn count_ones_words(words: &[u64]) -> usize {
    use core::arch::aarch64::*;
    let (chunks, tail) = words.as_chunks::<8>();
    // Per chunk each u8 lane is ≤ 32; UADALP adds pairs, so each u16 lane gains ≤ 64.
    const CHUNKS_PER_FLUSH: usize = (u16::MAX / 64) as usize; // 1023
    let mut total = 0;
    for group in chunks.chunks(CHUNKS_PER_FLUSH) {
        let mut acc = unsafe { vdupq_n_u16(0) };
        for chunk in group {
            let p = chunk.as_ptr().cast::<u8>();
            // SAFETY: a chunk is 8 u64s = 64 readable bytes.
            let (a, b, c, d) = unsafe {
                (
                    vld1q_u8(p),
                    vld1q_u8(p.add(16)),
                    vld1q_u8(p.add(32)),
                    vld1q_u8(p.add(48)),
                )
            };
            let s = unsafe {
                vaddq_u8(
                    vaddq_u8(vcntq_u8(a), vcntq_u8(b)),
                    vaddq_u8(vcntq_u8(c), vcntq_u8(d)),
                )
            };
            acc = unsafe { vpadalq_u8(acc, s) }; // UADALP
        }
        total += unsafe { vaddlvq_u16(acc) as usize };
    }
    total + tail.iter().map(|w| w.count_ones() as usize).sum::<usize>()
}

With the resulting ASM:

LBB85_3:
        ldp q1, q2, [x13]
        ldp q3, q4, [x13, #32]
        cnt.16b v1, v1
        cnt.16b v2, v2
        cnt.16b v3, v3
        cnt.16b v4, v4
        add.16b v1, v2, v1
        add.16b v2, v3, v4
        add.16b v1, v1, v2
        uadalp.8h v0, v1
        add x13, x13, #64
        cmp x13, x12
        b.ne LBB85_3
        uaddlv.8h s0, v0
        fmov w13, s0
        add x8, x8, x13
        cbnz x11, LBB85_2

That being said, this should decrease the operations by 1, but, the code is horrendous. Also, there was not really a noticable performance improvement in the boolean_array benchmarks, I only ran through a small amount but noticing the perf didn't really change I exited early lol:

Benchmarks snippet this PR
true_count(all_true, 64)
                        time:   [9.8319 ns 9.8490 ns 9.8649 ns]
Found 3 outliers among 100 measurements (3.00%)
  3 (3.00%) high mild

has_true(all_true, 64)  time:   [8.0431 ns 8.0521 ns 8.0619 ns]
Found 6 outliers among 100 measurements (6.00%)
  5 (5.00%) high mild
  1 (1.00%) high severe

has_false(all_true, 64) time:   [9.3496 ns 9.3617 ns 9.3747 ns]
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) high mild

true_count(all_false, 64)
                        time:   [9.8090 ns 9.8213 ns 9.8349 ns]
Found 5 outliers among 100 measurements (5.00%)
  5 (5.00%) high mild

has_true(all_false, 64) time:   [8.9055 ns 8.9186 ns 8.9326 ns]
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high mild

has_false(all_false, 64)
                        time:   [8.8893 ns 8.9028 ns 8.9185 ns]
Found 3 outliers among 100 measurements (3.00%)
  1 (1.00%) high mild
  2 (2.00%) high severe

true_count(mixed_early, 64)
                        time:   [9.8369 ns 9.8573 ns 9.8821 ns]
Found 6 outliers among 100 measurements (6.00%)
  4 (4.00%) high mild
  2 (2.00%) high severe

has_false(mixed_early, 64)
                        time:   [9.0135 ns 9.0300 ns 9.0483 ns]
Found 5 outliers among 100 measurements (5.00%)
  4 (4.00%) high mild
  1 (1.00%) high severe

true_count(nulls_all_true, 64)
                        time:   [9.2620 ns 9.2852 ns 9.3093 ns]
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high mild

has_true(nulls_all_true, 64)
                        time:   [7.9764 ns 8.0999 ns 8.2228 ns]
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) high mild

has_false(nulls_all_true, 64)
                        time:   [10.102 ns 10.130 ns 10.159 ns]
Found 5 outliers among 100 measurements (5.00%)
  4 (4.00%) high mild
  1 (1.00%) high severe

take_n_true_unique(32)  time:   [87.153 ns 88.768 ns 90.064 ns]
Benchmark snippet with UADALP
true_count(all_true, 64)
                        time:   [9.8282 ns 9.8448 ns 9.8605 ns]
                        change: [−0.3596% −0.1550% +0.0563%] (p = 0.13 > 0.05)
                        No change in performance detected.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high mild

has_true(all_true, 64)  time:   [8.1367 ns 8.1619 ns 8.1894 ns]
                        change: [+1.2133% +1.4913% +1.7940%] (p = 0.00 < 0.05)
                        Performance has regressed.

has_false(all_true, 64) time:   [9.4249 ns 9.4461 ns 9.4662 ns]
                        change: [+0.5042% +0.6909% +0.8768%] (p = 0.00 < 0.05)
                        Change within noise threshold.

true_count(all_false, 64)
                        time:   [9.8631 ns 9.8795 ns 9.8952 ns]
                        change: [−0.0373% +0.2251% +0.4831%] (p = 0.10 > 0.05)
                        No change in performance detected.
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) high mild

has_true(all_false, 64) time:   [8.9118 ns 8.9331 ns 8.9598 ns]
                        change: [−0.2245% +0.0002% +0.2247%] (p = 1.00 > 0.05)
                        No change in performance detected.
Found 3 outliers among 100 measurements (3.00%)
  3 (3.00%) high mild

has_false(all_false, 64)
                        time:   [8.9489 ns 8.9603 ns 8.9718 ns]
                        change: [−1.1010% −0.1834% +0.3938%] (p = 0.75 > 0.05)
                        No change in performance detected.
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) high mild

true_count(mixed_early, 64)
                        time:   [9.9050 ns 9.9222 ns 9.9405 ns]
                        change: [+0.2972% +0.6001% +0.8762%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high mild

has_false(mixed_early, 64)
                        time:   [8.9812 ns 9.0043 ns 9.0301 ns]
                        change: [−0.0181% +0.3545% +0.7718%] (p = 0.07 > 0.05)
                        No change in performance detected.
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) high mild
  2 (2.00%) high severe

true_count(nulls_all_true, 64)
                        time:   [9.2890 ns 9.3118 ns 9.3366 ns]
                        change: [+0.1582% +0.4465% +0.7484%] (p = 0.00 < 0.05)
                        Change within noise threshold.

has_true(nulls_all_true, 64)
                        time:   [7.8813 ns 7.9714 ns 8.0606 ns]
                        change: [−3.3464% −1.5696% +0.1403%] (p = 0.09 > 0.05)
                        No change in performance detected.

has_false(nulls_all_true, 64)
                        time:   [10.040 ns 10.052 ns 10.065 ns]
                        change: [−0.9417% −0.6809% −0.4366%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 6 outliers among 100 measurements (6.00%)
  2 (2.00%) high mild
  4 (4.00%) high severe

take_n_true_unique(32)  time:   [87.706 ns 89.046 ns 90.072 ns]
                        change: [−1.9092% +1.5018% +4.6731%] (p = 0.38 > 0.05)
                        No change in performance detected.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arrow-core Core crates related to representing arrow data

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Faster count_set_bits on aarch64 by summing byte popcounts before widening

4 participants