#!/bin/bash # verify-like-hot-runs.sh: check whether the hot runs of ClickBench Q20 (`URL LIKE '%google%'`) on the pgrust # v0.4-preview binary that ClickHouse/ClickBench#2165 removed replay a stored answer, or pass over every row again. # # Run on a fresh Ubuntu 24.04 arm64 machine (c8g.4xlarge, 500 GB gp2), as a sudo-capable user: # bash verify-like-hot-runs.sh # Takes ~10 min, most of it downloading hits.parquet and loading 100M rows. The table lands in ~/verify-like/results.md. # # The test: Q20 three times and '%rambler%' twice, once with the server on all CPUs and once on one CPU # (`taskset -c 0 ./start`; the server's worker pool follows its CPU affinity, so it runs one worker). # - A stored answer, or a stored row selection (12.5 MB: one bit per row), is read in about a millisecond on one # core, so the hot run stays about as fast on one CPU. # - The dictionary cache keeps, per part, one bit per distinct URL saying whether it matches the pattern. A hot run # still checks all 100M rows' URL codes against it, so on one CPU it takes about (all-CPU hot time) x (CPUs). # - '%rambler%' costs more on its first run than its second: the first fills the dictionary cache for that pattern. # Times are psql \timing through the entry's own ./query, as the benchmark measures them. set -euo pipefail BIN_URL=https://pgrust.com/downloads/archive/2026-09-clickbench-snapshot/pgrust-v0.4-preview-aarch64-neoverse-v2.tar.gz BIN_SHA=a8b08f029255a2f27364c51dae119fba6aff4beb20fa58125345527e78d3b2e3 CLICKBENCH_COMMIT=964e362a891f2ac19d0a896a642758555341ab74 # ClickBench main just before the pgrust entry was removed W=$HOME/verify-like; mkdir -p "$W"; cd "$W" echo "== 1. tools + the entry's own ClickBench scripts" sudo apt-get update -qq && sudo apt-get install -y -qq git python3 curl util-linux >/dev/null [ -d ClickBench ] || git clone -q https://github.com/ClickHouse/ClickBench.git git -C ClickBench checkout -q "$CLICKBENCH_COMMIT" cd ClickBench/pgrust echo "== 2. the submitted binary (pinned by sha256)" curl -fsSL -o "$W/preview.tar.gz" "$BIN_URL" echo "$BIN_SHA $W/preview.tar.gz" | sha256sum -c - export PGRUST_TARBALL="$W/preview.tar.gz" sudo chmod a+r "$W/preview.tar.gz"; chmod a+x "$HOME" "$W" echo "== 3. install, start, load (the entry's scripts, unmodified; skipped on a rerun)" up() { for _ in $(seq 1 300); do ./check >/dev/null 2>&1 && return 0; sleep 1; done; echo "server did not come up" >&2; exit 1; } if [ ! -f "$W/loaded" ]; then ./install && ./start && up && ./load && touch "$W/loaded"; fi . ./env.sh sha256sum "$PGRUST_PREFIX/bin/postgres" GOOGLE="SELECT COUNT(*) FROM hits WHERE URL LIKE '%google%';" RAMBLER="SELECT COUNT(*) FROM hits WHERE URL LIKE '%rambler%';" run() { { ./query <<<"$1" 2>&1 >/dev/null || true; } | tail -1; } # one run: wall seconds (or the error) pid() { sudo head -1 "$PGDATA/postmaster.pid" 2>/dev/null || true; } arm() { # arm LABEL [CMD...]: restart the server (started through CMD, e.g. taskset), Q20 x3, '%rambler%' x2 local label=$1 g1 g2 g3 r1 r2 workers cpus; shift # ./stop reads $PGDATA/postmaster.pid, which only root and the server's user can see (ClickBench runs it as root) sudo ./stop; for _ in $(seq 1 120); do ./check >/dev/null 2>&1 || break; sleep 1; done ./check >/dev/null 2>&1 && { echo "server did not stop" >&2; exit 1; } "$@" ./start; up g1=$(run "$GOOGLE"); g2=$(run "$GOOGLE"); g3=$(run "$GOOGLE"); r1=$(run "$RAMBLER"); r2=$(run "$RAMBLER") workers=$(ps -L -u "$PGRUST_USER" -o comm= | grep -c '^pg:rtworker' || true) cpus=$(taskset -pc "$(pid)" | sed 's/.*: //') echo "| $label | $(pid) | $cpus | $workers | $g1 | $g2, $g3 | $r1 | $r2 |" >> "$W/results.md" } echo "== 4. Q20 on all CPUs, then on one CPU" { echo "# Q20 hot runs on all CPUs vs one CPU: pgrust v0.4-preview, $(lscpu | sed -n 's/^Model name: *//p' | head -1), $(nproc) CPUs" echo echo "| server | pid | allowed CPUs | worker threads | Q20 1st run s | Q20 hot s (2nd, 3rd) | '%rambler%' 1st s | '%rambler%' 2nd s |" echo "|---|---|---|---|---|---|---|---|" } > "$W/results.md" arm "all CPUs" arm "one CPU" taskset -c 0 sudo ./stop cat >> "$W/results.md" <<'EOF' A stored answer or row selection: Q20's hot run is about as fast on one CPU as on all of them. The dictionary cache: Q20's hot run on one CPU takes about (all-CPU hot time) x (CPUs), because every run checks all 100M rows; '%rambler%' is slower on its 1st run than its 2nd, which reuses the dictionary cache the 1st filled. EOF cat "$W/results.md"