|
| 1 | +#!/bin/bash |
| 2 | +# Prover scaling benchmark across fib_iterative sizes. |
| 3 | +# Shows how per-phase timing and heap grow with program size, plus linear regression. |
| 4 | +# |
| 5 | +# Usage: bench_prover_scaling.sh [--sizes "500k 1M 2M 4M"] [--runs N] |
| 6 | +# |
| 7 | +# Requires: instruments + jemalloc-stats features. |
| 8 | + |
| 9 | +set -euo pipefail |
| 10 | + |
| 11 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 12 | +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" |
| 13 | +TMP_DIR="/tmp/bench_prover_scaling" |
| 14 | +ELF_DIR="$ROOT_DIR/executor/program_artifacts/asm" |
| 15 | + |
| 16 | +GREEN='\033[0;32m' |
| 17 | +BOLD='\033[1m' |
| 18 | +NC='\033[0m' |
| 19 | + |
| 20 | +SIZES="500k 1M 2M 4M" |
| 21 | +RUNS=1 |
| 22 | + |
| 23 | +while [[ $# -gt 0 ]]; do |
| 24 | + case $1 in |
| 25 | + --sizes) SIZES="$2"; shift 2 ;; |
| 26 | + --runs) RUNS="$2"; shift 2 ;; |
| 27 | + *) echo "Unknown arg: $1"; exit 1 ;; |
| 28 | + esac |
| 29 | +done |
| 30 | + |
| 31 | +suffix_to_steps() { |
| 32 | + case $1 in |
| 33 | + 160k) echo 160000 ;; 250k) echo 250000 ;; 372k) echo 372000 ;; |
| 34 | + 500k) echo 500000 ;; 1M) echo 1000000 ;; 1200k) echo 1200000 ;; |
| 35 | + 2M) echo 2000000 ;; 4M) echo 4000000 ;; 8M) echo 8000000 ;; |
| 36 | + 16M) echo 16000000 ;; 32M) echo 32000000 ;; 64M) echo 64000000 ;; 128M) echo 128000000 ;; |
| 37 | + *) echo "Unknown size: $1" >&2; exit 1 ;; |
| 38 | + esac |
| 39 | +} |
| 40 | + |
| 41 | +rm -rf "$TMP_DIR" && mkdir -p "$TMP_DIR" |
| 42 | + |
| 43 | +echo -e "${GREEN}Building CLI with jemalloc-stats + instruments...${NC}" |
| 44 | +cargo build --release -p cli --features jemalloc-stats,instruments \ |
| 45 | + --manifest-path "$ROOT_DIR/Cargo.toml" 2>&1 | tail -1 |
| 46 | +CLI="$ROOT_DIR/target/release/cli" |
| 47 | + |
| 48 | +# Parse timing (seconds) and heap (MB) from one run's stdout + stderr. |
| 49 | +# Emits key=value lines to stdout. |
| 50 | +parse_run() { |
| 51 | + local stdout=$1 stderr=$2 |
| 52 | + awk ' |
| 53 | + function secs( s) { |
| 54 | + if (match($0, /[0-9]+\.?[0-9]*s/)) { |
| 55 | + s = substr($0, RSTART, RLENGTH - 1) |
| 56 | + return s |
| 57 | + } |
| 58 | + return "" |
| 59 | + } |
| 60 | + /^ Execute / { v = secs(); if (v) print "t_execute=" v } |
| 61 | + /^ Trace build/ { v = secs(); if (v) print "t_trace_build=" v } |
| 62 | + /^ AIR construction/ { v = secs(); if (v) print "t_air=" v } |
| 63 | + /^ Pre-pass/ { v = secs(); if (v) print "t_prepass=" v } |
| 64 | + /^ Round 1 / { v = secs(); if (v) print "t_round1=" v } |
| 65 | + /Main trace commits/ { v = secs(); if (v) print "t_main_commits="v } |
| 66 | + /Aux trace build/ { v = secs(); if (v) print "t_aux_build=" v } |
| 67 | + /Aux trace commit/ { v = secs(); if (v) print "t_aux_commit=" v } |
| 68 | + /Rounds 2/ { v = secs(); if (v) print "t_rounds24=" v } |
| 69 | + /Main expand_pool_to_lde/{ v = secs(); if (v) print "t_main_lde=" v } |
| 70 | + /Aux expand_pool_to_lde/ { v = secs(); if (v) print "t_aux_lde=" v } |
| 71 | + /Main commit \(Merkle\)/ { v = secs(); if (v) print "t_main_merkle=" v } |
| 72 | + /Aux commit \(Merkle\)/ { v = secs(); if (v) print "t_aux_merkle=" v } |
| 73 | + /^ Total FFT/ { v = secs(); if (v) print "t_total_fft=" v } |
| 74 | + /^ Total Merkle/ { v = secs(); if (v) print "t_total_merkle="v } |
| 75 | + /^ TOTAL / { v = secs(); if (v) print "t_total=" v } |
| 76 | + /After execute/ { print "h_execute=" $(NF-1) } |
| 77 | + /After trace build/ { print "h_trace_build=" $(NF-1) } |
| 78 | + /After AIR/ { print "h_air=" $(NF-1) } |
| 79 | + /after pool alloc/ { print "h_pool_alloc=" $(NF-1) } |
| 80 | + /after main commits/ { print "h_main_commits=" $(NF-1) } |
| 81 | + /after aux build/ { print "h_aux_build=" $(NF-1) } |
| 82 | + /after aux commit/ { print "h_aux_commit=" $(NF-1) } |
| 83 | + ' "$stderr" |
| 84 | + |
| 85 | + grep -o 'Peak heap: [0-9]*' "$stdout" | awk '{print "peak=" $3}' |
| 86 | + grep -o 'Proving time: [0-9.]*' "$stdout" | awk '{print "wall=" $3}' |
| 87 | +} |
| 88 | + |
| 89 | +get_val() { |
| 90 | + awk -F= -v k="$2" '$1==k {print $2; exit}' "$1" 2>/dev/null |
| 91 | +} |
| 92 | + |
| 93 | +# Pick the run whose wall-clock is the median across RUNS; copy its data file. |
| 94 | +# Writes final per-size data to ${size}_data.txt. |
| 95 | +select_median_run() { |
| 96 | + local size=$1 |
| 97 | + if [ "$RUNS" -eq 1 ]; then |
| 98 | + cp "$TMP_DIR/${size}_run_1.txt" "$TMP_DIR/${size}_data.txt" |
| 99 | + return |
| 100 | + fi |
| 101 | + # Collect (wall, run_index), sort by wall, pick middle |
| 102 | + local pairs="" |
| 103 | + for i in $(seq 1 "$RUNS"); do |
| 104 | + local w |
| 105 | + w=$(get_val "$TMP_DIR/${size}_run_${i}.txt" wall) |
| 106 | + pairs+=" $w $i" |
| 107 | + done |
| 108 | + local median_idx |
| 109 | + median_idx=$(echo "$pairs" | tr ' ' '\n' | awk 'NF' | paste -d' ' - - | \ |
| 110 | + sort -n | awk -v n="$RUNS" 'NR == int((n+1)/2) {print $2}') |
| 111 | + cp "$TMP_DIR/${size}_run_${median_idx}.txt" "$TMP_DIR/${size}_data.txt" |
| 112 | +} |
| 113 | + |
| 114 | +for size in $SIZES; do |
| 115 | + ELF="$ELF_DIR/fib_iterative_${size}.elf" |
| 116 | + [ -f "$ELF" ] || { echo "Missing: $ELF"; continue; } |
| 117 | + steps=$(suffix_to_steps "$size") |
| 118 | + echo -e "${GREEN}Running fib_iterative_${size} (${RUNS} runs)...${NC}" |
| 119 | + |
| 120 | + for i in $(seq 1 "$RUNS"); do |
| 121 | + STDOUT="$TMP_DIR/${size}_run_${i}_stdout.txt" |
| 122 | + STDERR="$TMP_DIR/${size}_run_${i}_stderr.txt" |
| 123 | + "$CLI" prove "$ELF" -o "$TMP_DIR/proof.bin" --time >"$STDOUT" 2>"$STDERR" |
| 124 | + rm -f "$TMP_DIR/proof.bin" |
| 125 | + { |
| 126 | + echo "steps=$steps" |
| 127 | + parse_run "$STDOUT" "$STDERR" |
| 128 | + } > "$TMP_DIR/${size}_run_${i}.txt" |
| 129 | + done |
| 130 | + select_median_run "$size" |
| 131 | +done |
| 132 | + |
| 133 | +# --------------------------------------------------------------------------- |
| 134 | +# Rendering |
| 135 | +# --------------------------------------------------------------------------- |
| 136 | + |
| 137 | +print_row() { |
| 138 | + local label=$1 key=$2 unit=$3 |
| 139 | + printf " %-26s" "$label" |
| 140 | + for size in $SIZES; do |
| 141 | + local data="$TMP_DIR/${size}_data.txt" |
| 142 | + if [ ! -f "$data" ]; then |
| 143 | + printf " %10s" "-"; continue |
| 144 | + fi |
| 145 | + local v |
| 146 | + v=$(get_val "$data" "$key") |
| 147 | + if [ -z "$v" ]; then |
| 148 | + printf " %10s" "-" |
| 149 | + elif [ "$unit" = "s" ]; then |
| 150 | + printf " %9.2fs" "$v" |
| 151 | + else |
| 152 | + printf " %10d" "$v" |
| 153 | + fi |
| 154 | + done |
| 155 | + echo "" |
| 156 | +} |
| 157 | + |
| 158 | +print_header() { |
| 159 | + printf " %-26s" "" |
| 160 | + for size in $SIZES; do printf " %10s" "$size"; done |
| 161 | + echo "" |
| 162 | + printf " %-26s" "" |
| 163 | + for size in $SIZES; do printf " %10s" "──────────"; done |
| 164 | + echo "" |
| 165 | +} |
| 166 | + |
| 167 | +echo "" |
| 168 | +echo -e "${BOLD}=== TIMING (seconds) ===${NC}" |
| 169 | +print_header |
| 170 | +print_row "Execute" t_execute s |
| 171 | +print_row "Trace build" t_trace_build s |
| 172 | +print_row "AIR construction" t_air s |
| 173 | +print_row "Pre-pass" t_prepass s |
| 174 | +print_row "Round 1" t_round1 s |
| 175 | +print_row " Main trace commits" t_main_commits s |
| 176 | +print_row " Main LDE" t_main_lde s |
| 177 | +print_row " Main Merkle" t_main_merkle s |
| 178 | +print_row " Aux trace build" t_aux_build s |
| 179 | +print_row " Aux trace commit" t_aux_commit s |
| 180 | +print_row " Aux LDE" t_aux_lde s |
| 181 | +print_row " Aux Merkle" t_aux_merkle s |
| 182 | +print_row "Rounds 2-4" t_rounds24 s |
| 183 | +print_row "Total FFT (all rounds)" t_total_fft s |
| 184 | +print_row "Total Merkle" t_total_merkle s |
| 185 | +print_row "TOTAL" t_total s |
| 186 | + |
| 187 | +echo "" |
| 188 | +echo -e "${BOLD}=== HEAP (MB absolute) ===${NC}" |
| 189 | +print_header |
| 190 | +print_row "After execute" h_execute mb |
| 191 | +print_row "After trace build" h_trace_build mb |
| 192 | +print_row "After AIR construction" h_air mb |
| 193 | +print_row "After pool alloc" h_pool_alloc mb |
| 194 | +print_row "After main commits" h_main_commits mb |
| 195 | +print_row "After aux build" h_aux_build mb |
| 196 | +print_row "After aux commit" h_aux_commit mb |
| 197 | +print_row "Peak heap" peak mb |
| 198 | + |
| 199 | +# --------------------------------------------------------------------------- |
| 200 | +# Linear regression per metric: y = a + b * (steps / 1M) |
| 201 | +# --------------------------------------------------------------------------- |
| 202 | + |
| 203 | +regress() { |
| 204 | + local label=$1 key=$2 unit=$3 |
| 205 | + local pairs="" |
| 206 | + for size in $SIZES; do |
| 207 | + local data="$TMP_DIR/${size}_data.txt" |
| 208 | + [ -f "$data" ] || continue |
| 209 | + local steps v |
| 210 | + steps=$(get_val "$data" steps) |
| 211 | + v=$(get_val "$data" "$key") |
| 212 | + [ -z "$v" ] && continue |
| 213 | + [ -z "$steps" ] && continue |
| 214 | + local steps_m |
| 215 | + steps_m=$(awk -v s="$steps" 'BEGIN {printf "%.6f", s / 1000000}') |
| 216 | + pairs+=" $steps_m $v" |
| 217 | + done |
| 218 | + echo "$pairs" | awk -v label="$label" -v unit="$unit" '{ |
| 219 | + n = NF / 2 |
| 220 | + if (n < 2) { printf " %-26s (insufficient data)\n", label; next } |
| 221 | + for (i = 0; i < n; i++) { x[i] = $(2*i+1); y[i] = $(2*i+2) } |
| 222 | + sx=0; sy=0; sxx=0; sxy=0 |
| 223 | + for (i=0;i<n;i++) { sx+=x[i]; sy+=y[i]; sxx+=x[i]*x[i]; sxy+=x[i]*y[i] } |
| 224 | + d = n*sxx - sx*sx |
| 225 | + if (d == 0) { printf " %-26s (constant)\n", label; next } |
| 226 | + b = (n*sxy - sx*sy) / d |
| 227 | + a = (sy - b*sx) / n |
| 228 | + ym = sy/n; ss_tot=0; ss_res=0 |
| 229 | + for (i=0;i<n;i++) { p=a+b*x[i]; ss_res+=(y[i]-p)^2; ss_tot+=(y[i]-ym)^2 } |
| 230 | + r2 = (ss_tot>0) ? 1 - ss_res/ss_tot : 1 |
| 231 | + if (unit == "s") |
| 232 | + printf " %-26s %+7.2fs/M (base %6.2fs, R\xc2\xb2=%.3f)\n", label, b, a, r2 |
| 233 | + else |
| 234 | + printf " %-26s %+7.0f MB/M (base %6.0f MB, R\xc2\xb2=%.3f)\n", label, b, a, r2 |
| 235 | + }' |
| 236 | +} |
| 237 | + |
| 238 | +echo "" |
| 239 | +echo -e "${BOLD}=== TIMING GROWTH (per 1M steps) ===${NC}" |
| 240 | +regress "Execute" t_execute s |
| 241 | +regress "Trace build" t_trace_build s |
| 242 | +regress "AIR construction" t_air s |
| 243 | +regress "Pre-pass" t_prepass s |
| 244 | +regress "Round 1" t_round1 s |
| 245 | +regress "Rounds 2-4" t_rounds24 s |
| 246 | +regress "Total FFT" t_total_fft s |
| 247 | +regress "Total Merkle" t_total_merkle s |
| 248 | +regress "TOTAL" t_total s |
| 249 | + |
| 250 | +echo "" |
| 251 | +echo -e "${BOLD}=== HEAP GROWTH (per 1M steps) ===${NC}" |
| 252 | +regress "After execute" h_execute mb |
| 253 | +regress "After trace build" h_trace_build mb |
| 254 | +regress "After AIR construction" h_air mb |
| 255 | +regress "After pool alloc" h_pool_alloc mb |
| 256 | +regress "After main commits" h_main_commits mb |
| 257 | +regress "After aux build" h_aux_build mb |
| 258 | +regress "After aux commit" h_aux_commit mb |
| 259 | +regress "Peak heap" peak mb |
| 260 | + |
| 261 | +echo "" |
| 262 | +echo "Raw data: $TMP_DIR/" |
0 commit comments