onesvm-browser-server/bench/lightpanda/run.sh
chii 983259836d chore: init workspace with onesvm-dev-md + casa-commander
docs: 联网搜索服务架构方案全套(plan-final/design-arch/选型决策/整合导览/MCP文档/部署预设/联调手册)
bench: 5 方案 + 代理 + 站点矩阵本机实测工程(无密钥)
部署目标:primary mgr1 先行测试(待批准后执行)
2026-09-01 15:19:52 +08:00

246 lines
7.9 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# lightpanda S3c: up → idle 5min → T4 tiers ×3 → T5 (worker=4) → summarize
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
# shellcheck disable=SC1091
source "$ROOT/_lib.sh"
NAME="bench-s3c-lightpanda"
IMG="docker.io/lightpanda/browser:0.3.7"
CDP_HTTP="${CDP_HTTP:-http://127.0.0.1:19222}"
SCHEME="${SCHEME:-lightpanda}"
COMPOSE_PROJ="${COMPOSE_PROJ:-bench-s3c-lightpanda}"
T5_PAR="${T5_PAR:-4}"
RAW="$ROOT/raw"
SAMP="$ROOT/samples"
mkdir -p "$RAW" "$SAMP"
export CDP_HTTP SCHEME
exec > >(tee -a "$RAW/run.log") 2>&1
log "=== $SCHEME S3c start ==="
ensure_image "$IMG" "$RAW/pull.how"
PULL_HOW="$(cat "$RAW/pull.how" 2>/dev/null || echo unknown)"
if [[ "${SKIP_UP:-}" != "1" ]]; then
docker rm -f "$NAME" >/dev/null 2>&1 || true
log "docker compose up -d project=$COMPOSE_PROJ"
(cd "$ROOT" && docker compose -p "$COMPOSE_PROJ" up -d --force-recreate)
fi
log "wait CDP $CDP_HTTP/json/version"
if ! wait_http "$CDP_HTTP/json/version" 40; then
log "json/version not ready; try /json/list"
wait_http "$CDP_HTTP/json/list" 20 || true
docker logs --tail 80 "$NAME" | tee "$RAW/container-start.log" || true
fi
curl -sS --max-time 5 "$CDP_HTTP/json/version" | tee "$RAW/cdp-version.json" || true
echo
ARCH="$(docker image inspect --format '{{.Architecture}}' "$IMG" 2>/dev/null || echo '?')"
DIGEST="$(docker inspect --format '{{index .RepoDigests 0}}' "$NAME" 2>/dev/null || true)"
IID="$(docker inspect --format '{{.Image}}' "$NAME" 2>/dev/null || true)"
log "image arch=$ARCH digest=$DIGEST pull=$PULL_HOW"
PROXY_CODE="$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 10 --max-time 20 \
-x http://127.0.0.1:17890 -A onesvm-bench/s3c \
https://www.google.com/generate_204 || true)"
log "proxy generate_204 http=$PROXY_CODE"
HOST_RES="$(docker exec "$NAME" sh -c 'getent hosts host.docker.internal || true' 2>/dev/null || echo unresolved)"
log "host.docker.internal => $HOST_RES"
if [[ "${SKIP_IDLE:-}" != "1" ]]; then
log "idle 300s interval=5"
sample_stats "$RAW/idle.tsv" 5 300 "$NAME"
log "idle done"
else
log "SKIP_IDLE=1"
fi
T4_CTRL="https://quotes.toscrape.com/js/"
T4_MEDIUM="https://medium.com/airbnb-engineering/rearchitecting-airbnbs-frontend-b5da7e3e8395"
T4_SHOP="https://www.allbirds.com/products/mens-wool-runners"
T4_AMZ="https://www.amazon.com/dp/B08N5WRWNW"
T4_T3X="https://duckduckgo.com/?q=best+bluetooth+earbuds+2026+reddit"
SENT="$RAW/session.done"
rm -f "$SENT"
sample_until_file "$RAW/session.tsv" 1 "$SENT" 10 "$NAME" &
STAT_PID=$!
sleep 10
: > "$RAW/t4.jsonl"
run_tier() {
local tier="$1" url="$2" n="$3"
local i
for i in $(seq 1 "$n"); do
log "$tier trial $i url=$url"
set +e
line="$(bash "$ROOT/request.sh" "$tier" "$url" 25 2>"$RAW/${tier}-${i}.excerpt")"
rc=$?
set -e
echo "$line" | tee -a "$RAW/t4.jsonl"
if [[ "$i" -eq 1 ]]; then
cp "$RAW/${tier}-${i}.excerpt" "$SAMP/${tier}.json" 2>/dev/null || true
fi
log "$tier trial $i rc=$rc"
sleep 1
done
}
run_tier T4-ctrl "$T4_CTRL" 3
run_tier T4-medium "$T4_MEDIUM" 3
run_tier T4-shop "$T4_SHOP" 3
run_tier T4-amz "$T4_AMZ" 3
run_tier T4-t3x "$T4_T3X" 1
touch "$SENT"
wait "$STAT_PID" || true
log "T5 60x T4-ctrl parallelism=$T5_PAR queue=client_fifo"
SENT5="$RAW/t5.done"
rm -f "$SENT5"
sample_until_file "$RAW/t5-stats.tsv" 1 "$SENT5" 15 "$NAME" &
STAT5_PID=$!
T5_START_MS=$(python3 -c 'import time;print(int(time.time()*1000))')
: > "$RAW/t5.jsonl"
: > "$RAW/t5-queue.jsonl"
python3 - "$ROOT" "$T4_CTRL" "$T5_PAR" "$T5_START_MS" "$RAW/t5.jsonl" "$RAW/t5-queue.jsonl" <<'PY'
import subprocess, time, json, os, sys
from concurrent.futures import ThreadPoolExecutor, as_completed
root, url, par, t0, outp, qpath = sys.argv[1:7]
par = int(par)
t0 = int(t0) / 1000.0
n = 60
req = os.path.join(root, "request.sh")
def one(i):
enq = time.time()
p = subprocess.run(["bash", req, "T5", url, "30"], capture_output=True, text=True)
end = time.time()
line = p.stdout.strip().splitlines()[-1] if p.stdout.strip() else "{}"
try:
row = json.loads(line)
except Exception:
row = {"assert_ok": False, "t_total": end - enq, "note": "parse", "http_code": 0}
start = end - float(row.get("t_total") or 0)
q = {
"i": i,
"enqueue_s": enq - t0,
"start_s": start - t0,
"end_s": end - t0,
"queue_wait_s": max(0.0, start - enq),
"assert_ok": row.get("assert_ok"),
"t_total": row.get("t_total"),
}
return row, q
rows = []
qs = []
with ThreadPoolExecutor(max_workers=par) as ex:
futs = [ex.submit(one, i) for i in range(1, n + 1)]
for f in as_completed(futs):
row, q = f.result()
rows.append(row)
qs.append(q)
with open(outp, "w", encoding="utf-8") as f:
for r in rows:
f.write(json.dumps(r, ensure_ascii=False) + "\n")
with open(qpath, "w", encoding="utf-8") as f:
for q in qs:
f.write(json.dumps(q) + "\n")
print(f"t5 done n={len(rows)} ok={sum(1 for r in rows if r.get('assert_ok'))}")
PY
T5_END=$(date +%s)
# wall from tsv start roughly
T5_WALL=$(python3 - "$RAW/t5-queue.jsonl" <<'PY'
import json,sys
from pathlib import Path
qs=[json.loads(l) for l in Path(sys.argv[1]).read_text().splitlines() if l.strip()]
if not qs:
print(0); raise SystemExit
print(round(max(q["end_s"] for q in qs), 3))
PY
)
log "T5 wall=${T5_WALL}s"
touch "$SENT5"
wait "$STAT5_PID" || true
python3 - "$RAW/t5.jsonl" "$SAMP" <<'PY'
import json
from pathlib import Path
import sys
p=Path(sys.argv[1]); d=Path(sys.argv[2])
rows=[json.loads(l) for l in p.read_text().splitlines() if l.strip()]
for i,r in enumerate(rows[:2],1):
(d/f"t5-{i}.json").write_text(json.dumps(r,ensure_ascii=False,indent=2)+"\n")
print("saved t5 samples", len(rows))
PY
# html vs text note from first ctrl excerpt
python3 - "$SAMP" "$RAW" <<'PY'
import json
from pathlib import Path
samp=Path("$SAMP".replace("$SAMP", __import__("os").environ.get("SAMP","")))
PY
OOM="$(docker inspect --format '{{.State.OOMKilled}}' "$NAME" 2>/dev/null || echo false)"
STATUS="$(docker inspect --format '{{.State.Status}}' "$NAME" 2>/dev/null || echo unknown)"
log "status=$STATUS oom=$OOM"
docker logs --tail 50 "$NAME" | tee "$RAW/container-tail.log" || true
python3 - <<PY
import json, datetime, statistics, math
from pathlib import Path
raw = Path("$RAW")
qs = []
qp = raw / "t5-queue.jsonl"
if qp.exists():
qs = [json.loads(l) for l in qp.read_text().splitlines() if l.strip()]
def pct(xs, p):
if not xs: return None
s=sorted(xs)
k=(len(s)-1)*p/100
f=int(k); c=int(math.ceil(k))
if f==c: return round(s[f],4)
return round(s[f]+(s[c]-s[f])*(k-f),4)
waits=[q.get("queue_wait_s") or 0 for q in qs]
ends=[q.get("end_s") or 0 for q in qs]
meta = {
"ref": "$IMG",
"digest": None if "$DIGEST" in ("<no value>", "") else "$DIGEST",
"id": "$IID",
"arch": "$ARCH",
"pull": "$PULL_HOW",
"probed_at": datetime.datetime.now().astimezone().isoformat(timespec="seconds"),
"oom": str("$OOM").lower()=="true",
"container_status": "$STATUS",
"t5_wall_s": float("$T5_WALL" or 0),
"t5_parallelism": int("$T5_PAR"),
"queue_impl": "client_fifo",
"queue": {
"first_complete_s": round(min(ends),4) if ends else None,
"last_complete_s": round(max(ends),4) if ends else None,
"queue_wait_p50_s": pct(waits,50),
"queue_wait_p95_s": pct(waits,95),
"rejected": 0,
},
"proxy": {
"mixed": "http://127.0.0.1:17890",
"container_via": "http://host.docker.internal:17890",
"generate_204_http": "$PROXY_CODE",
"host_docker_internal": """$HOST_RES""".strip(),
"egress_note": "HTTP(S)_PROXY + extra_hosts host-gateway",
},
"commands": [
"docker compose -p $COMPOSE_PROJ up -d",
"node cdp_fetch.mjs $CDP_HTTP <url>",
"T5 ThreadPoolExecutor workers=$T5_PAR n=60",
],
"notes": ["queue_impl=client_fifo; 生产应是 SQLite WAL + CONCURRENT"],
}
(raw/"image-meta.json").write_text(json.dumps(meta, ensure_ascii=False, indent=2)+"\n")
PY
python3 "$ROOT/summarize.py"
log "=== $SCHEME S3c done ==="