perf: 解析入口改用线程本地分配器,稳态免系统调用

This commit is contained in:
2026-10-12 03:35:12 +00:00
parent 75bb28671a
commit 9084aff761
8 changed files with 161 additions and 97 deletions
Generated
+20 -20
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_builtins" name = "oxide_builtins"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"chrono", "chrono",
"fast-float", "fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_bytecode" name = "oxide_bytecode"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxide_log", "oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cfg" name = "oxide_cfg"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_emit", "oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cli" name = "oxide_cli"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"bumpalo", "bumpalo",
@@ -1228,7 +1228,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_code_cache" name = "oxide_code_cache"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"lru", "lru",
"oxide_bytecode", "oxide_bytecode",
@@ -1237,7 +1237,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_compiler" name = "oxide_compiler"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1257,7 +1257,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_dce" name = "oxide_dce"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1271,7 +1271,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_emit" name = "oxide_emit"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxc_allocator", "oxc_allocator",
@@ -1286,7 +1286,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_engine" name = "oxide_engine"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1298,7 +1298,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_ir" name = "oxide_ir"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_log", "oxide_log",
@@ -1307,7 +1307,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_kernel" name = "oxide_kernel"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1321,7 +1321,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_liveness" name = "oxide_liveness"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1333,7 +1333,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_log" name = "oxide_log"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"tracing", "tracing",
"tracing-appender", "tracing-appender",
@@ -1342,11 +1342,11 @@ dependencies = [
[[package]] [[package]]
name = "oxide_net" name = "oxide_net"
version = "0.16.1" version = "0.16.2"
[[package]] [[package]]
name = "oxide_parser" name = "oxide_parser"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"oxc_allocator", "oxc_allocator",
"oxc_ast", "oxc_ast",
@@ -1361,7 +1361,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_regalloc" name = "oxide_regalloc"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1377,7 +1377,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_runtime_api" name = "oxide_runtime_api"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"num-traits", "num-traits",
@@ -1390,7 +1390,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_test262" name = "oxide_test262"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1410,7 +1410,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_types" name = "oxide_types"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1423,7 +1423,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_vm" name = "oxide_vm"
version = "0.16.1" version = "0.16.2"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"chrono", "chrono",
+1 -1
View File
@@ -24,7 +24,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.16.1" version = "0.16.2"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
rust-version = "1.80" rust-version = "1.80"
+15 -16
View File
@@ -12,7 +12,6 @@ use std::sync::Arc;
use oxide_compiler::compiler::{compiled_module_hash, Compiler}; use oxide_compiler::compiler::{compiled_module_hash, Compiler};
use oxide_kernel::kernel::KernelCore; use oxide_kernel::kernel::KernelCore;
use oxide_parser::Allocator;
use oxide_vm::vm_pool::VmPool; use oxide_vm::vm_pool::VmPool;
use super::protocol::ServerResponse; use super::protocol::ServerResponse;
@@ -20,7 +19,7 @@ use super::protocol::ServerResponse;
/// 执行请求处理:解析 → 编译 → 取 VM → 执行 → 渲染完成值 → 归还池的六段路径。 /// 执行请求处理:解析 → 编译 → 取 VM → 执行 → 渲染完成值 → 归还池的六段路径。
/// ///
/// # 步骤 /// # 步骤
/// 1. parse:局部分配器加 `oxide_parser::parse`,失败时各条错误换行拼接成错误帧。 /// 1. parse:线程本地分配器加 `oxide_parser::parse`,失败时各条错误换行拼接成错误帧。
/// 2. compile:`Compiler` 加 `CodeForge` 结构哈希缓存,失败以原文成错误帧。 /// 2. compile:`Compiler` 加 `CodeForge` 结构哈希缓存,失败以原文成错误帧。
/// 3. spawn:从池取 `VmGuard`(闭包内创建,panic 时随展开被 drop)。 /// 3. spawn:从池取 `VmGuard`(闭包内创建,panic 时随展开被 drop)。
/// 4. run:设每请求步数覆盖(含 None)后 `run`,`Err` 即错误帧。 /// 4. run:设每请求步数覆盖(含 None)后 `run`,`Err` 即错误帧。
@@ -38,22 +37,22 @@ use super::protocol::ServerResponse;
/// - release 下 `panic=abort` 使 `catch_unwind` 空操作,watchdog 是崩溃后恢复的唯一自动手段。 /// - release 下 `panic=abort` 使 `catch_unwind` 空操作,watchdog 是崩溃后恢复的唯一自动手段。
pub fn handle_eval(code: &str, max_steps: Option<u64>, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ServerResponse { pub fn handle_eval(code: &str, max_steps: Option<u64>, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ServerResponse {
let response = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { let response = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
// parse:局部分配器,解析失败各条换行拼接成错误帧。 // parse + compile:线程本地分配器,AST 生命周期被闭包边界约束(不逃逸)。
let allocator = Allocator::default(); // 解析失败各条换行拼接成错误帧,编译失败以原文成错误帧。
let program = match oxide_parser::parse(&allocator, code) { let module = match oxide_parser::with_parse_allocator(|allocator| {
Ok(program) => program, let program = oxide_parser::parse(allocator, code).map_err(|errors| {
Err(errors) => {
let message = errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("\n"); let message = errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("\n");
return ServerResponse::eval_err(message); ServerResponse::eval_err(message)
} })?;
}; let compiler = Compiler::new();
let hash = compiled_module_hash(&program);
// compile:结构哈希缓存,失败以原文成错误帧。 kernel
let compiler = Compiler::new(); .code_forge()
let hash = compiled_module_hash(&program); .get_or_insert_with(hash, || compiler.compile(&program))
let module = match kernel.code_forge().get_or_insert_with(hash, || compiler.compile(&program)) { .map_err(ServerResponse::eval_err)
}) {
Ok(module) => module, Ok(module) => module,
Err(message) => return ServerResponse::eval_err(message), Err(response) => return response,
}; };
// spawn:从池取 VM;guard 在闭包内创建,panic 时随展开被 drop。 // spawn:从池取 VM;guard 在闭包内创建,panic 时随展开被 drop。
+16 -14
View File
@@ -1,5 +1,5 @@
//! 默认编译服务:串联 `oxide_parser::parse` 与 `Compiler::compile`, //! 默认编译服务:串联 `oxide_parser::parse` 与 `Compiler::compile`,
//! parse 错误映射与 `Allocator` 构造的唯一实现源。 //! parse 错误映射的唯一实现源。
use oxide_bytecode::module::CompiledModule; use oxide_bytecode::module::CompiledModule;
use oxide_parser::Program; use oxide_parser::Program;
@@ -13,22 +13,24 @@ pub struct DefaultCompilerService;
impl CompilerService for DefaultCompilerService { impl CompilerService for DefaultCompilerService {
fn compile_script(&self, source: &str) -> Result<CompiledModule, String> { fn compile_script(&self, source: &str) -> Result<CompiledModule, String> {
// 局部分配器:AST 节点经 oxc bump 分配器分配,编译完成即弃。 // 线程本地分配器:AST 节点经 oxc bump 分配器分配,编译完成即弃。
let allocator = oxide_parser::Allocator::default(); oxide_parser::with_parse_allocator(|allocator| {
let program = parse_program(&allocator, source)?; let program = parse_program(allocator, source)?;
// 动态路径源契约:调用方以源码域转义(`string_forge::source_escape`) // 动态路径源契约:调用方以源码域转义(`string_forge::source_escape`)
// 形态传源,故置 source_encoded。 // 形态传源,故置 source_encoded。
Compiler::new().with_source_encoded(true).compile(&program) Compiler::new().with_source_encoded(true).compile(&program)
})
} }
fn compile_eval_script(&self, source: &str) -> Result<CompiledModule, String> { fn compile_eval_script(&self, source: &str) -> Result<CompiledModule, String> {
let allocator = oxide_parser::Allocator::default(); oxide_parser::with_parse_allocator(|allocator| {
let program = parse_program(&allocator, source)?; let program = parse_program(allocator, source)?;
// eval 脚本:顶层 var/function 声明落全局属性 configurable:true。 // eval 脚本:顶层 var/function 声明落全局属性 configurable:true。
Compiler::new() Compiler::new()
.with_eval_script(true) .with_eval_script(true)
.with_source_encoded(true) .with_source_encoded(true)
.compile(&program) .compile(&program)
})
} }
} }
+24 -21
View File
@@ -958,27 +958,30 @@ impl Emitter {
fn compile_js_dep( fn compile_js_dep(
&self, resolved: &ResolvedModule, loader: &mut dyn ModuleSourceLoader, path_stack: &mut Vec<String>, &self, resolved: &ResolvedModule, loader: &mut dyn ModuleSourceLoader, path_stack: &mut Vec<String>,
) -> Result<(IRFunction, bool, HashSet<String>, HashSet<String>), String> { ) -> Result<(IRFunction, bool, HashSet<String>, HashSet<String>), String> {
let alloc = oxide_parser::Allocator::default(); // 线程本地分配器:依赖模块递归解析共享同一分配器,
let program = oxide_parser::parse_module(&alloc, &resolved.source).map_err(|errs| { // 重入深度守卫防止嵌套 reset 销毁外层 AST。
format!( oxide_parser::with_parse_allocator(|alloc| {
"module parse error: {}", let program = oxide_parser::parse_module(alloc, &resolved.source).map_err(|errs| {
errs.first().map(|e| e.message.as_str()).unwrap_or("parse failed") format!(
) "module parse error: {}",
})?; errs.first().map(|e| e.message.as_str()).unwrap_or("parse failed")
let mut ctx = CompileCtx::new(); )
ctx.is_global_scope = false; })?;
// 依赖模块经 parse_module 解析,顶层恒严格模式。 let mut ctx = CompileCtx::new();
ctx.is_strict = true; ctx.is_global_scope = false;
self.emit_module_into_ctx(&program, &resolved.path, loader, path_stack, &mut ctx, false)?; // 依赖模块经 parse_module 解析,顶层恒严格模式。
let reassignable = ctx.module_live_dep; ctx.is_strict = true;
let own_export_names = module_own_export_names(&program.body); self.emit_module_into_ctx(&program, &resolved.path, loader, path_stack, &mut ctx, false)?;
let var_scoped = module_var_scoped_export_names(&program.body); let reassignable = ctx.module_live_dep;
Ok(( let own_export_names = module_own_export_names(&program.body);
ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None), let var_scoped = module_var_scoped_export_names(&program.body);
reassignable, Ok((
own_export_names, ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None),
var_scoped, reassignable,
)) own_export_names,
var_scoped,
))
})
} }
/// 发 defer 预注册:对 deferred namespace 对象逐名 `__modulePreRegister`, /// 发 defer 预注册:对 deferred namespace 对象逐名 `__modulePreRegister`,
+7 -7
View File
@@ -68,16 +68,16 @@ impl Engine {
/// 构造传入,无缺省臂,漏传即旗标失效。 /// 构造传入,无缺省臂,漏传即旗标失效。
/// ///
/// # 步骤 /// # 步骤
/// 1. 局部分配器上解析源码为 Program。 /// 1. 线程本地分配器上解析源码为 Program。
/// 2. 结构哈希作缓存键,CodeForge 未命中时调给定编译器编译并缓存。 /// 2. 结构哈希作缓存键,CodeForge 未命中时调给定编译器编译并缓存。
pub fn compile_with(&self, source: &str, compiler: &Compiler) -> Result<Arc<CompiledModule>, String> { pub fn compile_with(&self, source: &str, compiler: &Compiler) -> Result<Arc<CompiledModule>, String> {
// 局部分配器:AST 节点经 oxc bump 分配器分配,编译完成即弃。 oxide_parser::with_parse_allocator(|allocator| {
let allocator = oxide_parser::Allocator::default(); let program = oxide_parser::parse(allocator, source)
let program = oxide_parser::parse(&allocator, source) .map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
let hash = compiled_module_hash(&program); let hash = compiled_module_hash(&program);
self.kernel.code_forge().get_or_insert_with(hash, || compiler.compile(&program)) self.kernel.code_forge().get_or_insert_with(hash, || compiler.compile(&program))
})
} }
/// 编译 + 池借出 Vm 执行,返回完成值(eval 便捷路径)。 /// 编译 + 池借出 Vm 执行,返回完成值(eval 便捷路径)。
+55
View File
@@ -4,6 +4,8 @@ mod error;
mod parser_log; mod parser_log;
mod redeclaration_filter; mod redeclaration_filter;
use std::cell::{Cell, UnsafeCell};
/// 解析错误:消息 + 源文件字节区间(起始,结束)。 /// 解析错误:消息 + 源文件字节区间(起始,结束)。
pub use error::OxideError; pub use error::OxideError;
/// 重导出 `oxc_allocator::Allocator`:AST 节点的 bump 分配器,解析产物生命周期由其决定。 /// 重导出 `oxc_allocator::Allocator`:AST 节点的 bump 分配器,解析产物生命周期由其决定。
@@ -13,6 +15,59 @@ pub use oxc_ast::ast::*;
/// 重导出 `oxc_span::Span`:源码中的位置区间。 /// 重导出 `oxc_span::Span`:源码中的位置区间。
pub use oxc_span::Span; pub use oxc_span::Span;
// 线程本地持久 bump 分配器:解析入口共享该分配器,chunk 内存按线程
// 高水位保留复用,稳态解析不再走全局分配器。
//
// 分配器存于 `UnsafeCell`:唯一可变操作 `reset()` 只在最外层(深度为零)
// 调用,彼时尚无共享引用存活;闭包执行期间只产生共享 `&Allocator` 引用
// (重入不再 reset),共享引用可别名,无 `&`/`&mut` 混用。
// 重入深度独立存于 `Cell`(`Copy` 类型内部可变),嵌套读写不产生借用冲突。
thread_local! {
static PARSE_ALLOCATOR: UnsafeCell<Allocator> = UnsafeCell::new(Allocator::new());
static PARSE_DEPTH: Cell<usize> = const { Cell::new(0) };
}
/// 重入深度守卫:出口(含 panic 展开)把深度减一,防深度泄漏。
struct DepthGuard<'a>(&'a Cell<usize>);
impl Drop for DepthGuard<'_> {
fn drop(&mut self) {
self.0.set(self.0.get() - 1);
}
}
/// 在线程本地分配器上执行一次解析上下文:闭包内完成解析与编译,
/// AST 生命周期被闭包边界约束。
///
/// # 边界与前提
/// - 闭包不得把 AST 逃逸出闭包边界:下一次顶层入口会 reset 分配器。
/// - 闭包内重入本助手(嵌套解析)安全:深度非零时跳过 reset,
/// 嵌套解析共享同一分配器,AST 在外层退出前全部有效。
///
/// # 副作用
/// - 深度为零时 reset 分配器(bump 指针归零,高水位 chunk 保留);
/// 重入深度入口加一、出口减一(含 panic 展开路径)。
pub fn with_parse_allocator<R, F>(f: F) -> R
where
F: for<'a> FnOnce(&'a Allocator) -> R,
{
PARSE_DEPTH.with(|depth| {
let is_root = depth.get() == 0;
depth.set(depth.get() + 1);
let _guard = DepthGuard(depth);
PARSE_ALLOCATOR.with(|cell| {
let ptr = cell.get();
// reset 只在最外层执行:此刻尚无共享引用存活,`&mut` 独占安全。
if is_root {
unsafe { (*ptr).reset() };
}
// 共享引用跨闭包存活;重入只再产生共享引用,无 `&mut` 混用。
let alloc = unsafe { &*ptr };
f(alloc)
})
})
}
/// 把 `source` 解析为 AST 根节点 `Program`,AST 分配在 `allocator` 中。 /// 把 `source` 解析为 AST 根节点 `Program`,AST 分配在 `allocator` 中。
/// ///
/// 使用 oxc 默认 `SourceType`(严格模式由语法自身决定)。返回 `Ok(program)` /// 使用 oxc 默认 `SourceType`(严格模式由语法自身决定)。返回 `Ok(program)`
+23 -18
View File
@@ -145,30 +145,35 @@ fn run_test_inner(
return TestResult::skip(path.to_path_buf(), e); return TestResult::skip(path.to_path_buf(), e);
} }
}; };
let alloc = oxide_parser::Allocator::default(); // 线程本地分配器:解析与编译同闭包内完成,AST 生命周期被闭包边界约束。
let program = match oxide_parser::parse_module(&alloc, &code) { // 闭包返回模块列表或提前判定的结果,外层拆包:Ok 取模块,Err 直接返回结果。
Ok(p) => p, match oxide_parser::with_parse_allocator(|alloc| {
Err(errs) => { // 解析模块:解析失败按 negative 元数据判定 pass/fail,短路返回。
let program = oxide_parser::parse_module(alloc, &code).map_err(|errs| {
let dur = start.elapsed().as_millis() as u64; let dur = start.elapsed().as_millis() as u64;
let msg = format!("parse error: {}", errs[0].message); let msg = format!("parse error: {}", errs[0].message);
if meta.negative.is_some() { if meta.negative.is_some() {
return TestResult::pass(path.to_path_buf(), dur, msg); TestResult::pass(path.to_path_buf(), dur, msg)
} else {
TestResult::fail(path.to_path_buf(), dur, msg)
} }
return TestResult::fail(path.to_path_buf(), dur, msg); })?;
} let mut loader = Test262ModuleLoader;
}; match compiler.compile_module(&program, path.to_string_lossy().as_ref(), &mut loader) {
let mut loader = Test262ModuleLoader; Ok(m) => Ok(vec![Arc::new(m)]),
match compiler.compile_module(&program, path.to_string_lossy().as_ref(), &mut loader) { Err(e) => {
Ok(m) => vec![Arc::new(m)], let dur = start.elapsed().as_millis() as u64;
Err(e) => { let msg = format!("compile error: {e}");
let dur = start.elapsed().as_millis() as u64; match classify_compile_error(&msg, meta.negative.is_some(), no_skip) {
let msg = format!("compile error: {e}"); TestOutcome::Pass(m) => Err(TestResult::pass(path.to_path_buf(), dur, m)),
match classify_compile_error(&msg, meta.negative.is_some(), no_skip) { TestOutcome::Fail(m) => Err(TestResult::fail(path.to_path_buf(), dur, m)),
TestOutcome::Pass(m) => return TestResult::pass(path.to_path_buf(), dur, m), TestOutcome::Skip(m) => Err(TestResult::skip(path.to_path_buf(), m)),
TestOutcome::Fail(m) => return TestResult::fail(path.to_path_buf(), dur, m), }
TestOutcome::Skip(m) => return TestResult::skip(path.to_path_buf(), m),
} }
} }
}) {
Ok(modules) => modules,
Err(result) => return result,
} }
} else { } else {
// script 臂两模块方案:前缀模块按 includes 组合缓存(免每测试重解析重编译), // script 臂两模块方案:前缀模块按 includes 组合缓存(免每测试重解析重编译),