#!/bin/bash # Which decompression branch substr(v, 500000, 100) takes on 1 MB zstd values # in a UTF8 and a SQL_ASCII database, and how many page faults the backend # takes per value with the reused context and with a fresh one. No timing. # branch_faults.sh REUSE_BINDIR FRESH_BINDIR WORKDIR # REUSE_BINDIR bin/ of a build with the 0003 decode code (reused DCtx) # FRESH_BINDIR bin/ of the same tree with the reuse undone # (nocfbot-v9n-without-reuse.diff.txt: the v7 decode code) # WORKDIR empty scratch directory; the cluster is ~30 MB # One small cluster (shared_buffers 32MB), 20 values of 1 MB per database, # the "random" corpus of bench.sh. Queries run in single-user mode, under # gdb (breakpoints at the entry of zstd_decompress_datum, the full branch, # and zstd_decompress_datum_slice, printing slicelength) and under # perf stat -e page-faults (one query vs six; faults per value = # (six - one) / 5 / 20, which cancels startup). Both builds must share the # catalog version, as they do when only toast_compression.c differs. set -eu R=$1 F=$2 W=$3 D=$W/cl; P=$((56400 + RANDOM % 100)) "$R/initdb" -D $D -A trust --no-sync -U postgres -E SQL_ASCII --locale=C >/dev/null cat >> $D/postgresql.conf </dev/null q() { "$R/psql" -X -qAt -h $W -p $P -U postgres -v ON_ERROR_STOP=1 "$@"; } q -d postgres -c "CREATE DATABASE u8 ENCODING 'UTF8' TEMPLATE template0 LC_COLLATE 'C' LC_CTYPE 'C'" q -d postgres -c "CREATE DATABASE sqlascii ENCODING 'SQL_ASCII' TEMPLATE template0 LC_COLLATE 'C' LC_CTYPE 'C'" for db in u8 sqlascii; do q -d $db -c "SELECT setseed(0.42)" -c "CREATE TABLE t (id int, v text)" \ -c "INSERT INTO t SELECT g, left(string_agg(md5(random()::text) || encode(sha256(random()::text::bytea), 'base64'), ''), 1000000) FROM generate_series(1, 20) g, generate_series(1, 1000000 / 76 + 1) i GROUP BY g" q -d $db -c "SELECT format('db=%s encoding=%s eml=%s values=%s bytes=%s method=%s slice_md5=%s', current_database(), current_setting('server_encoding'), pg_encoding_max_length(pg_char_to_encoding(current_setting('server_encoding'))), count(*), min(octet_length(v)), string_agg(DISTINCT pg_column_compression(v), ','), md5(string_agg(substr(v, 500000, 100), '' ORDER BY id))) FROM t" done "$R/pg_ctl" -D $D -m fast -w stop >/dev/null cat > $W/br.gdb <<'GDB' set pagination off set confirm off break *zstd_decompress_datum commands silent continue end break *zstd_decompress_datum_slice commands silent printf "slicelength=%d\n", $rsi continue end run info breakpoints GDB single() { "$1/postgres" --single -D $D -c shared_buffers=32MB $2; } for db in u8 sqlascii; do for e in "substr(v, 500000, 100)" "substr(v, 1, 100)" "v || 'x'"; do out=$(echo "SELECT sum(length($e)) FROM t" | gdb -q -batch -x $W/br.gdb --args "$R/postgres" --single -D $D -c shared_buffers=32MB $db 2>&1) full=$(echo "$out" | sed -n '/^Num/,$p' | awk '//{s=1;next} //{s=2;next} /already hit/{h[s]=$4} END{print (h[1]?h[1]:0)}') sl=$(echo "$out" | sed -n '/^Num/,$p' | awk '//{s=1;next} s&&/already hit/{print $4; exit}') lens=$(echo "$out" | grep -oE 'slicelength=[0-9]+' | sort -u | tr '\n' ' ') echo "branch db=$db expr=\"$e\" full_entries=$full slice_entries=${sl:-0} ${lens}" done done pf() { # bindir db expr n for i in $(seq $4); do echo "SELECT sum(length($3)) FROM t;"; done | perf stat -x, -e page-faults "$1/postgres" --single -D $D -c shared_buffers=32MB $2 2>&1 >/dev/null | awk -F, '/page-faults/{print $1}' } for c in "sqlascii|substr(v, 500000, 100)" "sqlascii|substr(v, 1, 100)" "sqlascii|v || 'x'" "u8|substr(v, 500000, 100)"; do db=${c%%|*} e=${c#*|} for rep in 1 2 3; do for b in reuse fresh; do [ $b = reuse ] && bin=$R || bin=$F one=$(pf $bin $db "$e" 1); six=$(pf $bin $db "$e" 6) echo "faults db=$db expr=\"$e\" build=$b rep=$rep one=$one six=$six per_value=$(( (six - one) / 100 ))" done done done rm -rf $D $W/br.gdb $W/log