feat: 新增进程级启动五段分解度量用例

This commit is contained in:
2026-09-28 18:31:29 +00:00
parent cf298ab3d1
commit 31355ff6b6
2 changed files with 96 additions and 0 deletions
+95
View File
@@ -741,6 +741,101 @@ pub fn run_mem_kernel_lifetime() -> ExitCode {
ExitCode::SUCCESS
}
/// 进程级启动五段分解度量:专用 kernel(`KernelConfig::minimal()`,同
/// `kernel_lifetime` 口径,不污染 bench 共享 kernel),每轮重建 kernel 与池
/// 模拟新进程,连测五段——① `KernelCore::new`(kernel 初始化)②
/// `VmPool::new`(池构造)③ 首次 `pool.spawn()`(首个 Vm 构造)④ 固定微源
/// `oxide_parser::parse` 加 `Compiler::new().compile`(parse 加 compile)⑤
/// 首次 `vm.run`(执行),打印各段毫秒加合计,三轮取中位。
///
/// # 边界与前提
/// - 启动是单次事件非序列,不进 `report_series` 泄漏判据;
/// - “首个 Vm 构造”段可与 `builtin_world_build` 用例的每 Vm 构造成本锚对照
/// (同 debug 口径);偏差大先查 harness 污染再疑用例。
///
/// # 副作用
/// - 不改引擎执行路径;每轮 kernel 与池在轮末 drop,无跨轮状态。
pub fn run_mem_startup_breakdown() -> ExitCode {
const ROUNDS: usize = 3;
const SOURCE: &str = "var x = 1 + 2; x * 3";
let mut rounds: Vec<[f64; 5]> = Vec::with_capacity(ROUNDS);
for round in 0..ROUNDS {
// kernel 初始化:每轮全新构造,模拟进程启动。
let t0 = Instant::now();
let config = KernelConfig::minimal();
let kernel = KernelCore::new(config.clone());
let t1 = Instant::now();
// 池构造:空池,min_size 参数当前仅作预留不预热。
let pool = VmPool::new(Arc::clone(&kernel), config.min_pool_size, config.max_pool_size);
let t2 = Instant::now();
// 首个 Vm 构造:池空,spawn 走 grow 分支全量构造。
let mut guard = pool.spawn();
let t3 = Instant::now();
// parse 加 compile:固定微源每轮重测。
let allocator = Allocator::default();
let program = match oxide_parser::parse(&allocator, SOURCE) {
Ok(p) => p,
Err(e) => {
eprintln!("[startup_breakdown] round {round} parse failed: {e:?}");
return ExitCode::FAILURE;
}
};
let module = match Compiler::new().compile(&program) {
Ok(m) => Arc::new(m),
Err(e) => {
eprintln!("[startup_breakdown] round {round} compile failed: {e}");
return ExitCode::FAILURE;
}
};
let t4 = Instant::now();
// 首次执行:微源结果必须为 9。
let result = match guard.vm_mut().run(&module) {
Ok(v) => v,
Err(e) => {
eprintln!("[startup_breakdown] round {round} run failed: {e}");
return ExitCode::FAILURE;
}
};
let t5 = Instant::now();
if result != JsValue::float(9.0) {
eprintln!("[startup_breakdown] round {round} result mismatch: {result:?}");
return ExitCode::FAILURE;
}
rounds.push([
t1.duration_since(t0).as_secs_f64() * 1e3,
t2.duration_since(t1).as_secs_f64() * 1e3,
t3.duration_since(t2).as_secs_f64() * 1e3,
t4.duration_since(t3).as_secs_f64() * 1e3,
t5.duration_since(t4).as_secs_f64() * 1e3,
]);
}
// 逐轮明细 + 三轮中位:中位抗单轮噪声,明细供查 harness 污染。
let mut medians = [0.0f64; 5];
for s in 0..5 {
let mut values = [rounds[0][s], rounds[1][s], rounds[2][s]];
values.sort_by(f64::total_cmp);
medians[s] = values[1];
}
for (round, segs) in rounds.iter().enumerate() {
eprintln!(
"[startup_breakdown] round {round}: kernel_init={:.3} pool_new={:.3} first_vm={:.3} parse_compile={:.3} first_run={:.3} total={:.3} ms",
segs[0], segs[1], segs[2], segs[3], segs[4], segs.iter().sum::<f64>()
);
}
eprintln!(
"[startup_breakdown] median: kernel_init={:.3} pool_new={:.3} first_vm={:.3} parse_compile={:.3} first_run={:.3} total={:.3} ms",
medians[0], medians[1], medians[2], medians[3], medians[4], medians.iter().sum::<f64>()
);
ExitCode::SUCCESS
}
/// 全序列回归报告:打印首末 RSS、全序列斜率/R² 与窗口(末 20 样本)总
/// 增量;斜率显著为正(R² > 0.9)返回失败,表示泄漏签名。
fn report_series(case: &str, unit: &str, series: &[(usize, f64)]) -> ExitCode {
+1
View File
@@ -44,6 +44,7 @@ pub fn run_benchmarks(config: BenchConfig, kernel: Arc<KernelCore>, pool: Arc<Vm
Some("pool_high_water") => leak_detect::run_mem_pool_high_water(&kernel),
Some("object_churn_peak") => leak_detect::run_mem_object_churn_peak(&kernel),
Some("kernel_lifetime") => leak_detect::run_mem_kernel_lifetime(),
Some("startup_breakdown") => leak_detect::run_mem_startup_breakdown(),
_ => leak_detect::run_leak_detect(&config, &kernel, &pool),
},
_ => {