From 0d926d6ac2a0ce370434c42c65028894ffbc6672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Mon, 29 Jun 2026 15:54:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(benchmark):=20=E7=AE=80=E5=8C=96=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E7=BA=AF=E8=80=97=E6=97=B6,=20=E4=BF=AE=E5=A4=8D=20pi?= =?UTF-8?q?pefail/BOA=E6=BA=90/JerryScript=E7=BC=96=E8=AF=91/=E5=BD=95?= =?UTF-8?q?=E5=85=A5=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 输出移除 X.Xx 倍率, 只显示耗时对比 - set -o pipefail 避免 cargo 编译失败被 pipe 吞掉 - BOA 改用 boa/ dev 源码 (兼容 Rust 1.96+) - build.sh 修复 IFS='|' 分隔, 解决多词参数被拆碎 - 加入 pkg-config/cmake/nodejs 依赖, 支持 BOA/JerryScript/V8 - test262 各引擎全部改为录入值, 仅 OxideJS 现场跑 (cherry picked from commit d64c6577e768195687b182a3ccb6ef36d318e905) --- benchmark/build.sh | 34 +++++++------- benchmark/run_benchmark.py | 93 +++++++++----------------------------- 2 files changed, 38 insertions(+), 89 deletions(-) diff --git a/benchmark/build.sh b/benchmark/build.sh index 96703f2..e9e63c7 100644 --- a/benchmark/build.sh +++ b/benchmark/build.sh @@ -1,7 +1,7 @@ #!/bin/bash # OxideJS Benchmark Suite — 一键构建 # 兼容: Debian/Ubuntu, RHEL/Fedora, Arch, Alpine, openSUSE -set -e +set -eo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(dirname "$SCRIPT_DIR")" @@ -13,23 +13,23 @@ step() { echo -e "\n${BOLD}${CYAN}═══ $* ═══${NC}"; } # ── 包管理器检测 ───────────────────────────────────────────────────── detect_pm() { - if command -v apt-get &>/dev/null; then echo "apt" "apt-get install -y" "apt-get update -qq" - elif command -v dnf &>/dev/null; then echo "dnf" "dnf install -y" "dnf check-update 2>/dev/null || true" - elif command -v yum &>/dev/null; then echo "yum" "yum install -y" "yum check-update 2>/dev/null || true" - elif command -v pacman &>/dev/null; then echo "pacman" "pacman -S --noconfirm" "pacman -Sy --noconfirm" - elif command -v apk &>/dev/null; then echo "apk" "apk add --no-cache" "apk update" - elif command -v zypper &>/dev/null; then echo "zypper" "zypper install -y" "zypper refresh" - else echo "unknown" "" "" + if command -v apt-get &>/dev/null; then echo "apt|apt-get install -y|apt-get update -qq" + elif command -v dnf &>/dev/null; then echo "dnf|dnf install -y|dnf check-update 2>/dev/null || true" + elif command -v yum &>/dev/null; then echo "yum|yum install -y|yum check-update 2>/dev/null || true" + elif command -v pacman &>/dev/null; then echo "pacman|pacman -S --noconfirm|pacman -Sy --noconfirm" + elif command -v apk &>/dev/null; then echo "apk|apk add --no-cache|apk update" + elif command -v zypper &>/dev/null; then echo "zypper|zypper install -y|zypper refresh" + else echo "unknown||" fi } map_pkgs() { case "$1" in - apt) echo "gcc make git python3 python3-pip curl pkg-config cmake";; - dnf|yum) echo "gcc make git python3 python3-pip curl pkgconfig cmake";; - pacman) echo "gcc make git python python-pip curl pkgconf cmake";; - apk) echo "gcc make git python3 py3-pip curl pkgconfig cmake";; - zypper) echo "gcc make git python3 python3-pip curl pkg-config cmake";; + apt) echo "gcc make git python3 python3-pip curl pkg-config cmake nodejs";; + dnf|yum) echo "gcc make git python3 python3-pip curl pkgconfig cmake nodejs";; + pacman) echo "gcc make git python python-pip curl pkgconf cmake nodejs";; + apk) echo "gcc make git python3 py3-pip curl pkgconfig cmake nodejs";; + zypper) echo "gcc make git python3 python3-pip curl pkg-config cmake nodejs";; *) echo "gcc make git python3 curl";; esac } @@ -103,10 +103,10 @@ build_quickjs() { # ── BOA ────────────────────────────────────────────────────────────────── build_boa() { - step "BOA (baseline v0.21)" - local boa_dir="$REPO_ROOT/baseline-boa" + step "BOA (dev)" + local boa_dir="$REPO_ROOT/boa" if [ ! -d "$boa_dir" ]; then - warn "baseline-boa/ 不存在,跳过" + warn "boa/ 不存在,跳过" return 0 fi if [ -f "$boa_dir/target/release/boa" ]; then @@ -188,7 +188,7 @@ echo -e "${BOLD}${CYAN}╚══════════════════ step "检测环境" echo " OS: $(uname -a 2>/dev/null | cut -d' ' -f1-3)" echo " CPU: $(nproc 2>/dev/null || echo '?') cores" -read pm icmd ucmd <<< $(detect_pm) +IFS='|' read pm icmd ucmd <<< $(detect_pm) info "包管理器: $pm" install_deps "$pm" "$icmd" "$ucmd" $(map_pkgs "$pm") diff --git a/benchmark/run_benchmark.py b/benchmark/run_benchmark.py index db67833..6c8d4ea 100644 --- a/benchmark/run_benchmark.py +++ b/benchmark/run_benchmark.py @@ -31,18 +31,12 @@ RESULTS_DIR = REPO_ROOT / "benchmark" / "results" class Engine: """A JS engine available for benchmarking.""" - def __init__(self, key, name, exe, run_cmd, *, - test262_exe=None, test262_rate=None, - test262_runner_is_conf=False, test262_conf=None, test262_cwd=None): + def __init__(self, key, name, exe, run_cmd, *, test262_rate=None): self.key = key self.name = name self.exe = Path(exe) if isinstance(exe, str) else exe self._run_cmd = run_cmd # list of str, {exe} and {file} placeholders - self.test262_exe = Path(test262_exe) if test262_exe else None self.test262_rate = test262_rate # pre-recorded pass rate or None - self.test262_runner_is_conf = test262_runner_is_conf - self.test262_conf = test262_conf - self.test262_cwd = test262_cwd @property def available(self): @@ -58,7 +52,7 @@ class Engine: f.write(js); tmp = f.name try: if self.key == "v8": - rc, out, err, t = run(["node", "-p", js], timeout=timeout) + rc, out, err, t = run(["node", tmp], timeout=timeout) else: cmd = [str(self.exe) if p == "{exe}" else (tmp if p == "{file}" else p) for p in self._run_cmd] rc, out, err, t = run(cmd, timeout=timeout) @@ -73,65 +67,35 @@ class Engine: cmd = [str(self.exe) if p == "{exe}" else (str(file_path) if p == "{file}" else p) for p in self._run_cmd] return run(cmd, timeout=timeout) - def run_test262(self, timeout=7200): - """Run test262 suite. Returns (pass, fail, skip, total, elapsed_sec) or None.""" - if self.test262_rate is not None: - return None # pre-recorded, no runner - if not self.test262_exe or not self.test262_exe.exists(): - return None - if self.test262_runner_is_conf: - cwd = self.test262_cwd or self.exe.parent - rc, out, err, _ = run([str(self.test262_exe), "-c", str(self.test262_conf)], - timeout=timeout, cwd=cwd) - else: - rc, out, err, _ = run([str(self.test262_exe), "--supervise", str(TEST262_DIR)], - timeout=timeout) - summary = {"pass": 0, "fail": 0, "skip": 0, "total": 0} - for line in (out + err).split('\n'): - for k in ("pass", "fail", "skip", "total"): - m = re.search(rf'{k}\s*:\s*(\d+)', line, re.IGNORECASE) - if m: summary[k] = max(summary[k], int(m.group(1))) - if summary["total"] == 0: - return None - return summary - - # ── Engine registry ───────────────────────────────────────────────────── -# QuickJS (全平台, make 编译) -QJS_DIR = REPO_ROOT / "baseline-quickjs" +# QuickJS (全平台, make 编译) — test262 98.8% (ES2020 full, minus Annex B) QUICKJS = Engine("quickjs", "QuickJS", - exe=QJS_DIR / "qjs", + exe=REPO_ROOT / "baseline-quickjs" / "qjs", run_cmd=["{exe}", "{file}"], - test262_exe=QJS_DIR / "run-test262", - test262_runner_is_conf=True, - test262_conf="test262.conf", - test262_cwd=QJS_DIR, test262_rate=94.5) -# BOA (全平台, cargo build) -BOA_DIR = REPO_ROOT / "baseline-boa" +# BOA (全平台, cargo build) — test262 ~85% (ES2020) BOA = Engine("boa", "BOA", - exe=BOA_DIR / "target" / "release" / "boa", + exe=REPO_ROOT / "boa" / "target" / "release" / "boa", run_cmd=["{exe}", "--strict", "{file}"], test262_rate=85.0) -# JerryScript (全平台, cmake + make) -JERRY_DIR = REPO_ROOT / "baseline-jerryscript" +# JerryScript (全平台, cmake+make) — test262 ~98% (ES5.1 + partial ES6) JERRY = Engine("jerry", "JerryScript", - exe=JERRY_DIR / "build" / "bin" / "MinSizeRel" / "jerry", + exe=REPO_ROOT / "baseline-jerryscript" / "build" / "bin" / "MinSizeRel" / "jerry", run_cmd=["{exe}", "{file}"], test262_rate=98.0) -# V8 via node (主流平台可选) +# V8 via node (主流平台可选) — test262 ~99.9% V8 = Engine("v8", "V8(node)", - exe=Path("node"), # resolved via shutil_which + exe=Path("node"), run_cmd=["node", "-p"], test262_rate=99.9) -# Hermes (主流平台可选) +# Hermes (主流平台可选) — test262 ~90% HERMES = Engine("hermes", "Hermes", - exe=Path("hermes"), # resolved via shutil_which + exe=Path("hermes"), run_cmd=["{exe}", "{file}"], test262_rate=90.0) @@ -219,7 +183,7 @@ def bench_timing(): if avg and avg != float('inf'): ratio = ox_avg / avg row[f"{eng.key}_ratio"] = round(ratio, 1) - parts.append(f" {eng.name} {avg:.3f}ms {ratio:.1f}x") + parts.append(f" {eng.name} {avg:.3f}ms") else: row[f"{eng.key}_ratio"] = 0 parts.append(f" {eng.name} N/A") @@ -255,7 +219,7 @@ def bench_stress(): if val and val != float('inf'): ratio = ox / val row[f"{eng.key}_ratio"] = round(ratio, 1) - parts.append(f" {eng.name} {val:.1f}ms {ratio:.1f}x") + parts.append(f" {eng.name} {val:.1f}ms") else: row[f"{eng.key}_ratio"] = 0 parts.append(f" {eng.name} N/A") @@ -296,28 +260,11 @@ def bench_test262_oxide(): return summary def bench_test262_baselines(): - """各引擎 test262 成绩汇总 — 运行 runner 或使用已知通过率""" - print("\n" + "=" * 60) - print(" 3b. Test262 — 各引擎") - print("=" * 60) + """各引擎 test262 成绩 — 全部使用已知通过率,不实际跑""" results = {} for eng in ALL_ENGINES: if eng.test262_rate is not None: results[eng.key] = {"rate": eng.test262_rate, "note": f"{eng.name} 录入值"} - print(f" {eng.name:15s} {eng.test262_rate:.1f}% (录入值)") - elif eng.available: - s = eng.run_test262() - if s: - ran = s['pass'] + s['fail'] - rate = s['pass'] / ran * 100 if ran else 0 - results[eng.key] = {"rate": round(rate, 1), "raw": s} - print(f" {eng.name:15s} {rate:.1f}%") - else: - results[eng.key] = {"rate": 0, "note": "runner 不可用"} - print(f" {eng.name:15s} 无法运行") - else: - results[eng.key] = {"rate": 0, "note": "引擎不可用"} - print(f" {eng.name:15s} N/A") return results def parse_jsonl(path): @@ -504,20 +451,22 @@ def report(all_data): timing = all_data.get("timing", []) if timing: w("## Single JS Execution\n") - w("| Scenario | OxideJS | QuickJS | Ratio |") + w("| Scenario | OxideJS | QuickJS ") w("|------|---------|---------|------|") for r in timing: - w(f"| {r['name']} | {r['ox_ms']:.3f}ms | {r['qjs_ms']:.3f}ms | {r['ratio']:.1f}x |") + qjs_v = r.get('quickjs_ms', 0); qjs_r = r.get('quickjs_ratio', 0) + w(f"| {r['name']} | {r['ox_ms']:.3f}ms | {qjs_v:.3f}ms |") w("") # Stress stress = all_data.get("stress", []) if stress: w("## Stress Benchmarks\n") - w("| Test | OxideJS | QuickJS | Ratio |") + w("| Test | OxideJS | QuickJS ") w("|------|---------|---------|------|") for r in stress: - w(f"| {r['test']} | {r['ox_ms']:.1f}ms | {r['qjs_ms']:.1f}ms | {r['ratio']:.1f}x |") + qjs_v = r.get('quickjs_ms', 0); qjs_r = r.get('quickjs_ratio', 0) + w(f"| {r['test']} | {r['ox_ms']:.1f}ms | {qjs_v:.1f}ms |") w("") # Startup