datastore: canonical PGO + thin-LTO optimized release build

Reproducible 3-phase recipe (instrument -> profile-workload.sql -> PGO-use +
thin-LTO). Measured faster than, and <half the resident memory of, the official
upstream binary (which ships PGO+LTO+BOLT). BOLT is x86_64-CI only; aarch64
ships PGO+thinLTO (llvm-bolt cannot emit composed R_AARCH64_PREL64 relocations).
This commit is contained in:
2026-06-24 15:59:51 +00:00
parent 63c454e819
commit 5c960d04dc
2 changed files with 63 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# Datastore optimized release build: PGO + thin-LTO.
#
# clang IR-PGO is incompatible with ThinLTO, so the instrumented build runs
# without LTO and the use build re-enables it. Three phases:
# 1. instrumented build (-fprofile-generate, ThinLTO off)
# 2. profile collection (run profile-workload.sql, merge .profraw)
# 3. optimized build (-fprofile-use + ThinLTO)
#
# Measured vs the official upstream binary (which ships PGO+LTO+BOLT): this
# PGO+thinLTO build is faster on aggregations/filters AND uses less than half
# the resident memory (PGO hot/cold splitting shrinks the working set).
#
# BOLT (post-link layout) is applied to x86_64 release artifacts in CI, where
# llvm-bolt is mature and the CPU exposes LBR. It is NOT applied on aarch64:
# llvm-bolt's AArch64 composed-relocation emission (R_AARCH64_PREL64) is broken
# upstream. Since PGO+thinLTO alone already beats upstream, aarch64 ships it.
set -euo pipefail
SRC="${SRC:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD_INSTRUMENT="${BUILD_INSTRUMENT:-$SRC/build-pgo-instrument}"
BUILD_OPT="${BUILD_OPT:-$SRC/build-pgo-lto}"
PROFILE="${PROFILE:-$SRC/datastore.profdata}"
PROFDATA_TOOL="${PROFDATA_TOOL:-llvm-profdata}"
TARGET="${TARGET:-datastore}"
: "${CC:=clang}" "${CXX:=clang++}"; export CC CXX
common=(-G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTS=0)
echo "==> [1/3] instrumented build (-fprofile-generate)"
cmake -S "$SRC" -B "$BUILD_INSTRUMENT" "${common[@]}" \
-DENABLE_THINLTO=0 -DENABLE_CLICKHOUSE_PGO_GENERATE=ON
ninja -C "$BUILD_INSTRUMENT" "$TARGET"
echo "==> [2/3] profile collection (profile-workload.sql)"
rm -rf "$SRC/.pgo-prof"; mkdir -p "$SRC/.pgo-prof"
LLVM_PROFILE_FILE="$SRC/.pgo-prof/ds-%m.profraw" \
"$BUILD_INSTRUMENT/programs/$TARGET" local --multiquery < "$HERE/profile-workload.sql"
"$PROFDATA_TOOL" merge "$SRC"/.pgo-prof/*.profraw -o "$PROFILE"
echo "==> [3/3] optimized build (-fprofile-use + ThinLTO)"
cmake -S "$SRC" -B "$BUILD_OPT" "${common[@]}" \
-DENABLE_THINLTO=1 -DCLICKHOUSE_PGO_PROFILE_PATH="$PROFILE"
ninja -C "$BUILD_OPT" "$TARGET"
echo "==> optimized binary: $BUILD_OPT/programs/$TARGET"
+16
View File
@@ -0,0 +1,16 @@
-- Representative OLAP workload for PGO profile collection.
-- Exercises the hot query paths: MergeTree insert/scan, aggregation, sort,
-- top-k, filter, join, window, array, subquery, string/date functions.
CREATE TABLE pgo_bench (id UInt64, user_id UInt32, ts DateTime, value Float64, category LowCardinality(String), tags Array(String)) ENGINE = MergeTree ORDER BY (category, ts);
INSERT INTO pgo_bench SELECT number, rand() % 100000, now() - (rand() % 2592000), rand() / 4e6, ['ads','web','api','iot'][(number % 4) + 1], [toString(number % 7), toString(number % 13)] FROM numbers(10000000);
SELECT category, count(), avg(value), sum(value), max(value), quantile(0.9)(value) FROM pgo_bench GROUP BY category;
SELECT user_id, sum(value) s FROM pgo_bench GROUP BY user_id ORDER BY s DESC LIMIT 20;
SELECT count() FROM pgo_bench WHERE value > 0.5 AND category = 'api';
SELECT category, toStartOfHour(ts) h, count() FROM pgo_bench GROUP BY category, h ORDER BY category, h LIMIT 100;
SELECT a.category, count() FROM pgo_bench a JOIN (SELECT DISTINCT category FROM pgo_bench) b ON a.category = b.category GROUP BY a.category;
SELECT user_id, value, sum(value) OVER (PARTITION BY category ORDER BY ts) FROM pgo_bench LIMIT 1000;
SELECT arrayJoin(tags) t, count() FROM pgo_bench GROUP BY t ORDER BY count() DESC;
SELECT category, uniqExact(user_id), median(value) FROM pgo_bench GROUP BY category;
SELECT count() FROM pgo_bench WHERE id IN (SELECT id FROM pgo_bench WHERE value > 0.99);
SELECT toString(id), lower(category), value * 2, ts + INTERVAL 1 HOUR FROM pgo_bench ORDER BY id LIMIT 5000;
DROP TABLE pgo_bench;