Change default x86 build target from x86-64-v2 (SSE4.2) to x86-64-v3 (AVX2)#90043
Conversation
|
It shows both improvements and regressions in some cases. I'll keep it open to review it in depth and see if we can introduce dynamic dispatching where it benefits |
|
Merged with master after a few of the improvements have been introduced via dynamic dispatch. I'll keep investigating other speed ups |
|
Next to investigate is memcpy: We use our own, which is optimized for SSE4.2 and does 128 bit copies. AVX2 does 256 bit copies, and AVX512 would allow us to copy 512 bit per instruction. There was quite a bit of work done to introduce our own (#21520). Some ideas:
Something that did not work: Prefetching |
|
I tried different alternatives for memcpy but keeping our currently implementation seems the best option so far |
In `range.cpp` `executeConstStartStep` and `iota.cpp` `iota`/`iotaWithStep`, replace `start + idx * step` with an accumulator `value += step`. This avoids per-element multiplies (e.g. `pmaddubsw` for UInt8) that cause the compiler to choose a high auto-vectorization threshold (128 bytes for AVX2 vs 32 for SSE), making the vectorized path unreachable for short arrays. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Idk, if this could be useful for you or not. Afaik, the most conservative way to enable AVX2 would be to set |
|
We currently use the default tuning for x86-64-v3 in clang which contains a bunch of tunings, including |
|
The cleanest approach is to assume a processor without throttling, therefore we won't need crutches ( |
On x86-64-v3 (AVX2), the compiler semi-vectorizes the `UInt64`->`Float32`
conversion loop using `vpand`/`vpsrlq`/`vpor`/`vblendvpd` for unsigned
fixup, scalar `vcvtsi2ss` with extract/insert, and `vblendvps` for
conditional selection. This ~20-instruction sequence per 4 elements is
slower than simple scalar code (~5 instructions per element with
perfectly-predicted branches), because there is no AVX2 vector instruction
for int64->float conversion (only AVX-512 has `vcvtqq2ps`).
Move the `UInt64`->`Float32` specialization out of the `__aarch64__` guard
and add `_Pragma("clang loop vectorize(disable)")` on x86 to generate
clean scalar code with VEX encoding and 8x unrolling.
Benchmark: `sum(toFloat32(number))` goes from ~9% regression to ~20%
improvement on v3 vs v2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Most of the regressions should be covered now. The remaining red tests that I see are all within or at threshold. For example, |
The default x86 build now requires x86-64-v3 (AVX2). Update the install script to check for `avx2` instead of `sse4_2` in `/proc/cpuinfo`, falling back to the `amd64compat` build on older hardware. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Performance test noise analysisThe performance comparison reports show elevated thresholds and a high number of borderline regressions. This is expected and not indicative of real regressions. Root cause of the noise: this PR compares a v2 (SSE4.2) baseline against a v3 (AVX2) build. Unlike normal PRs where both old and new binaries share the same ISA target and produce nearly identical machine code, here the two binaries have fundamentally different code generation — different instructions, different vectorization decisions, different instruction scheduling. The perf test alternates between old and new binary runs, and the different ISA targets leave different residual state in CPU caches, branch predictors, etc. This causes variable performance deltas between runs, leading to high variance and high thresholds. Statistical summary (AMD shards, sha 8ba3244):
Local verification: all CI-reported regressions were benchmarked locally (master binary from CI vs local v3 build). None showed real regressions:
Note: the This noise will disappear once v3 becomes the baseline — future PRs will compare v3 vs v3, restoring normal threshold levels. |
Under MSan, `SelectStreamFactory::createForShardWithAnalyzer` calls `InterpreterSelectQueryAnalyzer::getSampleBlockAndPlannerContext` once per shard. Each invocation runs a full `only_analyze` `Planner` pass which costs ~140 ms × 20 shards = ~3 s on the initiator before the first remote sub-query is even sent. Combined with the staggered sub-query execution (~5.5 s for 20 × 1.4 s shards) the total wall-time consistently lands in the 9.5–10.2 s range, tipping over the 10 s `timeout` in the test. The same code path runs on every build, but only MSan inflates each analyze pass enough to push the test past its budget. Tag the test as `no-msan` and update the stale comment that still refers to a 5 s budget (the test was switched to 10 s years ago). CI report: https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=90043&sha=a61ed3dbbc4da693beb4601322c3f8f6d8b2f8b0&name_0=PR&name_1=Stateless+tests+%28amd_msan%2C+WasmEdge%2C+parallel%2C+2%2F2%29 PR: ClickHouse#90043
The previous `${OBJCOPY/%objcopy/ar}` anchored the substitution to the
end of the path, so a versioned binary like `/usr/bin/llvm-objcopy-21`
(used by CI) did not match and `AR` was left as `llvm-objcopy-21`. The
script then ran `llvm-objcopy-21 x lib.a`, and `llvm-objcopy` treated
`x` as an input file:
/usr/bin/llvm-objcopy-21: error: 'x': No such file or directory
Substitute `objcopy` only inside the basename and re-attach the
directory, so both `/usr/bin/llvm-objcopy-21` and
`/opt/objcopy-tools/bin/llvm-objcopy` resolve correctly.
CI: https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=90043&sha=latest&name_0=PR
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Commit 7bb9509 ("Remove unnecessary dispatch") removed the `x86_64_v3` specialization on the assumption that at a v3 build the `Default` namespace is already v3, making the per-function attribute redundant. That assumption breaks for files compiled with explicit `-march=x86-64-v2` overrides. `FunctionsHashingMisc.cpp` is one such file (overridden to avoid an unrelated SLP regression in `xxh3 ClickHouse#48` / `farmHash64 ClickHouse#5`). Without a v3-attribute specialization there, the runtime dispatcher falls back to the v2-compiled `Default` body, which uses SSE2/XMM codegen instead of AVX2/YMM. That regressed `hiveHash` / `javaHash` on UUID and Decimal128 columns by 12-18% (perf tests `general_purpose_hashes_on_UUID ClickHouse#30` and `ClickHouse#32`, `ClickHouse#58` on Decimal). Restore the `DECLARE_X86_64_V3_SPECIFIC_CODE` macro, the `x86_64_v3` slot in `DECLARE_MULTITARGET_CODE`, and the per-function `x86_64_v3` registration in both `FunctionIntHash` and `FunctionAnyHash`. This goes hand-in-hand with the v2 override on `FunctionsHashingMisc.cpp`: that override stays as the SLP fix, and this restoration makes the dispatcher recover the AVX2 fast path on hosts that support it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ssion At v3 the `UniquesHashSet::merge` probe loop lowers `good(hash) = (hash >> s) << s == hash` to BMI2 `bzhi`/`shlxq`/`shrxq`, which is slightly slower than the legacy `shl %cl + not + test` sequence on Sapphire Rapids and regresses `uniq_stored #1` by ~9%. Building this single TU with `-mno-bmi -mno-bmi2` restores the legacy shift codegen while keeping the rest of v3 (AVX2/FMA/etc.) untouched. Bench: v2 0.214s, v3-LTO before 0.233s (+9%), v3-LTO after 0.218s. The override must live in `src/AggregateFunctions/CMakeLists.txt` because that is where the `clickhouse_aggregate_functions` target is created. `set_source_files_properties` set in the parent `src/CMakeLists.txt` scope does not propagate to a target defined in a subdirectory (unlike the `StorageGenerateRandom` precedent, where the file is absorbed into the `dbms` target in the same scope via `add_object_library`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@rschu1ze @nickitat The PR is finally ready for review Some notes:
Current performance report: https://pastila.nl/?00da47bb/5185028c8a271967d39877b08c189567#IQymHLWEeKoq+EhoEfv/iQ==GCM Summary for 11e6269:
We can keep iterating more, but at this point I think it's safe to review and merge. |
LLVM Coverage Report
Changed lines: 95.28% (726/762) | lost baseline coverage: 25 line(s) · Uncovered code |
rschu1ze
left a comment
There was a problem hiding this comment.
@Algunenano I actually reviewed an earlier state of the PR (also approved) and then the incremental commits you pushed since. Looks all good to me. Thanks for this PR!
e21a84e
…6-64-v3 (AVX2)" This reverts PR ClickHouse#90043 (merge commit e21a84e) due to performance regressions observed in the MSan build. Note: a post-merge follow-up commit (9df07df, "Pass nm/ar/objcopy paths from CMake to localize_rust_c_symbols.sh") added an `NM_PATH` lookup in `cmake/tools.cmake` for the now-removed `cmake/localize_rust_c_symbols.sh` helper. The lookup is left in place by this revert; it is unused but harmless and can be cleaned up separately.

Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
This change was reverted
Documentation entry for user-facing changes
The default ClickHouse binary for x86 Linux now requires AVX2 (x86-64-v3). CPUs without AVX2 support will fail with an "Illegal instruction" error. For systems without AVX2, use the compatibility build (
amd64compat) available athttps://builds.clickhouse.com/master/amd64compat/clickhouse. The install script (curl https://clickhouse.com/ | sh) automatically detects CPU capabilities and downloads the appropriate build.Closes #99172
Version info
26.5.1.594