From c71f8bd54e398378a7733d87074acfe03b71ab27 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 11 Sep 2026 20:20:00 +0000 Subject: [PATCH] =?UTF-8?q?perf:=20=E5=A2=9E=20kernel=5Flifetime=20?= =?UTF-8?q?=E6=A0=A1=E5=87=86=E7=94=A8=E4=BE=8B=E2=80=94=E2=80=94=E4=B8=93?= =?UTF-8?q?=E7=94=A8=20kernel=2020=20=E4=B8=87=E5=94=AF=E4=B8=80=E9=94=AE?= =?UTF-8?q?=20intern=EF=BC=881/4=20=E7=89=A9=E5=8C=96=EF=BC=89=E5=A2=9E?= =?UTF-8?q?=E9=95=BF=E6=96=9C=E7=8E=87=E9=A6=96=E6=B5=8B=E5=9F=BA=E7=BA=BF?= =?UTF-8?q?=EF=BC=88=E4=BB=85=E8=AE=B0=E5=BD=95=E4=B8=8D=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=EF=BC=89+=20=E5=AF=BF=E5=91=BD=E6=97=8B=E9=92=AE=E7=AB=AF?= =?UTF-8?q?=E5=88=B0=E7=AB=AF=20+=20=E9=87=8D=E5=BB=BA=E5=90=8E=20entry=5F?= =?UTF-8?q?count=20200000=E2=86=920=20=E4=B8=A5=E6=A0=BC=E4=B8=BB=E9=94=9A?= =?UTF-8?q?=EF=BC=8Cexit=20=E4=BB=85=E7=94=B1=E9=87=8D=E5=BB=BA=E7=9B=B8?= =?UTF-8?q?=E7=A1=AE=E5=AE=9A=E6=80=A7=E6=96=AD=E8=A8=80=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_cli/src/bench/leak_detect.rs | 82 ++++++++++++++++++++++- crates/oxide_cli/src/bench/mod.rs | 1 + 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/crates/oxide_cli/src/bench/leak_detect.rs b/crates/oxide_cli/src/bench/leak_detect.rs index d339435f..45311f02 100644 --- a/crates/oxide_cli/src/bench/leak_detect.rs +++ b/crates/oxide_cli/src/bench/leak_detect.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use std::time::Instant; use oxide_compiler::compiler::{compiled_module_hash, Compiler}; -use oxide_kernel::kernel::KernelCore; +use oxide_kernel::kernel::{KernelConfig, KernelCore}; use oxide_parser::Allocator; use oxide_types::value::JsValue; use oxide_vm::vm::Vm; @@ -521,6 +521,86 @@ pub fn run_mem_object_churn_peak(kernel: &Arc) -> ExitCode { ExitCode::SUCCESS } +/// 校准用例:长寿命 kernel 寿命旋钮端到端 + PermInterner 增长斜率首测 +/// 基线。专用 kernel(重建阈值 Some(100_000),不污染 bench 共享 kernel), +/// K = 200_000 次唯一键 intern(每 4 次物化一次,模拟 `perm_string` = +/// intern + 物化同式),每 20k 采 VmRSS;增长相斜率只记录不判定 +/// (append-only 增长,正斜率即预期签名,"斜率>0 即 fail" 契约不适用, +/// 独立打印逻辑);重建相验"无存活 VM"边界重建:阈值已达 → +/// `should_rebuild_perm` true → drop 旧 kernel → 新 kernel 严格主锚 +/// `entry_count` 恰 0 + 旋钮 false。 +/// +/// # 注意事项 +/// 不传 bench 共享 kernel:20 万条目会改变后续 case 的 CodeForge/harness +/// 面;本 case 全程无存活 VM(从不自建),drop 旧 Arc 即整体释放。 +/// 重建后 RSS 可能不回落至增长前之下(glibc 小对象 free 滞留分配器自由 +/// 链不回 OS),RSS 为次锚仅参考、不进 exit 判据。exit 0/1 仅由重建相 +/// 确定性断言驱动。 +pub fn run_mem_kernel_lifetime() -> ExitCode { + const TOTAL: u32 = 200_000; + const SAMPLE_EVERY: u32 = 20_000; + const REBUILD_THRESHOLD: u32 = 100_000; + + let mut config = KernelConfig::minimal(); + config.perm_interner_max_entries = Some(REBUILD_THRESHOLD); + let kernel = KernelCore::new(config.clone()); + let interner = kernel.perm_interner(); + + // 增长相:唯一键 intern + 1/4 物化,RSS 序列采样。 + let rss_pre = read_vmrss_kb(); + let mut series: Vec<(usize, f64)> = Vec::new(); + for i in 0..TOTAL { + let key = format!("kernel_lifetime_{i}"); + let (id, _) = interner.intern(&key); + if i % 4 == 0 { + let _ = interner.string_ptr(id); + } + if i % SAMPLE_EVERY == 0 { + if let Some(kb) = read_vmrss_kb() { + series.push((i as usize, kb as f64)); + } + } + } + let rss_post = read_vmrss_kb(); + if let Some(kb) = rss_post { + series.push((TOTAL as usize, kb as f64)); + } + let entries = interner.entry_count(); + let (slope, r2) = linreg(&series); + eprintln!( + "[kernel_lifetime] growth: samples={} rss_kb={}..{} entries={} slope={:.6} kB/entry (~{:.1} B/entry) R²={:.4} (append-only 预期签名,仅记录不判定)", + series.len(), + rss_pre.unwrap_or(0) as f64, + rss_post.unwrap_or(0) as f64, + entries, + slope, + slope * 1024.0, + r2 + ); + + // 重建相:旋钮端到端 + 严格主锚。 + if entries != TOTAL || !kernel.should_rebuild_perm() { + eprintln!( + "[kernel_lifetime] knob not armed: entries={} (expect {TOTAL}) should_rebuild={}", + entries, + kernel.should_rebuild_perm() + ); + return ExitCode::FAILURE; + } + drop(kernel); + let rebuilt = KernelCore::new(config); + let rebuilt_entries = rebuilt.perm_interner().entry_count(); + eprintln!( + "[kernel_lifetime] rebuild: entries {entries} -> {rebuilt_entries} should_rebuild={} rss_kb={:?}", + rebuilt.should_rebuild_perm(), + read_vmrss_kb() + ); + if rebuilt_entries != 0 || rebuilt.should_rebuild_perm() { + return ExitCode::FAILURE; + } + ExitCode::SUCCESS +} + /// 全序列回归报告:打印首末 RSS、全序列斜率/R² 与窗口(末 20 样本)总 /// 增量;斜率显著为正(R² > 0.9)返回失败,表示泄漏签名。 fn report_series(case: &str, unit: &str, series: &[(usize, f64)]) -> ExitCode { diff --git a/crates/oxide_cli/src/bench/mod.rs b/crates/oxide_cli/src/bench/mod.rs index d53aca88..acd42b4c 100644 --- a/crates/oxide_cli/src/bench/mod.rs +++ b/crates/oxide_cli/src/bench/mod.rs @@ -43,6 +43,7 @@ pub fn run_benchmarks(config: BenchConfig, kernel: Arc, pool: Arc leak_detect::run_mem_closure_dead_leak(&kernel), 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(), _ => leak_detect::run_leak_detect(&config, &kernel, &pool), }, _ => {