mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 新增 --profile 旗标聚合现有计数器统一出口
This commit is contained in:
Generated
+18
-18
@@ -1095,7 +1095,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1120,7 +1120,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1128,7 +1128,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1139,7 +1139,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1159,7 +1159,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1168,7 +1168,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1187,7 +1187,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1201,7 +1201,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1216,7 +1216,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1225,7 +1225,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1240,7 +1240,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1252,7 +1252,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1261,7 +1261,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1276,7 +1276,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1292,7 +1292,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1304,7 +1304,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"hashbrown 0.14.5",
|
||||
@@ -1333,7 +1333,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -13,6 +13,8 @@ pub mod leak_detect;
|
||||
pub mod metrics;
|
||||
/// 结果格式化输出。
|
||||
pub mod output;
|
||||
/// `--profile` 旗标的聚合计数器快照。
|
||||
pub mod profile;
|
||||
/// Rust 原生 benchmark 委托。
|
||||
pub mod rust_bench;
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
//! `--profile` 旗标的聚合计数器快照:只读聚合现有计数器(GC 统计、内联缓存
|
||||
//! 命中/未命中、session 堆账目、指令数),按每指标一行输出。
|
||||
//!
|
||||
//! 零热路径写:`collect` 只从 VM 内省面读 getter,不写任何计数器;旗标默认
|
||||
//! 关闭,关闭时不进入采集路径。
|
||||
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
/// 单次 run 的聚合计数器快照(现有计数器的只读聚合)。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProfileOutput {
|
||||
// GC 统计(session 累计口径,12 字段)
|
||||
pub gc_total_collections: u64,
|
||||
pub gc_total_bytes_freed: u64,
|
||||
pub gc_total_objects_scanned: u64,
|
||||
pub gc_total_objects_live: u64,
|
||||
pub gc_total_objects_dead: u64,
|
||||
pub gc_last_objects_scanned: u64,
|
||||
pub gc_last_objects_live: u64,
|
||||
pub gc_last_objects_dead: u64,
|
||||
pub gc_last_bytes_freed: u64,
|
||||
pub gc_last_duration_us: u64,
|
||||
pub gc_max_duration_us: u64,
|
||||
pub gc_min_duration_us: u64,
|
||||
// 内联缓存命中/未命中
|
||||
pub ic_hits: u64,
|
||||
pub ic_misses: u64,
|
||||
pub ic_hit_rate: f64,
|
||||
// session 堆账目
|
||||
pub session_objects: usize,
|
||||
pub session_bytes: usize,
|
||||
pub session_bytes_peak: usize,
|
||||
pub run_alloc_peak: usize,
|
||||
pub run_alloc_bytes: usize,
|
||||
// 指令数与执行时长
|
||||
pub instruction_count: u64,
|
||||
pub exec_time_us: u64,
|
||||
}
|
||||
|
||||
impl ProfileOutput {
|
||||
/// 从 VM 内省面只读聚合(零热路径写,不写任何计数器)。
|
||||
///
|
||||
/// `exec_time_us` 由调用方在 `run` 前后计时后传入。
|
||||
pub fn collect(vm: &Vm, exec_time_us: u64) -> Self {
|
||||
let gc = vm.session_gc_stats();
|
||||
Self {
|
||||
gc_total_collections: gc.total_collections,
|
||||
gc_total_bytes_freed: gc.total_bytes_freed,
|
||||
gc_total_objects_scanned: gc.total_objects_scanned,
|
||||
gc_total_objects_live: gc.total_objects_live,
|
||||
gc_total_objects_dead: gc.total_objects_dead,
|
||||
gc_last_objects_scanned: gc.last_collection_objects_scanned,
|
||||
gc_last_objects_live: gc.last_collection_objects_live,
|
||||
gc_last_objects_dead: gc.last_collection_objects_dead,
|
||||
gc_last_bytes_freed: gc.last_collection_bytes_freed,
|
||||
gc_last_duration_us: gc.last_collection_duration_us,
|
||||
gc_max_duration_us: gc.max_collection_duration_us,
|
||||
// 无收集时 min 字段为 u64::MAX 哨兵,映射为 0 避免输出哨兵值。
|
||||
gc_min_duration_us: if gc.min_collection_duration_us == u64::MAX {
|
||||
0
|
||||
} else {
|
||||
gc.min_collection_duration_us
|
||||
},
|
||||
ic_hits: vm.ic_hit_count(),
|
||||
ic_misses: vm.ic_miss_count(),
|
||||
ic_hit_rate: vm.ic_hit_rate(),
|
||||
session_objects: vm.session_object_count(),
|
||||
session_bytes: vm.session_bytes_allocated(),
|
||||
session_bytes_peak: vm.session_bytes_peak(),
|
||||
run_alloc_peak: vm.run_alloc_peak(),
|
||||
run_alloc_bytes: vm.run_alloc_bytes_total(),
|
||||
instruction_count: vm.instruction_count(),
|
||||
exec_time_us,
|
||||
}
|
||||
}
|
||||
|
||||
/// 导出为 `(指标名, 值)` 列表,每指标一行,与 bench 输出对齐。
|
||||
pub fn iter_metrics(&self) -> Vec<(&'static str, String)> {
|
||||
vec![
|
||||
("instruction_count", self.instruction_count.to_string()),
|
||||
("session_objects", self.session_objects.to_string()),
|
||||
("session_bytes", self.session_bytes.to_string()),
|
||||
("session_bytes_peak", self.session_bytes_peak.to_string()),
|
||||
("run_alloc_peak", self.run_alloc_peak.to_string()),
|
||||
("run_alloc_bytes", self.run_alloc_bytes.to_string()),
|
||||
("ic_hit_rate", format!("{:.4}", self.ic_hit_rate)),
|
||||
("ic_hits", self.ic_hits.to_string()),
|
||||
("ic_misses", self.ic_misses.to_string()),
|
||||
("gc_total_collections", self.gc_total_collections.to_string()),
|
||||
("gc_total_bytes_freed", self.gc_total_bytes_freed.to_string()),
|
||||
("gc_total_objects_scanned", self.gc_total_objects_scanned.to_string()),
|
||||
("gc_total_objects_live", self.gc_total_objects_live.to_string()),
|
||||
("gc_total_objects_dead", self.gc_total_objects_dead.to_string()),
|
||||
("gc_last_objects_scanned", self.gc_last_objects_scanned.to_string()),
|
||||
("gc_last_objects_live", self.gc_last_objects_live.to_string()),
|
||||
("gc_last_objects_dead", self.gc_last_objects_dead.to_string()),
|
||||
("gc_last_bytes_freed", self.gc_last_bytes_freed.to_string()),
|
||||
("gc_last_duration_us", self.gc_last_duration_us.to_string()),
|
||||
("gc_max_duration_us", self.gc_max_duration_us.to_string()),
|
||||
("gc_min_duration_us", self.gc_min_duration_us.to_string()),
|
||||
("exec_time_us", self.exec_time_us.to_string()),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
use std::fs;
|
||||
use std::process::ExitCode;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use ansi_term::Colour::Red;
|
||||
use clap::{Parser, Subcommand};
|
||||
@@ -40,6 +41,11 @@ struct Cli {
|
||||
|
||||
#[arg(short, long, global = true)]
|
||||
quiet: bool,
|
||||
|
||||
/// 聚合并输出现有计数器(GC 统计、内联缓存、session 堆账目、指令数)。
|
||||
/// 默认关闭,关闭时零开销;输出每指标一行,与 bench 输出对齐。
|
||||
#[arg(long, global = true)]
|
||||
profile: bool,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
@@ -94,13 +100,13 @@ fn main() -> ExitCode {
|
||||
Some(Commands::Eval { code, trace }) => {
|
||||
let kernel = make_kernel(cli.verbose, cli.quiet);
|
||||
let pool = make_pool(&kernel);
|
||||
eval(&code, &kernel, &pool, trace, true)
|
||||
eval(&code, &kernel, &pool, trace, cli.profile, true)
|
||||
}
|
||||
Some(Commands::Run { file, repeat, trace }) => {
|
||||
let kernel = make_kernel(cli.verbose, cli.quiet);
|
||||
let pool = make_pool(&kernel);
|
||||
for n in 0..repeat {
|
||||
let code = run(&file, &kernel, &pool, trace);
|
||||
let code = run(&file, &kernel, &pool, trace, cli.profile);
|
||||
// 失败即终止并传播退出码,供脚本与 CI 区分成败。
|
||||
if code != ExitCode::SUCCESS {
|
||||
return code;
|
||||
@@ -158,7 +164,9 @@ fn make_pool(kernel: &Arc<KernelCore>) -> Arc<VmPool> {
|
||||
VmPool::new(Arc::clone(kernel), kernel.config.min_pool_size, kernel.config.max_pool_size)
|
||||
}
|
||||
|
||||
fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool, print_result: bool) -> ExitCode {
|
||||
fn eval(
|
||||
code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool, profile: bool, print_result: bool,
|
||||
) -> ExitCode {
|
||||
let allocator = Allocator::default();
|
||||
let program = match oxide_parser::parse(&allocator, code) {
|
||||
Ok(p) => p,
|
||||
@@ -184,8 +192,16 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool, p
|
||||
|
||||
let mut guard = pool.spawn();
|
||||
guard.vm_mut().set_instruction_trace(trace);
|
||||
let exec_start = Instant::now();
|
||||
match guard.vm_mut().run(&module) {
|
||||
Ok(result) => {
|
||||
// --profile 开启时聚合现有计数器,每指标一行输出到 stderr(stdout 保持纯结果)。
|
||||
if profile {
|
||||
let p = bench::profile::ProfileOutput::collect(guard.vm(), exec_start.elapsed().as_micros() as u64);
|
||||
for (name, value) in p.iter_metrics() {
|
||||
eprintln!("{name} = {value}");
|
||||
}
|
||||
}
|
||||
// eval 臂按 REPL 语义打印完成值;run 臂脚本只输出自身产生内容。
|
||||
if print_result {
|
||||
format_result(guard.vm(), kernel.perm_interner().as_ref(), kernel.shape_forge().as_ref(), result);
|
||||
@@ -292,9 +308,9 @@ fn format_array(
|
||||
format!("[{}]", items.join(", "))
|
||||
}
|
||||
|
||||
fn run(file: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool) -> ExitCode {
|
||||
fn run(file: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>, trace: bool, profile: bool) -> ExitCode {
|
||||
match fs::read_to_string(file) {
|
||||
Ok(source) => eval(&source, kernel, pool, trace, false),
|
||||
Ok(source) => eval(&source, kernel, pool, trace, profile, false),
|
||||
Err(err) => {
|
||||
kernel_error!("cannot read {}: {}", file, err);
|
||||
eprintln!("{}", Red.paint(format!("Cannot read {file}: {err}")));
|
||||
|
||||
@@ -185,6 +185,14 @@ impl Vm {
|
||||
+ self.gc_state.session_cell_ptrs.borrow().len() * std::mem::size_of::<Cell>()
|
||||
}
|
||||
|
||||
/// 本 run 累计分配字节(O(1) 读)。
|
||||
///
|
||||
/// 公开包装,供 profiling 统一出口跨 crate 读取:与 `run_alloc_bytes` 同一读
|
||||
/// 口径,零行为变化。
|
||||
pub fn run_alloc_bytes_total(&self) -> usize {
|
||||
self.run_alloc_bytes()
|
||||
}
|
||||
|
||||
/// 本 run 累计分配字节的全量重算版:各分量按当前值各计一次、互不相交——
|
||||
/// 存活对象头加堆数据(属性/元素/meta 向量容量、upvalue 列表容量、native
|
||||
/// 状态盒)+ 存活串(头加 payload)+ BigInt 表长 × 尺寸 + cell 表长 ×
|
||||
|
||||
Reference in New Issue
Block a user