Files
ClickHouse/tests/performance/array_index_simd_numeric.xml
Raúl MarínandClaude Opus 5.5 5a758c9862 Vectorize the block scan of indexOf with a constant needle on ARM
`NumericArrayIndex<IndexOfAction, T>::findInBlocks` returned the rescan of the
matching block from inside the block loop. That kept clang from vectorizing the
`found |=` reduction for 4- and 8-byte elements on ARM, so `indexOf` ran a
scalar prefix, a scalar block reduction and a scalar rescan, which is slower
than the scalar loop it replaced. `has` was not affected.

Now the loop only breaks, and the block or the tail is rescanned after it.
`NumericArrayIndex<IndexOfAction, T>::vector` for `Int32`, `UInt32`, `Int64`,
`UInt64`, `Float32` and `Float64` now has the same NEON compares as `has`. The
`has` code is unchanged on both x86 and ARM. On x86 the `indexOf` block loop
stays the same, and the unrolled rescan becomes a short loop.

`clickhouse local` on Neoverse-V2, `max_threads = 1`, best of 9, 4M rows:
- `Array(UInt32)` of 24, hit in 8..15: 72 -> 52 ms
- `Array(UInt32)` of 24, hit at 9: 48 -> 26 ms
- `Array(Int64)` of 16, hit in 8..15: 58 -> 53 ms

Add a short-array case to the `array_index_simd_numeric` performance test.

Closes: https://github.com/ClickHouse/ClickHouse/issues/123338

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 00:46:03 +00:00

45 lines
1.6 KiB
XML

<test>
<settings>
<max_threads>1</max_threads>
<max_block_size>8192</max_block_size>
</settings>
<create_query>DROP TABLE IF EXISTS perf_array_index_simd_numeric</create_query>
<create_query>
CREATE TABLE perf_array_index_simd_numeric (arr Array(UInt32)) ENGINE = Memory
</create_query>
<fill_query>
INSERT INTO perf_array_index_simd_numeric
SELECT materialize(range(4096)::Array(UInt32))
FROM numbers(20000)
</fill_query>
<!-- Short arrays with the first match just past the scalar prefix, in the first block. -->
<create_query>DROP TABLE IF EXISTS perf_array_index_simd_numeric_short</create_query>
<create_query>
CREATE TABLE perf_array_index_simd_numeric_short (arr Array(UInt32)) ENGINE = Memory
</create_query>
<fill_query>
INSERT INTO perf_array_index_simd_numeric_short
SELECT arrayMap(x -> if(x = 8 + cityHash64(number) % 8, 0, x + 1), range(24))::Array(UInt32)
FROM numbers(4000000)
</fill_query>
<!-- Keep one representative input, but time each function independently. -->
<query>
SELECT sum(has(arr, 4294967295))
FROM perf_array_index_simd_numeric
FORMAT Null
</query>
<query>
SELECT sum(indexOf(arr, 100))
FROM perf_array_index_simd_numeric
FORMAT Null
</query>
<query>
SELECT sum(indexOf(arr, 0))
FROM perf_array_index_simd_numeric_short
FORMAT Null
</query>
<drop_query>DROP TABLE IF EXISTS perf_array_index_simd_numeric</drop_query>
<drop_query>DROP TABLE IF EXISTS perf_array_index_simd_numeric_short</drop_query>
</test>