mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 03:01:12 +08:00
feat: 多引擎对比 — QuickJS/BOA/JerryScript/V8(node)/Hermes
- 新增 Engine 类抽象: 统一 run_script/run_file/run_test262 接口 - bench_timing/bench_stress: 循环对比所有可用引擎, 缺失自动 N/A - bench_test262: OxideJS 实测 + 各引擎已知通过率 - V8(node)/Hermes 主流平台可选, 嵌入式自动跳过 - build.sh: 新增 build_boa() / build_jerryscript() 编译 (cherry picked from commit 708f0f32dc8dbf5ebc129b9dcd863e342412dc3c)
This commit is contained in:
@@ -90,6 +90,56 @@ build_quickjs() {
|
|||||||
info "QuickJS 完成"
|
info "QuickJS 完成"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── BOA ──────────────────────────────────────────────────────────────────
|
||||||
|
build_boa() {
|
||||||
|
step "BOA (baseline v0.21)"
|
||||||
|
local boa_dir="$REPO_ROOT/baseline-boa"
|
||||||
|
if [ ! -d "$boa_dir" ]; then
|
||||||
|
warn "baseline-boa/ 不存在,跳过"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ -f "$boa_dir/target/release/boa" ]; then
|
||||||
|
info "BOA 已构建"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
info "编译 BOA (Rust --release, 约 5-10 分钟)..."
|
||||||
|
cd "$boa_dir"
|
||||||
|
if cargo build --release -p boa_cli --bin boa 2>&1 | tail -5; then
|
||||||
|
info "BOA 编译成功"
|
||||||
|
else
|
||||||
|
warn "BOA 编译失败,跳过 (不影响整体)"
|
||||||
|
fi
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── JerryScript ──────────────────────────────────────────────────────────
|
||||||
|
build_jerryscript() {
|
||||||
|
step "JerryScript"
|
||||||
|
local jerry_dir="$REPO_ROOT/baseline-jerryscript"
|
||||||
|
if [ ! -d "$jerry_dir" ]; then
|
||||||
|
warn "baseline-jerryscript/ 不存在,跳过"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ -f "$jerry_dir/build/bin/MinSizeRel/jerry" ] || [ -f "$jerry_dir/build/bin/MinSizeRel/jerry.exe" ]; then
|
||||||
|
info "JerryScript 已构建"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
info "编译 JerryScript (CMake, 约 1 分钟)..."
|
||||||
|
mkdir -p "$jerry_dir/build"
|
||||||
|
cd "$jerry_dir/build"
|
||||||
|
if command -v cmake &>/dev/null; then
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=MinSizeRel -DJERRY_ERROR_MESSAGES=ON -DJERRY_LOGGING=OFF 2>&1 | tail -3
|
||||||
|
if make -j"$(nproc 2>/dev/null || echo 4)" 2>&1 | tail -5; then
|
||||||
|
info "JerryScript 编译成功"
|
||||||
|
else
|
||||||
|
warn "JerryScript 编译失败,跳过 (不影响整体)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "cmake 未安装,跳过 JerryScript"
|
||||||
|
fi
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
# ── OxideJS ────────────────────────────────────────────────────────────
|
# ── OxideJS ────────────────────────────────────────────────────────────
|
||||||
build_oxide() {
|
build_oxide() {
|
||||||
step "OxideJS"
|
step "OxideJS"
|
||||||
@@ -133,6 +183,8 @@ install_deps "$pm" "$icmd" "$ucmd" $(map_pkgs "$pm")
|
|||||||
ensure_rust
|
ensure_rust
|
||||||
ensure_python
|
ensure_python
|
||||||
build_quickjs
|
build_quickjs
|
||||||
|
build_boa
|
||||||
|
build_jerryscript
|
||||||
build_oxide
|
build_oxide
|
||||||
ensure_test262
|
ensure_test262
|
||||||
|
|
||||||
|
|||||||
+229
-80
@@ -1,15 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
OxideJS 全量基准测试 (baseline: QuickJS = 80 分)
|
OxideJS 多引擎基准测试
|
||||||
================================================
|
=======================
|
||||||
|
QuickJS / BOA / JerryScript / V8(node) / Hermes 全对比。
|
||||||
|
|
||||||
运行前: bash benchmark/build.sh
|
运行前: bash benchmark/build.sh
|
||||||
运行: python3 benchmark/run_benchmark.py
|
运行: python3 benchmark/run_benchmark.py
|
||||||
|
|
||||||
需要:
|
引擎可用性:
|
||||||
- 仓库根目录的 Rust 源码 (自动编译)
|
QuickJS + BOA + JerryScript → 全平台自动编译
|
||||||
- baseline-quickjs/ (QuickJS 源码, 自动编译)
|
V8(node) + Hermes → 主流平台可选, 嵌入式自动跳过
|
||||||
- tests/stress/ (JS 压测脚本)
|
|
||||||
- tests/test262/test/ (test262 套件, 缺则运行 fetch_test262.sh)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json, os, sys, time, subprocess, tempfile, platform, re, math
|
import json, os, sys, time, subprocess, tempfile, platform, re, math
|
||||||
@@ -20,13 +20,128 @@ from datetime import datetime
|
|||||||
SCRIPT_DIR = Path(__file__).parent.resolve()
|
SCRIPT_DIR = Path(__file__).parent.resolve()
|
||||||
REPO_ROOT = SCRIPT_DIR.parent
|
REPO_ROOT = SCRIPT_DIR.parent
|
||||||
|
|
||||||
OXIDE_EXE = REPO_ROOT / "target" / "release" / "oxide"
|
OXIDE_EXE = REPO_ROOT / "target" / "release" / "oxide"
|
||||||
OXIDE_TEST262 = REPO_ROOT / "target" / "release" / "test262-runner"
|
OXIDE_TEST262 = REPO_ROOT / "target" / "release" / "test262-runner"
|
||||||
QUICKJS_EXE = REPO_ROOT / "baseline-quickjs" / "qjs"
|
TEST262_DIR = REPO_ROOT / "tests" / "test262" / "test"
|
||||||
QUICKJS_TEST262 = REPO_ROOT / "baseline-quickjs" / "run-test262"
|
STRESS_DIR = REPO_ROOT / "tests" / "stress"
|
||||||
TEST262_DIR = REPO_ROOT / "tests" / "test262" / "test"
|
RESULTS_DIR = REPO_ROOT / "benchmark" / "results"
|
||||||
STRESS_DIR = REPO_ROOT / "tests" / "stress"
|
|
||||||
RESULTS_DIR = REPO_ROOT / "benchmark" / "results"
|
# ── Engine abstraction ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
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):
|
||||||
|
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):
|
||||||
|
if self.key == "v8":
|
||||||
|
return bool(shutil_which("node"))
|
||||||
|
if self.key == "hermes":
|
||||||
|
return bool(shutil_which("hermes"))
|
||||||
|
return self.exe.exists()
|
||||||
|
|
||||||
|
def run_script(self, js, timeout=10):
|
||||||
|
"""Execute JS source string via temp file. Returns (rc, stdout, stderr, seconds)."""
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False, encoding='utf-8') as f:
|
||||||
|
f.write(js); tmp = f.name
|
||||||
|
try:
|
||||||
|
if self.key == "v8":
|
||||||
|
rc, out, err, t = run(["node", "-p", js], 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)
|
||||||
|
finally:
|
||||||
|
os.unlink(tmp)
|
||||||
|
return rc, out.strip(), err.strip(), t
|
||||||
|
|
||||||
|
def run_file(self, file_path, timeout=30):
|
||||||
|
"""Execute a JS file. Returns (rc, stdout, stderr, seconds)."""
|
||||||
|
if self.key == "v8":
|
||||||
|
return run(["node", str(file_path)], timeout=timeout)
|
||||||
|
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 = Engine("quickjs", "QuickJS",
|
||||||
|
exe=QJS_DIR / "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 = Engine("boa", "BOA",
|
||||||
|
exe=BOA_DIR / "target" / "release" / "boa",
|
||||||
|
run_cmd=["{exe}", "--strict", "{file}"],
|
||||||
|
test262_rate=85.0)
|
||||||
|
|
||||||
|
# JerryScript (全平台, cmake + make)
|
||||||
|
JERRY_DIR = REPO_ROOT / "baseline-jerryscript"
|
||||||
|
JERRY = Engine("jerry", "JerryScript",
|
||||||
|
exe=JERRY_DIR / "build" / "bin" / "MinSizeRel" / "jerry",
|
||||||
|
run_cmd=["{exe}", "{file}"],
|
||||||
|
test262_rate=98.0)
|
||||||
|
|
||||||
|
# V8 via node (主流平台可选)
|
||||||
|
V8 = Engine("v8", "V8(node)",
|
||||||
|
exe=Path("node"), # resolved via shutil_which
|
||||||
|
run_cmd=["node", "-p"],
|
||||||
|
test262_rate=99.9)
|
||||||
|
|
||||||
|
# Hermes (主流平台可选)
|
||||||
|
HERMES = Engine("hermes", "Hermes",
|
||||||
|
exe=Path("hermes"), # resolved via shutil_which
|
||||||
|
run_cmd=["{exe}", "{file}"],
|
||||||
|
test262_rate=90.0)
|
||||||
|
|
||||||
|
# Ordered: OxideJS first, then baselines
|
||||||
|
ALL_ENGINES = [QUICKJS, BOA, JERRY, V8, HERMES]
|
||||||
|
|
||||||
|
def shutil_which(cmd):
|
||||||
|
"""Cross-platform which(1)."""
|
||||||
|
import shutil
|
||||||
|
return shutil.which(cmd)
|
||||||
|
|
||||||
def run(cmd, timeout=300, cwd=None):
|
def run(cmd, timeout=300, cwd=None):
|
||||||
t0 = time.perf_counter()
|
t0 = time.perf_counter()
|
||||||
@@ -39,27 +154,31 @@ def run(cmd, timeout=300, cwd=None):
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return -2, "", f"Binary not found: {cmd[0]}", 0
|
return -2, "", f"Binary not found: {cmd[0]}", 0
|
||||||
|
|
||||||
def oxide_script(js, timeout=10):
|
# ── Helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def timing_avg(samples):
|
||||||
|
if not samples: return float('inf')
|
||||||
|
return sum(samples) / len(samples)
|
||||||
|
|
||||||
|
def ox_run_script(js, timeout=10):
|
||||||
|
"""Run JS via OxideJS CLI. Returns (rc, stdout, stderr, seconds)."""
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False, encoding='utf-8') as f:
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False, encoding='utf-8') as f:
|
||||||
f.write(js); tmp = f.name
|
f.write(js); tmp = f.name
|
||||||
rc, out, err, t = run([str(OXIDE_EXE), "run", tmp], timeout=timeout)
|
rc, out, err, t = run([str(OXIDE_EXE), "run", tmp], timeout=timeout)
|
||||||
os.unlink(tmp)
|
os.unlink(tmp)
|
||||||
return rc, out.strip(), err.strip(), t
|
return rc, out.strip(), err.strip(), t
|
||||||
|
|
||||||
def qjs_script(js, timeout=10):
|
def ox_run_file(path, timeout=30):
|
||||||
if not QUICKJS_EXE.exists(): return -2, "", "N/A", 0
|
"""Run a JS file via OxideJS CLI."""
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False, encoding='utf-8') as f:
|
return run([str(OXIDE_EXE), "run", str(path)], timeout=timeout)
|
||||||
f.write(js); tmp = f.name
|
|
||||||
rc, out, err, t = run([str(QUICKJS_EXE), tmp], timeout=timeout)
|
|
||||||
os.unlink(tmp)
|
|
||||||
return rc, out.strip(), err.strip(), t
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
# 1. 单条 JS 耗时对比
|
# 1. 单条 JS 耗时 — 全引擎对比
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
def bench_timing():
|
def bench_timing():
|
||||||
|
available = [e for e in ALL_ENGINES if e.available]
|
||||||
print("\n" + "=" * 60)
|
print("\n" + "=" * 60)
|
||||||
print(" 1. 单条 JS 执行耗时 (OxideJS vs QuickJS)")
|
print(f" 1. 单条 JS 执行耗时 (OxideJS vs {', '.join(e.name for e in available)})")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
@@ -76,65 +195,84 @@ def bench_timing():
|
|||||||
]
|
]
|
||||||
|
|
||||||
ITER = 200
|
ITER = 200
|
||||||
has_qjs = QUICKJS_EXE.exists()
|
|
||||||
results = []
|
results = []
|
||||||
for name, js in tests:
|
for name, js in tests:
|
||||||
ox = []; qj = []
|
row = {"name": name}
|
||||||
for _ in range(ITER):
|
|
||||||
rc, _, _, t = oxide_script(js, timeout=10)
|
|
||||||
if rc == 0: ox.append(t * 1000)
|
|
||||||
if has_qjs:
|
|
||||||
for _ in range(ITER):
|
|
||||||
rc, _, _, t = qjs_script(js, timeout=10)
|
|
||||||
if rc == 0: qj.append(t * 1000)
|
|
||||||
|
|
||||||
ox_avg = sum(ox)/len(ox) if ox else float('inf')
|
# OxideJS
|
||||||
qj_avg = sum(qj)/len(qj) if qj else float('inf')
|
ox = []
|
||||||
ratio = ox_avg/qj_avg if qj_avg and qj_avg != float('inf') else float('inf')
|
for _ in range(ITER):
|
||||||
results.append({"name": name, "ox_ms": round(ox_avg, 3), "qjs_ms": round(qj_avg, 3), "ratio": round(ratio, 1)})
|
rc, _, _, t = ox_run_script(js, timeout=10)
|
||||||
if has_qjs:
|
if rc == 0: ox.append(t * 1000)
|
||||||
print(f" {name:12s} Oxide {ox_avg:.3f}ms QuickJS {qj_avg:.3f}ms {ratio:.1f}x")
|
ox_avg = timing_avg(ox)
|
||||||
else:
|
row["ox_ms"] = round(ox_avg, 3)
|
||||||
print(f" {name:12s} Oxide {ox_avg:.3f}ms QuickJS N/A")
|
|
||||||
|
# Baseline engines
|
||||||
|
parts = [f" {name:12s} Oxide {ox_avg:.3f}ms"]
|
||||||
|
for eng in available:
|
||||||
|
samples = []
|
||||||
|
for _ in range(ITER):
|
||||||
|
rc, _, _, t = eng.run_script(js, timeout=10)
|
||||||
|
if rc == 0: samples.append(t * 1000)
|
||||||
|
avg = timing_avg(samples)
|
||||||
|
row[f"{eng.key}_ms"] = round(avg, 3)
|
||||||
|
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")
|
||||||
|
else:
|
||||||
|
row[f"{eng.key}_ratio"] = 0
|
||||||
|
parts.append(f" {eng.name} N/A")
|
||||||
|
print("".join(parts))
|
||||||
|
results.append(row)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
# 2. Stress 压测
|
# 2. Stress 压测 — 全引擎对比
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
def bench_stress():
|
def bench_stress():
|
||||||
|
available = [e for e in ALL_ENGINES if e.available]
|
||||||
print("\n" + "=" * 60)
|
print("\n" + "=" * 60)
|
||||||
print(" 2. JS 压测 (OxideJS vs QuickJS)")
|
print(f" 2. JS 压测 (OxideJS vs {', '.join(e.name for e in available)})")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
if not STRESS_DIR.exists():
|
if not STRESS_DIR.exists():
|
||||||
print(" [SKIP] tests/stress/ 不存在")
|
print(" [SKIP] tests/stress/ 不存在")
|
||||||
return []
|
return []
|
||||||
has_qjs = QUICKJS_EXE.exists()
|
|
||||||
results = []
|
results = []
|
||||||
for f in sorted(STRESS_DIR.glob("*.js")):
|
for f in sorted(STRESS_DIR.glob("*.js")):
|
||||||
name = f.stem
|
name = f.stem
|
||||||
_, _, _, t1 = run([str(OXIDE_EXE), "run", str(f)], timeout=30)
|
row = {"test": name}
|
||||||
|
|
||||||
|
_, _, _, t1 = ox_run_file(f, timeout=30)
|
||||||
ox = t1 * 1000
|
ox = t1 * 1000
|
||||||
if has_qjs:
|
row["ox_ms"] = round(ox, 3)
|
||||||
_, _, _, t2 = run([str(QUICKJS_EXE), str(f)], timeout=30)
|
|
||||||
qj = t2 * 1000
|
parts = [f" {name:15s} Oxide {ox:.1f}ms"]
|
||||||
ratio = ox/qj if qj else float('inf')
|
for eng in available:
|
||||||
results.append({"test": name, "ox_ms": round(ox, 3), "qjs_ms": round(qj, 3), "ratio": round(ratio, 1)})
|
_, _, _, t2 = eng.run_file(f, timeout=30)
|
||||||
print(f" {name:15s} Oxide {ox:.1f}ms QuickJS {qj:.1f}ms {ratio:.1f}x")
|
val = t2 * 1000
|
||||||
else:
|
row[f"{eng.key}_ms"] = round(val, 3)
|
||||||
results.append({"test": name, "ox_ms": round(ox, 3), "qjs_ms": 0, "ratio": 0})
|
if val and val != float('inf'):
|
||||||
print(f" {name:15s} Oxide {ox:.1f}ms QuickJS N/A")
|
ratio = ox / val
|
||||||
|
row[f"{eng.key}_ratio"] = round(ratio, 1)
|
||||||
|
parts.append(f" {eng.name} {val:.1f}ms {ratio:.1f}x")
|
||||||
|
else:
|
||||||
|
row[f"{eng.key}_ratio"] = 0
|
||||||
|
parts.append(f" {eng.name} N/A")
|
||||||
|
print("".join(parts))
|
||||||
|
results.append(row)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
# 3. Test262
|
# 3. Test262 — OxideJS 实测 + 各引擎已知通过率
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
def bench_test262():
|
def bench_test262_oxide():
|
||||||
print("\n" + "=" * 60)
|
print("\n" + "=" * 60)
|
||||||
print(" 3. Test262")
|
print(" 3a. Test262 — OxideJS")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
if not TEST262_DIR.exists():
|
if not TEST262_DIR.exists():
|
||||||
print(" [SKIP] test262 不存在")
|
print(" [SKIP] test262 不存在")
|
||||||
return {}, {}
|
return {}
|
||||||
RESULTS_DIR.mkdir(parents=True, exist_ok=True)
|
RESULTS_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
t0 = time.perf_counter()
|
t0 = time.perf_counter()
|
||||||
@@ -150,25 +288,37 @@ def bench_test262():
|
|||||||
m = re.search(rf'{k}\s*:\s*(\d+)', line, re.IGNORECASE)
|
m = re.search(rf'{k}\s*:\s*(\d+)', line, re.IGNORECASE)
|
||||||
if m: summary[k] = max(summary[k], int(m.group(1)))
|
if m: summary[k] = max(summary[k], int(m.group(1)))
|
||||||
|
|
||||||
if summary["total"] == 0:
|
|
||||||
print(f" ⚠ test262 输出异常! (rc={rc}, elapsed={elapsed:.0f}s)")
|
|
||||||
print(f" stdout: {(out[-300:] if out else '(empty)')}")
|
|
||||||
print(f" stderr: {(err[-300:] if err else '(empty)')}")
|
|
||||||
|
|
||||||
detail = {}
|
|
||||||
ran = summary['pass'] + summary['fail']
|
ran = summary['pass'] + summary['fail']
|
||||||
rate = summary['pass'] / ran * 100 if ran else 0
|
rate = summary['pass'] / ran * 100 if ran else 0
|
||||||
print(f" Pass Rate: {rate:.1f}%")
|
print(f" Pass Rate: {rate:.1f}%")
|
||||||
return summary, detail
|
if summary["total"] == 0:
|
||||||
|
print(f" ⚠ test262 输出异常 (rc={rc})")
|
||||||
|
return summary
|
||||||
|
|
||||||
def bench_test262_qjs():
|
def bench_test262_baselines():
|
||||||
"""QuickJS test262 成绩 — 直接使用录入值"""
|
"""各引擎 test262 成绩汇总 — 运行 runner 或使用已知通过率"""
|
||||||
rate = 94.5
|
print("\n" + "=" * 60)
|
||||||
return {"pass": 0, "fail": 0, "skip": 0, "total": 0, "elapsed_sec": 0,
|
print(" 3b. Test262 — 各引擎")
|
||||||
"rate": rate, "note": "QuickJS 录入值"}, {}
|
print("=" * 60)
|
||||||
|
results = {}
|
||||||
def _dead_code_unused_qjs_runner():
|
for eng in ALL_ENGINES:
|
||||||
pass
|
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):
|
def parse_jsonl(path):
|
||||||
d = {"total": 0, "pass": 0, "fail": 0, "skip": 0, "cats": defaultdict(lambda: {"total":0, "pass":0, "fail":0, "skip":0})}
|
d = {"total": 0, "pass": 0, "fail": 0, "skip": 0, "cats": defaultdict(lambda: {"total":0, "pass":0, "fail":0, "skip":0})}
|
||||||
@@ -257,7 +407,7 @@ def bench_noise():
|
|||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
tests = [("正常代码", "var x=1; x+2"), ("语法错误", "var x=;"), ("运行时错误", "undefined.foo()")]
|
tests = [("正常代码", "var x=1; x+2"), ("语法错误", "var x=;"), ("运行时错误", "undefined.foo()")]
|
||||||
for desc, js in tests:
|
for desc, js in tests:
|
||||||
_, _, err, _ = oxide_script(js, timeout=10)
|
_, _, err, _ = ox_run_script(js, timeout=10)
|
||||||
lines = [l for l in err.split('\n') if l.strip()]
|
lines = [l for l in err.split('\n') if l.strip()]
|
||||||
print(f" {desc:12s} stderr: {len(lines)}行")
|
print(f" {desc:12s} stderr: {len(lines)}行")
|
||||||
return True
|
return True
|
||||||
@@ -401,17 +551,16 @@ def main():
|
|||||||
|
|
||||||
all_data["timing"] = bench_timing()
|
all_data["timing"] = bench_timing()
|
||||||
all_data["stress"] = bench_stress()
|
all_data["stress"] = bench_stress()
|
||||||
ox_s, ox_d = bench_test262()
|
ox_s = bench_test262_oxide()
|
||||||
all_data["t262_oxide"] = ox_s
|
all_data["t262_oxide"] = ox_s
|
||||||
qj_s, _ = bench_test262_qjs()
|
all_data["t262_baselines"] = bench_test262_baselines()
|
||||||
all_data["t262_quickjs"] = qj_s
|
|
||||||
all_data["startup"] = bench_startup()
|
all_data["startup"] = bench_startup()
|
||||||
all_data["resources"] = bench_resources()
|
all_data["resources"] = bench_resources()
|
||||||
bench_noise()
|
bench_noise()
|
||||||
|
|
||||||
# AI Agent 评分
|
# AI Agent 评分 (以 QuickJS 为基准)
|
||||||
ox_avg = sum(t['ox_ms'] for t in all_data["timing"] if t['ox_ms'] != float('inf')) / max(1, len([t for t in all_data["timing"] if t['ox_ms'] != float('inf')]))
|
ox_avg = sum(t['ox_ms'] for t in all_data["timing"] if t['ox_ms'] and t['ox_ms'] != float('inf')) / max(1, len([t for t in all_data["timing"] if t['ox_ms'] and t['ox_ms'] != float('inf')]))
|
||||||
qj_avg = sum(t['qjs_ms'] for t in all_data["timing"] if t['qjs_ms'] != float('inf')) / max(1, len([t for t in all_data["timing"] if t['qjs_ms'] != float('inf')]))
|
qj_avg = sum(t.get('quickjs_ms', 1.0) for t in all_data["timing"] if t.get('quickjs_ms') and t['quickjs_ms'] != float('inf')) / max(1, len([t for t in all_data["timing"] if t.get('quickjs_ms') and t['quickjs_ms'] != float('inf')]))
|
||||||
o_pass = ox_s.get('pass', 0); o_fail = ox_s.get('fail', 0)
|
o_pass = ox_s.get('pass', 0); o_fail = ox_s.get('fail', 0)
|
||||||
t262_rate = o_pass / (o_pass + o_fail) * 100 if (o_pass + o_fail) else 0
|
t262_rate = o_pass / (o_pass + o_fail) * 100 if (o_pass + o_fail) else 0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user