Files
datastore/tests/performance/wide_integer_conversion.xml
ManuelandClaude Fable 5 6b80338f3b Branchless sign extension in wide::integer conversions
wide_integer_from_builtin and wide_integer_from_wide_integer sign-extend
through an `if (rhs < 0)` branch that selects between two separate fill
loops. In columnar conversion loops (ConvertImpl) this data-dependent
branch mispredicts on mixed-sign data and prevents vectorization even
when it predicts well.

Select the extension value (0 or -1) branchlessly instead and use a
single fill loop.

Standalone kernel benchmark of the columnar conversion loop (clang 21,
-march=x86-64-v3, 64k-element blocks, GB/s of processed data):

    conversion               before   after
    Int64 -> Int256, random sign      10.3    73.9
    Int64 -> Int256, positive only    31.3    73.3
    Int128 -> Int256, random sign     12.2    68.0
    Int64 -> Int128                   61.5    62.2
    UInt64 -> UInt128/UInt256         unchanged

The same effect is visible at the query level: with the official
26.6.1 binary, `toInt256(toInt64(cityHash64(number)))` over 100M rows
takes 6.8x the time of the equivalent unsigned conversion in a single
thread, and 3.5x the time of the same conversion with positive-only
values.

Result equivalence with the previous implementation was verified over
36 million checks: all builtin integral types (int8/16/32/64, unsigned
variants) converted to UInt128/Int128/UInt256/Int256 including type
boundary values, 128-to-256-bit widening for all signedness
combinations, narrowing, and constexpr evaluation.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-06 21:00:08 +02:00

16 lines
985 B
XML

<test>
<!-- Conversion of native integers to 256-bit integers and widening of 128-bit to 256-bit integers.
Random 64-bit values give a random sign for the signed cases, which is hostile to a branchy
sign extension. The unsigned and 128-bit target queries guard the already fast paths. -->
<settings>
<max_threads>1</max_threads>
</settings>
<query>SELECT count() FROM numbers(50000000) WHERE NOT ignore(toInt256(toInt64(cityHash64(number))))</query>
<query>SELECT count() FROM numbers(50000000) WHERE NOT ignore(toInt256(toInt64(bitAnd(cityHash64(number), 4611686018427387903))))</query>
<query>SELECT count() FROM numbers(50000000) WHERE NOT ignore(toUInt256(cityHash64(number)))</query>
<query>SELECT count() FROM numbers(50000000) WHERE NOT ignore(toInt256(toInt128(toInt64(cityHash64(number)))))</query>
<query>SELECT count() FROM numbers(50000000) WHERE NOT ignore(toInt128(toInt64(cityHash64(number))))</query>
</test>