mirror of
https://github.com/mileswolfallen2/gelectron.git
synced 2026-09-08 04:43:14 +00:00
235 lines
9.9 KiB
Bash
Executable File
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 ""
|