#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" ANDROID_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)" RESULTS_DIR="$ANDROID_DIR/benchmark/results" CLASS_FILTER="ai.openclaw.app.benchmark.StartupMacrobenchmark#coldStartup" BASELINE_JSON="" usage() { cat <<'EOF' Usage: ./scripts/perf-startup-benchmark.sh [--baseline ] Runs cold-start macrobenchmark only, then prints a compact summary. Also saves a timestamped snapshot JSON under benchmark/results/. If --baseline is omitted, compares against latest previous snapshot when available. EOF } while [[ $# -gt 0 ]]; do case "$1" in --baseline) BASELINE_JSON="${2:-}" shift 2 ;; -h|--help) usage exit 0 ;; *) echo "Unknown arg: $1" >&2 usage >&2 exit 2 ;; esac done if ! command -v jq >/dev/null 2>&1; then echo "jq required but missing." >&2 exit 1 fi if ! command -v adb >/dev/null 2>&1; then echo "adb required but missing." >&2 exit 1 fi device_count="$(adb devices | awk 'NR>1 && $2=="device" {c+=1} END {print c+0}')" if [[ "$device_count" -lt 1 ]]; then echo "No connected Android device (adb state=device)." >&2 exit 1 fi mkdir -p "$RESULTS_DIR" run_log="$(mktemp -t openclaw-android-bench.XXXXXX.log)" trap 'rm -f "$run_log"' EXIT cd "$ANDROID_DIR" ./gradlew :benchmark:connectedDebugAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.class="$CLASS_FILTER" \ --console=plain \ >"$run_log" 2>&1 latest_json="$( find "$ANDROID_DIR/benchmark/build/outputs/connected_android_test_additional_output/debug/connected" \ -name '*benchmarkData.json' -type f \ | while IFS= read -r file; do printf '%s\t%s\n' "$(stat -f '%m' "$file")" "$file" done \ | sort -nr \ | head -n1 \ | cut -f2- )" if [[ -z "$latest_json" || ! -f "$latest_json" ]]; then echo "benchmarkData.json not found after run." >&2 tail -n 120 "$run_log" >&2 exit 1 fi timestamp="$(date +%Y%m%d-%H%M%S)" snapshot_json="$RESULTS_DIR/startup-$timestamp.json" cp "$latest_json" "$snapshot_json" median_ms="$(jq -r '.benchmarks[] | select(.name=="coldStartup") | .metrics.timeToInitialDisplayMs.median' "$snapshot_json")" min_ms="$(jq -r '.benchmarks[] | select(.name=="coldStartup") | .metrics.timeToInitialDisplayMs.minimum' "$snapshot_json")" max_ms="$(jq -r '.benchmarks[] | select(.name=="coldStartup") | .metrics.timeToInitialDisplayMs.maximum' "$snapshot_json")" cov="$(jq -r '.benchmarks[] | select(.name=="coldStartup") | .metrics.timeToInitialDisplayMs.coefficientOfVariation' "$snapshot_json")" device="$(jq -r '.context.build.model' "$snapshot_json")" sdk="$(jq -r '.context.build.version.sdk' "$snapshot_json")" runs_count="$(jq -r '.benchmarks[] | select(.name=="coldStartup") | .metrics.timeToInitialDisplayMs.runs | length' "$snapshot_json")" printf 'startup.cold.median_ms=%.3f min_ms=%.3f max_ms=%.3f cov=%.4f runs=%s device=%s sdk=%s\n' \ "$median_ms" "$min_ms" "$max_ms" "$cov" "$runs_count" "$device" "$sdk" echo "snapshot_json=$snapshot_json" if [[ -z "$BASELINE_JSON" ]]; then BASELINE_JSON="$( find "$RESULTS_DIR" -name 'startup-*.json' -type f \ | while IFS= read -r file; do if [[ "$file" == "$snapshot_json" ]]; then continue fi printf '%s\t%s\n' "$(stat -f '%m' "$file")" "$file" done \ | sort -nr \ | head -n1 \ | cut -f2- )" fi if [[ -n "$BASELINE_JSON" ]]; then if [[ ! -f "$BASELINE_JSON" ]]; then echo "Baseline file missing: $BASELINE_JSON" >&2 exit 1 fi base_median="$(jq -r '.benchmarks[] | select(.name=="coldStartup") | .metrics.timeToInitialDisplayMs.median' "$BASELINE_JSON")" delta_ms="$(awk -v a="$median_ms" -v b="$base_median" 'BEGIN { printf "%.3f", (a-b) }')" delta_pct="$(awk -v a="$median_ms" -v b="$base_median" 'BEGIN { if (b==0) { print "nan" } else { printf "%.2f", ((a-b)/b)*100 } }')" echo "baseline_median_ms=$base_median delta_ms=$delta_ms delta_pct=$delta_pct%" fi