mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
bench: 主判改确定性列零容差并重锚全谱基线
This commit is contained in:
@@ -12,7 +12,10 @@ impl Baseline {
|
||||
}
|
||||
}
|
||||
|
||||
/// 一次性能回退:当前指标相对基线的比值超过容差即视为回归。
|
||||
/// 一次性能回退信号:当前指标相对基线的比值超过容差即报出。
|
||||
///
|
||||
/// `informational` 标记信息性信号(墙时型列):随表格打印供人工参考,
|
||||
/// 不影响退出码;非信息性信号是回归门,出现即退出码 1。
|
||||
pub struct Regression {
|
||||
pub metric: String,
|
||||
pub test_name: String,
|
||||
@@ -20,6 +23,7 @@ pub struct Regression {
|
||||
pub current: f64,
|
||||
pub ratio: f64,
|
||||
pub tolerance: f64,
|
||||
pub informational: bool,
|
||||
}
|
||||
|
||||
/// 从 `benchmark_baseline.json` 加载基线;文件不存在时返回空基线。
|
||||
@@ -47,8 +51,13 @@ pub fn save_baseline(results: &[MetricCollection]) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 对比当前结果与基线,返回所有超过各自容差(wall_time 10%、GC 类 50%、其它 20%)的回归项。
|
||||
/// 方向语义:`ic_hit_rate` 越高越好(下降超容差才报回归),其余指标越低越好(上升超容差报回归)。
|
||||
/// 对比当前结果与基线,返回所有超过各自容差的信号项。
|
||||
///
|
||||
/// 列语义分三档:确定性列(指令数、内存账目、垃圾回收计数、内联缓存计数)
|
||||
/// 跨运行逐字节一致,零容差,双向任何变化即信号;墙时型列(wall、exec、
|
||||
/// compile、gc_collection)容差 0.50,信息性,只打印不进退出码;
|
||||
/// `ic_hit_rate` 越高越好(下降超容差才报),其余列越低越好(上升超容差报)。
|
||||
/// 两侧零值跳过(基线为 0、当前非零的新分配场景由重锚吸收,不在此报)。
|
||||
pub fn compare_baseline(current: &[MetricCollection]) -> Vec<Regression> {
|
||||
let baseline = match load_baseline() {
|
||||
Ok(b) => b,
|
||||
@@ -77,14 +86,20 @@ pub fn compare_baseline(current: &[MetricCollection]) -> Vec<Regression> {
|
||||
if base_val == 0.0 {
|
||||
continue;
|
||||
}
|
||||
let tolerance = match name {
|
||||
"wall_time_us" => 0.10,
|
||||
"gc_trigger_count" | "gc_bytes_freed" | "gc_objects_scanned" | "gc_collection_us" => 0.50,
|
||||
"session_bytes" | "peak_bytes" | "retained_bytes" | "retained_objects" => 0.20,
|
||||
_ => 0.20,
|
||||
// 确定性列零容差(双向任何变化即信号);墙时型列信息性;
|
||||
// ic_hit_rate 越高越好;其余列保留 20% 默认带。
|
||||
let (tolerance, informational) = match name {
|
||||
"instruction_count" | "session_objects" | "session_bytes" | "gc_trigger_count"
|
||||
| "gc_bytes_freed" | "gc_objects_scanned" | "ic_hits" | "ic_misses" | "peak_bytes"
|
||||
| "retained_bytes" | "retained_objects" => (0.0, false),
|
||||
"wall_time_us" | "exec_time_us" | "compile_time_us" | "gc_collection_us" => (0.50, true),
|
||||
"ic_hit_rate" => (0.05, false),
|
||||
_ => (0.20, false),
|
||||
};
|
||||
let ratio = cur_val / base_val;
|
||||
let regressed = if matches!(name, "ic_hit_rate") {
|
||||
let regressed = if tolerance == 0.0 {
|
||||
(ratio - 1.0).abs() > 1e-9
|
||||
} else if matches!(name, "ic_hit_rate") {
|
||||
ratio < 1.0 - tolerance
|
||||
} else {
|
||||
ratio > 1.0 + tolerance
|
||||
@@ -97,6 +112,7 @@ pub fn compare_baseline(current: &[MetricCollection]) -> Vec<Regression> {
|
||||
current: cur_val,
|
||||
ratio,
|
||||
tolerance,
|
||||
informational,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,9 +155,27 @@ pub fn run_js_stress_bench(config: &BenchConfig, kernel: &Arc<KernelCore>, pool:
|
||||
eprintln!("Baseline saved to BENCHMARK_BASELINE.md + benchmark_baseline.json");
|
||||
} else {
|
||||
let regressions = compare_baseline(&results);
|
||||
if !regressions.is_empty() {
|
||||
// 信息性旗标(墙时型列)只打印供人工参考,不影响退出码。
|
||||
let informational: Vec<_> = regressions.iter().filter(|r| r.informational).collect();
|
||||
if !informational.is_empty() {
|
||||
eprintln!("\nWall-time drift (informational, does not affect exit code):");
|
||||
for r in &informational {
|
||||
eprintln!(
|
||||
" {}: {} baseline={} current={} ratio={:.2}% tolerance={:.0}%",
|
||||
r.test_name,
|
||||
r.metric,
|
||||
r.baseline,
|
||||
r.current,
|
||||
(r.ratio - 1.0) * 100.0,
|
||||
r.tolerance * 100.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
// 回归门:确定性列等硬信号出现即退出码 1。
|
||||
let hard: Vec<_> = regressions.iter().filter(|r| !r.informational).collect();
|
||||
if !hard.is_empty() {
|
||||
eprintln!("\nRegression detected:");
|
||||
for r in ®ressions {
|
||||
for r in &hard {
|
||||
eprintln!(
|
||||
" {}: {} baseline={} current={} ratio={:.2}% tolerance={:.0}%",
|
||||
r.test_name,
|
||||
|
||||
@@ -23,6 +23,17 @@ const MEMORY_BENCH_NOTES: &str = "\n内存基准用例(mem_*):\n
|
||||
再克隆一份(object_graph 见留存锚测试)。
|
||||
";
|
||||
|
||||
/// A/B 判据口径说明,随表格尾部输出。
|
||||
const BENCH_JUDGMENT_NOTES: &str = "\nA/B 判据口径:\n
|
||||
主判(回归门):确定性列——指令数、内存账目(session/peak/retained)、
|
||||
垃圾回收计数、内联缓存计数——跨运行逐字节一致,零容差,双向任何变化即信号;
|
||||
基线对比的退出码 1 只认这些列。
|
||||
次判(墙时间):wall/exec/compile/gc_collection 列在 ±5% 带内(固定核、
|
||||
warmup 2、三轮中位);基线对比容差 0.50,信息性,不影响退出码。
|
||||
基线文件是陈旧锚(安静树单次快照),不是 A/B 仪器;A/B 是同机同宿主
|
||||
严格交错对比。
|
||||
";
|
||||
|
||||
/// 把结果格式化为对齐的文本表格(供终端打印)。
|
||||
///
|
||||
/// `Sess B`/`Peak B`/`Ret B` 为 session 堆账目(字节):执行期累计 / 峰值高水位 /
|
||||
@@ -64,6 +75,7 @@ pub fn format_text_table(results: &[MetricCollection]) -> String {
|
||||
out.push('\n');
|
||||
}
|
||||
out.push_str(MEMORY_BENCH_NOTES);
|
||||
out.push_str(BENCH_JUDGMENT_NOTES);
|
||||
out
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user