Files

235 lines
9.9 KiB
Bash
Executable File

#!/usr/bin/env bash
BENCH_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$BENCH_DIR/.."
ELECTRON_BIN="$ROOT_DIR/node_modules/.bin/electron"
GEELECTRON_BIN="$ROOT_DIR/target/release/gelectron"
DEMO_APP="$BENCH_DIR"
RUNS=10
RESULTS_DIR="$BENCH_DIR/results"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p "$RESULTS_DIR"
echo ""
echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Gelectron vs Electron Benchmark"
echo " Runs: $RUNS"
echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# ── Preflight ──
if [ ! -x "$ELECTRON_BIN" ]; then
echo " ✗ Electron not found. Run: npm install electron"
exit 1
fi
if [ ! -x "$GEELECTRON_BIN" ]; then
echo " ✗ Gelectron binary not found. Run: cargo build --release"
exit 1
fi
# ── Measure package size ──
echo " Measuring package sizes..."
# Electron: the entire electron node_modules + demo
ELECTRON_SIZE=$(du -sm "$ROOT_DIR/node_modules/electron" 2>/dev/null | awk '{print $1}')
ELECTRON_DIST_SIZE=$(du -sm "$ROOT_DIR/node_modules/electron/dist" 2>/dev/null | awk '{print $1}')
# Gelectron: binary + compat layer + node
GEELECTRON_BIN_SIZE=$(du -sm "$GEELECTRON_BIN" 2>/dev/null | awk '{print $1}')
GEELECTRON_COMPAT_SIZE=$(du -sm "$ROOT_DIR/src/electron" 2>/dev/null | awk '{print $1}')
# Node.js bundled with gelectron would be ~20MB, but for dev we use system node
NODE_SIZE=$(du -sm "$(which node)" 2>/dev/null | awk '{print $1}')
echo ""
echo " ┌─────────────────────────────────────────┐"
echo " │ Package Size Comparison │"
echo " ├─────────────────────┬───────────┬────────┤"
echo " │ Component │ Electron │Gelectron│"
echo " ├─────────────────────┼───────────┼────────┤"
printf " │ Runtime (binary) │ %7sMB │ %6sMB │\n" "${ELECTRON_DIST_SIZE:-?}" "${GEELECTRON_BIN_SIZE:-?}"
printf " │ npm package total │ %7sMB │ N/A │\n" "${ELECTRON_SIZE:-?}"
printf " │ Compat layer │ N/A │ %6sMB │\n" "${GEELECTRON_COMPAT_SIZE:-?}"
echo " └─────────────────────┴───────────┴────────┘"
echo ""
# ── Recursive helpers (macOS-compatible) ──
# Sum RSS of a PID and all its descendants (process tree).
total_rss() {
local root="$1"
ps -eo pid,ppid,rss | awk -v root="$root" '
function sum_tree(pid, total, i) {
total = 0;
for(i=1;i<=NR;i++) { if(pids[i]==pid) { total = rss[i]; break; } }
for(i=1;i<=NR;i++) { if(ppids[i]==pid && pids[i]!=pid) total += sum_tree(pids[i]); }
return total;
}
{ pids[NR]=$1; ppids[NR]=$2; rss[NR]=$3+0; }
END { printf "%d\n", sum_tree(root+0); }
'
}
# Recursively kill a process tree.
kill_tree() {
local root="$1"
for c in $(ps -eo pid,ppid | awk -v p=$root '$2==p && $1!=p {print $1}'); do
kill_tree "$c"
done
kill -9 "$root" 2>/dev/null || true
}
# ── Benchmark function ──
# Runs a binary, waits for it to stabilize, captures memory (process tree RSS), kills it.
benchmark_run() {
local name="$1"
shift
local cmd=("$@")
# Launch and capture PID
"${cmd[@]}" &>/dev/null &
local pid=$!
# Wait for all processes to reach steady state
sleep 5
# Check if still running
if ! kill -0 "$pid" 2>/dev/null; then
echo "0,0"
return
fi
# Total RSS across entire process tree
local rss_kb
rss_kb=$(total_rss "$pid")
# Kill entire process tree
kill_tree "$pid"
sleep 0.5
echo "${rss_kb:-0}"
}
# ── Run benchmarks ──
ELECTRON_TIMES=()
ELECTRON_MEMS=()
GEELECTRON_TIMES=()
GEELECTRON_MEMS=()
echo " Running Electron x$RUNS..."
for i in $(seq 1 $RUNS); do
START=$(python3 -c 'import time; print(time.time())')
RSS_KB=$(benchmark_run "electron" "$ELECTRON_BIN" "$BENCH_DIR")
END=$(python3 -c 'import time; print(time.time())')
ELAPSED=$(python3 -c "print(f'{$END - $START:.3f}')")
MEM_MB=$(python3 -c "print(f'{$RSS_KB / 1024:.1f}')")
ELECTRON_TIMES+=("$ELAPSED")
ELECTRON_MEMS+=("$MEM_MB")
printf " Run %2d: %ss startup, %sMB total RSS\n" "$i" "$ELAPSED" "$MEM_MB"
done
echo ""
echo " Running Gelectron x$RUNS..."
for i in $(seq 1 $RUNS); do
START=$(python3 -c 'import time; print(time.time())')
RSS_KB=$(benchmark_run "gelectron" "$GEELECTRON_BIN" "$DEMO_APP")
END=$(python3 -c 'import time; print(time.time())')
ELAPSED=$(python3 -c "print(f'{$END - $START:.3f}')")
MEM_MB=$(python3 -c "print(f'{$RSS_KB / 1024:.1f}')")
GEELECTRON_TIMES+=("$ELAPSED")
GEELECTRON_MEMS+=("$MEM_MB")
printf " Run %2d: %ss startup, %sMB total RSS\n" "$i" "$ELAPSED" "$MEM_MB"
done
# ── Compute averages ──
E_TIMES_STR=$(IFS=,; echo "${ELECTRON_TIMES[*]}")
E_MEMS_STR=$(IFS=,; echo "${ELECTRON_MEMS[*]}")
G_TIMES_STR=$(IFS=,; echo "${GEELECTRON_TIMES[*]}")
G_MEMS_STR=$(IFS=,; echo "${GEELECTRON_MEMS[*]}")
AVG_E_TIME=$(python3 -c "ts=[$E_TIMES_STR]; print(f'{sum(ts)/len(ts):.3f}')")
AVG_E_MEM=$(python3 -c "ms=[$E_MEMS_STR]; print(f'{sum(ms)/len(ms):.1f}')")
AVG_G_TIME=$(python3 -c "ts=[$G_TIMES_STR]; print(f'{sum(ts)/len(ts):.3f}')")
AVG_G_MEM=$(python3 -c "ms=[$G_MEMS_STR]; print(f'{sum(ms)/len(ms):.1f}')")
# ── Compute min/max ──
MIN_E_TIME=$(python3 -c "print(f'{min([float(x) for x in [$E_TIMES_STR]]):.3f}')")
MAX_E_TIME=$(python3 -c "print(f'{max([float(x) for x in [$E_TIMES_STR]]):.3f}')")
MIN_G_TIME=$(python3 -c "print(f'{min([float(x) for x in [$G_TIMES_STR]]):.3f}')")
MAX_G_TIME=$(python3 -c "print(f'{max([float(x) for x in [$G_TIMES_STR]]):.3f}')")
MIN_E_MEM=$(python3 -c "print(f'{min([float(x) for x in [$E_MEMS_STR]]):.1f}')")
MAX_E_MEM=$(python3 -c "print(f'{max([float(x) for x in [$E_MEMS_STR]]):.1f}')")
MIN_G_MEM=$(python3 -c "print(f'{min([float(x) for x in [$G_MEMS_STR]]):.1f}')")
MAX_G_MEM=$(python3 -c "print(f'{max([float(x) for x in [$G_MEMS_STR]]):.1f}')")
# ── Compute percentages ──
TIME_PCT=$(python3 -c "e=$AVG_E_TIME; g=$AVG_G_TIME; print(f'{(1 - g / e) * 100:+.1f}')")
MEM_PCT=$(python3 -c "e=$AVG_E_MEM; g=$AVG_G_MEM; print(f'{(1 - g / e) * 100:+.1f}')")
SIZE_PCT=$(python3 -c "e=${ELECTRON_DIST_SIZE:-1}; g=${GEELECTRON_BIN_SIZE:-0}; print(f'{(1 - g / max(e, 1)) * 100:+.1f}')")
# ── Results ──
echo ""
echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " RESULTS ($RUNS runs, averages)"
echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo " ┌──────────────────────┬──────────┬──────────┬──────────┐"
echo " │ Metric │ Electron │Gelectron │ Delta │"
echo " ├──────────────────────┼──────────┼──────────┼──────────┤"
printf " │ Startup time (avg) │ %7ss │ %7ss │ %6s%% │\n" "$AVG_E_TIME" "$AVG_G_TIME" "$TIME_PCT"
printf " │ Startup time (min) │ %7ss │ %7ss │ │\n" "$MIN_E_TIME" "$MIN_G_TIME"
printf " │ Startup time (max) │ %7ss │ %7ss │ │\n" "$MAX_E_TIME" "$MAX_G_TIME"
echo " ├──────────────────────┼──────────┼──────────┼──────────┤"
printf " │ Memory RSS (avg) │ %6sMB │ %6sMB │ %6s%% │\n" "$AVG_E_MEM" "$AVG_G_MEM" "$MEM_PCT"
printf " │ Memory RSS (min) │ %6sMB │ %6sMB │ │\n" "$MIN_E_MEM" "$MIN_G_MEM"
printf " │ Memory RSS (max) │ %6sMB │ %6sMB │ │\n" "$MAX_E_MEM" "$MAX_G_MEM"
echo " ├──────────────────────┼──────────┼──────────┼──────────┤"
printf " │ Runtime binary size │ %6sMB │ %6sMB │ %6s%% │\n" "${ELECTRON_DIST_SIZE:-?}" "${GEELECTRON_BIN_SIZE:-?}" "$SIZE_PCT"
echo " └──────────────────────┴──────────┴──────────┴──────────┘"
echo ""
echo " Positive delta = Gelectron is smaller/faster"
echo " Negative delta = Gelectron is larger/slower"
echo ""
# ── Save raw data ──
RESULT_FILE="$RESULTS_DIR/benchmark_${TIMESTAMP}.json"
cat > "$RESULT_FILE" <<EOF
{
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"runs": $RUNS,
"platform": "$(uname -s) $(uname -m)",
"electron": {
"version": "$($ELECTRON_BIN --version 2>/dev/null || echo 'unknown')",
"avg_startup_s": $AVG_E_TIME,
"min_startup_s": $MIN_E_TIME,
"max_startup_s": $MAX_E_TIME,
"avg_memory_mb": $AVG_E_MEM,
"min_memory_mb": $MIN_E_MEM,
"max_memory_mb": $MAX_E_MEM,
"runtime_size_mb": ${ELECTRON_DIST_SIZE:-0},
"raw_times": [$(printf '%s,' "${ELECTRON_TIMES[@]}" | sed 's/,$//')],
"raw_memory": [$(printf '%s,' "${ELECTRON_MEMS[@]}" | sed 's/,$//')]
},
"gelectron": {
"avg_startup_s": $AVG_G_TIME,
"min_startup_s": $MIN_G_TIME,
"max_startup_s": $MAX_G_TIME,
"avg_memory_mb": $AVG_G_MEM,
"min_memory_mb": $MIN_G_MEM,
"max_memory_mb": $MAX_G_MEM,
"runtime_size_mb": ${GEELECTRON_BIN_SIZE:-0},
"raw_times": [$(printf '%s,' "${GEELECTRON_TIMES[@]}" | sed 's/,$//')],
"raw_memory": [$(printf '%s,' "${GEELECTRON_MEMS[@]}" | sed 's/,$//')]
}
}
EOF
echo " Raw data saved to: $RESULT_FILE"
echo ""
echo " ── Done ──"
echo ""