From a741d6453262780602c068f5e4e1dd7792f8f896 Mon Sep 17 00:00:00 2001 From: Tin Dang Date: Sun, 24 May 2026 21:54:28 +0700 Subject: [PATCH] chore(scripts): add PR #100 bench helpers (Moon vs Redis, s=1 and multi-shard) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three small helper scripts used during PR #100 validation. Captured here so future contributors can rerun the same exact methodology without recreating it from scratch. ## Scripts - `scripts/bench-s1-only.sh` — tight L-A.G3 verification: s=1 only, GET+SET, p=1/16/64, median-of-3. Used to isolate whether an s=1 regression is real or a bench-environment artifact. Moon-only (no Redis comparison). - `scripts/bench-pr100.sh` — Moon vs Redis side-by-side at s=1. GET+SET, p=1/16/64, c=400, median-of-3. Random keys via `-r 100000` so SET writes spread across the keyspace. ~15 min total runtime. - `scripts/bench-pr100-multishard.sh` — same matrix but cycles Moon at s=1/2/4 against a single-process Redis baseline. ~25-30 min. Useful for documenting the well-known multi-shard scaling profile on random keyspace (see `project_multi_shard_limitations.md`). All three scripts: - assume `redis-server` + `redis-benchmark` on PATH (works in the OrbStack `moon-dev` VM out of the box) - pass `--appendonly no --disk-offload disable` to Moon (fair-bench defaults per `feedback_bench_disk_offload_fairness.md`) - emit a markdown summary table + a TSV with raw `r1/r2/r3/median` values for archiving No source changes. Reference results from these scripts are inlined in the PR #100 description. author: Tin Dang --- scripts/bench-pr100-multishard.sh | 78 +++++++++++++++++++++++++++++++ scripts/bench-pr100.sh | 67 ++++++++++++++++++++++++++ scripts/bench-s1-only.sh | 37 +++++++++++++++ 3 files changed, 182 insertions(+) create mode 100755 scripts/bench-pr100-multishard.sh create mode 100755 scripts/bench-pr100.sh create mode 100644 scripts/bench-s1-only.sh diff --git a/scripts/bench-pr100-multishard.sh b/scripts/bench-pr100-multishard.sh new file mode 100755 index 00000000..0bcde2db --- /dev/null +++ b/scripts/bench-pr100-multishard.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# PR #100 multi-shard perf: Moon at s=1/2/4 vs Redis on same VM (6 cores). +# GET + SET, p=1/16/64, c=400, median-of-3. Random keys (-r 100000) so SET +# write rate isn't artificially capped to 1 hot key. +set -euo pipefail + +REPO=/Volumes/Games/tindang-repo/moon +MOON_PORT=7540 +REDIS_PORT=7541 +RESULTS=/tmp/pr100-multi.tsv +SUMMARY=/tmp/pr100-multi.md +CLIENTS=400 + +cd "$REPO" +printf "engine\tshards\top\tpipeline\tr1\tr2\tr3\tmedian\n" > "$RESULTS" + +extract_rps () { tr '\r' '\n' | grep -oE "[0-9]+\.[0-9]+ requests" | tail -1 | awk '{print $1}'; } +median3 () { printf "%s\n" "$@" | sort -n | sed -n '2p'; } + +run_cell () { + local engine=$1 port=$2 shards=$3 op=$4 pipe=$5 + local n=$((300000 * pipe / 4)) + [[ $n -lt 100000 ]] && n=100000 + [[ $n -gt 1500000 ]] && n=1500000 + local r1 r2 r3 med + r1=$(redis-benchmark -h 127.0.0.1 -p $port -t $op -P $pipe -c $CLIENTS -n $n -r 100000 -q 2>/dev/null | extract_rps) + r2=$(redis-benchmark -h 127.0.0.1 -p $port -t $op -P $pipe -c $CLIENTS -n $n -r 100000 -q 2>/dev/null | extract_rps) + r3=$(redis-benchmark -h 127.0.0.1 -p $port -t $op -P $pipe -c $CLIENTS -n $n -r 100000 -q 2>/dev/null | extract_rps) + med=$(median3 "$r1" "$r2" "$r3") + printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$engine" "$shards" "$op" "$pipe" "$r1" "$r2" "$r3" "$med" >> "$RESULTS" + printf "%-6s s=%-1s %-3s p=%-3s [%s %s %s] median=%s\n" "$engine" "$shards" "$op" "$pipe" "$r1" "$r2" "$r3" "$med" +} + +run_moon () { + local shards=$1 + echo "=== Moon (s=$shards, --appendonly no --disk-offload disable) ===" + ./target/release/moon --port $MOON_PORT --shards $shards --appendonly no --disk-offload disable > /tmp/moon-multi-s$shards.log 2>&1 & + local pid=$! + sleep 2 + redis-cli -p $MOON_PORT PING > /dev/null || { echo "moon s=$shards start fail"; kill $pid; exit 1; } + for op in get set; do for p in 1 16 64; do run_cell moon $MOON_PORT $shards $op $p; done; done + kill $pid 2>/dev/null; wait 2>/dev/null || true + sleep 1 +} + +# --- Moon s=1, s=2, s=4 --- +for s in 1 2 4; do run_moon $s; done + +# --- Redis (single-process baseline) --- +echo "=== Redis (single-process, appendonly no) ===" +redis-server --port $REDIS_PORT --appendonly no --save "" --daemonize no > /tmp/redis-multi.log 2>&1 & +RPID=$! +sleep 2 +redis-cli -p $REDIS_PORT PING > /dev/null || { echo "redis start fail"; kill $RPID; exit 1; } +for op in get set; do for p in 1 16 64; do run_cell redis $REDIS_PORT 1 $op $p; done; done +kill $RPID 2>/dev/null; wait 2>/dev/null || true + +# --- Summary table --- +echo "" +echo "=== Summary ===" +{ + echo "| cell | Redis | Moon s=1 | Moon s=2 | Moon s=4 | best vs Redis |" + echo "|-------------|------------|------------|------------|------------|---------------|" +} | tee "$SUMMARY" +for op in get set; do + for p in 1 16 64; do + r=$(awk -v op="$op" -v p="$p" '$1=="redis" && $3==op && $4==p {print $8}' "$RESULTS") + m1=$(awk -v op="$op" -v p="$p" '$1=="moon" && $2==1 && $3==op && $4==p {print $8}' "$RESULTS") + m2=$(awk -v op="$op" -v p="$p" '$1=="moon" && $2==2 && $3==op && $4==p {print $8}' "$RESULTS") + m4=$(awk -v op="$op" -v p="$p" '$1=="moon" && $2==4 && $3==op && $4==p {print $8}' "$RESULTS") + best=$(awk -v a="$m1" -v b="$m2" -v c="$m4" 'BEGIN {x=a; if(b>x)x=b; if(c>x)x=c; print x}') + ratio=$(awk -v b="$best" -v r="$r" 'BEGIN {printf "%.2f", b/r}') + printf "| %-3s p=%-3s | %-10s | %-10s | %-10s | %-10s | %sx |\n" "$op" "$p" "$r" "$m1" "$m2" "$m4" "$ratio" | tee -a "$SUMMARY" + done +done +echo "" +echo "Full TSV: $RESULTS" +echo "Summary: $SUMMARY" diff --git a/scripts/bench-pr100.sh b/scripts/bench-pr100.sh new file mode 100755 index 00000000..6a00b0ff --- /dev/null +++ b/scripts/bench-pr100.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# PR #100 final perf check: Moon (current HEAD) vs Redis on the same VM. +# s=1, GET + SET, p=1/16/64, c=400, median-of-3. ~15 min total. +set -euo pipefail + +REPO=/Users/tindang/workspaces/tind-repo/moon +MOON_PORT=7530 +REDIS_PORT=7531 +RESULTS=/tmp/pr100-bench.tsv +SUMMARY=/tmp/pr100-bench.md +CLIENTS=400 + +cd "$REPO" +printf "engine\top\tpipeline\tr1\tr2\tr3\tmedian\n" > "$RESULTS" + +extract_rps () { tr '\r' '\n' | grep -oE "[0-9]+\.[0-9]+ requests" | tail -1 | awk '{print $1}'; } +median3 () { printf "%s\n" "$@" | sort -n | sed -n '2p'; } + +run_cell () { + local engine=$1 port=$2 op=$3 pipe=$4 + local n=$((300000 * pipe / 4)) + [[ $n -lt 100000 ]] && n=100000 + [[ $n -gt 1500000 ]] && n=1500000 + local r1 r2 r3 med + r1=$(redis-benchmark -h 127.0.0.1 -p $port -t $op -P $pipe -c $CLIENTS -n $n -r 100000 -q 2>/dev/null | extract_rps) + r2=$(redis-benchmark -h 127.0.0.1 -p $port -t $op -P $pipe -c $CLIENTS -n $n -r 100000 -q 2>/dev/null | extract_rps) + r3=$(redis-benchmark -h 127.0.0.1 -p $port -t $op -P $pipe -c $CLIENTS -n $n -r 100000 -q 2>/dev/null | extract_rps) + med=$(median3 "$r1" "$r2" "$r3") + printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$engine" "$op" "$pipe" "$r1" "$r2" "$r3" "$med" >> "$RESULTS" + printf "%-6s %-3s p=%-3s [%s %s %s] median=%s\n" "$engine" "$op" "$pipe" "$r1" "$r2" "$r3" "$med" +} + +# --- Moon --- +echo "=== Moon (s=1, --appendonly no --disk-offload disable) ===" +./target/release/moon --port $MOON_PORT --shards 1 --appendonly no --disk-offload disable > /tmp/moon-pr100.log 2>&1 & +MPID=$! +sleep 2 +redis-cli -p $MOON_PORT PING > /dev/null || { echo "moon start fail"; kill $MPID; exit 1; } +for op in get set; do for p in 1 16 64; do run_cell moon $MOON_PORT $op $p; done; done +kill $MPID 2>/dev/null; wait 2>/dev/null || true +sleep 1 + +# --- Redis --- +echo "=== Redis (single-process, appendonly no) ===" +redis-server --port $REDIS_PORT --appendonly no --save "" --daemonize no > /tmp/redis-pr100.log 2>&1 & +RPID=$! +sleep 2 +redis-cli -p $REDIS_PORT PING > /dev/null || { echo "redis start fail"; kill $RPID; exit 1; } +for op in get set; do for p in 1 16 64; do run_cell redis $REDIS_PORT $op $p; done; done +kill $RPID 2>/dev/null; wait 2>/dev/null || true + +# --- Summary table --- +echo "" +echo "=== Summary ===" +echo "| cell | Moon | Redis | Moon vs Redis |" | tee "$SUMMARY" +echo "|-------------|------------|------------|---------------|" | tee -a "$SUMMARY" +for op in get set; do + for p in 1 16 64; do + m=$(awk -v op="$op" -v p="$p" '$1=="moon" && $2==op && $3==p {print $7}' "$RESULTS") + r=$(awk -v op="$op" -v p="$p" '$1=="redis" && $2==op && $3==p {print $7}' "$RESULTS") + ratio=$(awk -v m="$m" -v r="$r" 'BEGIN {printf "%.2f", m/r}') + printf "| %-3s p=%-3s | %-10s | %-10s | %sx |\n" "$op" "$p" "$m" "$r" "$ratio" | tee -a "$SUMMARY" + done +done +echo "" +echo "Full TSV: $RESULTS" +echo "Summary: $SUMMARY" diff --git a/scripts/bench-s1-only.sh b/scripts/bench-s1-only.sh new file mode 100644 index 00000000..37c037d6 --- /dev/null +++ b/scripts/bench-s1-only.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Tight L-A.G3 verification: s=1 only, GET+SET, p=1/16/64, median-of-3. +# Used to isolate whether the s=1 regression in the full matrix is real or +# a bench-environment artifact. +REPO=/Users/tindang/workspaces/tind-repo/moon +MOON_PORT=7520 +REDIS_PORT=7521 +RESULTS=/tmp/lane-a-s1-only.tsv +CLIENTS=400 + +cd "$REPO" +printf "engine\tshards\top\tpipeline\tr1\tr2\tr3\tmedian\n" > "$RESULTS" + +extract_rps () { tr '\r' '\n' | grep -oE "[0-9]+\.[0-9]+ requests" | tail -1 | awk '{print $1}'; } +median3 () { printf "%s\n" "$@" | sort -n | sed -n '2p'; } + +run_cell () { + local engine=$1 port=$2 shards=$3 op=$4 pipe=$5 + local n=$((300000 * pipe / 4)) + [[ $n -lt 100000 ]] && n=100000 + [[ $n -gt 1500000 ]] && n=1500000 + local r1 r2 r3 + r1=$(redis-benchmark -h 127.0.0.1 -p $port -t $op -P $pipe -c $CLIENTS -n $n -r 100000 -q 2>/dev/null | extract_rps) + r2=$(redis-benchmark -h 127.0.0.1 -p $port -t $op -P $pipe -c $CLIENTS -n $n -r 100000 -q 2>/dev/null | extract_rps) + r3=$(redis-benchmark -h 127.0.0.1 -p $port -t $op -P $pipe -c $CLIENTS -n $n -r 100000 -q 2>/dev/null | extract_rps) + local med + med=$(median3 "$r1" "$r2" "$r3") + printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$engine" "$shards" "$op" "$pipe" "$r1" "$r2" "$r3" "$med" >> "$RESULTS" + printf "%-6s s=%-2s %-3s p=%-3s [%s %s %s] median=%s\n" "$engine" "$shards" "$op" "$pipe" "$r1" "$r2" "$r3" "$med" +} + +./target/release/moon --port $MOON_PORT --shards 1 --appendonly no --disk-offload disable > /tmp/moon-s1.log 2>&1 & +PID=$! +sleep 2 +redis-cli -p $MOON_PORT PING > /dev/null || { echo "moon start fail"; exit 1; } +for op in get set; do for p in 1 16 64; do run_cell moon $MOON_PORT 1 $op $p; done; done +kill $PID 2>/dev/null; wait 2>/dev/null || true