refactor: 扁平化对象属性存储并优化常量池与 IC 热路径
This commit is contained in:
@@ -227,9 +227,9 @@ fn make_pair(
|
||||
|
||||
// Pre-allocate Vec slots so wire_ctor_proto can overwrite vec[0] later.
|
||||
// Proto: 1 property ("constructor"), Ctor: 2 properties ("prototype", "name").
|
||||
proto.ensure_hash_props().push(Box::new(JsValue::undefined())); // slot 0: "constructor" placeholder
|
||||
ctor.ensure_hash_props().push(Box::new(JsValue::undefined())); // slot 0: "prototype" placeholder
|
||||
ctor.ensure_hash_props().push(Box::new(JsValue::undefined())); // slot 1: "name" placeholder
|
||||
proto.ensure_hash_props().push(JsValue::undefined()); // slot 0: "constructor" placeholder
|
||||
ctor.ensure_hash_props().push(JsValue::undefined()); // slot 0: "prototype" placeholder
|
||||
ctor.ensure_hash_props().push(JsValue::undefined()); // slot 1: "name" placeholder
|
||||
|
||||
(P::new(proto), P::new(ctor))
|
||||
}
|
||||
@@ -299,13 +299,13 @@ impl BuiltinWorld {
|
||||
let ctor = unsafe { &mut *ctor_ptr };
|
||||
let vec = ctor.ensure_hash_props();
|
||||
if !vec.is_empty() {
|
||||
*vec[0] = JsValue::from_js_object(proto.as_ptr() as *mut JsObject);
|
||||
vec[0] = JsValue::from_js_object(proto.as_ptr() as *mut JsObject);
|
||||
}
|
||||
let proto_ptr = proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
let pvec = proto.ensure_hash_props();
|
||||
if !pvec.is_empty() {
|
||||
*pvec[0] = JsValue::from_js_object(ctor_ptr);
|
||||
pvec[0] = JsValue::from_js_object(ctor_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,11 +626,11 @@ impl BuiltinWorld {
|
||||
wrapper.set_shape_id(name_shape);
|
||||
wrapper
|
||||
.ensure_hash_props()
|
||||
.push(Box::new(JsValue::string(string_forge.intern(method_name).0, 0)));
|
||||
.push(JsValue::string(string_forge.intern(method_name).0, 0));
|
||||
let wrapper_val = JsValue::from_js_object(Box::into_raw(wrapper));
|
||||
let new_shape = shape_forge.make_shape(proto.shape_id(), si);
|
||||
proto.set_shape_id(new_shape);
|
||||
proto.ensure_hash_props().push(Box::new(wrapper_val));
|
||||
proto.ensure_hash_props().push(wrapper_val);
|
||||
proto.bump_generation();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -9,13 +9,21 @@ use crate::{kernel_debug, kernel_info};
|
||||
|
||||
pub use oxide_compiler::compiler::structural_hash;
|
||||
|
||||
/// Shared compiled-module cache.
|
||||
///
|
||||
/// Concurrency model:
|
||||
/// - Read-heavy after warmup: cache hits dominate steady-state execution.
|
||||
/// - Backed by a fixed-shard `DashMap` so lookup contention stays predictable across
|
||||
/// developer machines and CI hosts.
|
||||
pub struct CodeForge {
|
||||
map: DashMap<u64, Arc<CompiledModule>>,
|
||||
}
|
||||
|
||||
impl CodeForge {
|
||||
pub fn new() -> Self {
|
||||
Self { map: DashMap::new() }
|
||||
Self {
|
||||
map: DashMap::with_shard_amount(16),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_or_compile(
|
||||
|
||||
@@ -103,15 +103,15 @@ impl OxideKernel {
|
||||
|
||||
let nan_shape = shape_forge.make_shape(EMPTY_SHAPE_ID, si_nan);
|
||||
global_obj.set_shape_id(nan_shape);
|
||||
global_obj.ensure_hash_props().push(Box::new(JsValue::float(f64::NAN)));
|
||||
global_obj.ensure_hash_props().push(JsValue::float(f64::NAN));
|
||||
|
||||
let undef_shape = shape_forge.make_shape(nan_shape, si_undef);
|
||||
global_obj.set_shape_id(undef_shape);
|
||||
global_obj.ensure_hash_props().push(Box::new(JsValue::undefined()));
|
||||
global_obj.ensure_hash_props().push(JsValue::undefined());
|
||||
|
||||
let inf_shape = shape_forge.make_shape(undef_shape, si_infinity);
|
||||
global_obj.set_shape_id(inf_shape);
|
||||
global_obj.ensure_hash_props().push(Box::new(JsValue::float(f64::INFINITY)));
|
||||
global_obj.ensure_hash_props().push(JsValue::float(f64::INFINITY));
|
||||
|
||||
let global_object = P::new(global_obj);
|
||||
|
||||
|
||||
@@ -18,6 +18,13 @@ pub struct Shape {
|
||||
pub parent: Option<ShapeId>,
|
||||
}
|
||||
|
||||
/// Shared hidden-class store.
|
||||
///
|
||||
/// Concurrency model:
|
||||
/// - `shapes` is append-mostly behind an `RwLock`.
|
||||
/// - `transitions` is a sharded `DashMap` keyed by `(parent_shape, property_name)`.
|
||||
/// - Shard count is fixed for reproducible contention behavior across machines instead
|
||||
/// of relying on DashMap's CPU-dependent default.
|
||||
pub struct ShapeForge {
|
||||
shapes: RwLock<Vec<Option<Arc<Shape>>>>,
|
||||
transitions: DashMap<u64, ShapeId>,
|
||||
@@ -28,7 +35,7 @@ impl ShapeForge {
|
||||
pub fn new() -> Self {
|
||||
let forge = Self {
|
||||
shapes: RwLock::new(Vec::with_capacity(256)),
|
||||
transitions: DashMap::with_capacity(256),
|
||||
transitions: DashMap::with_capacity_and_shard_amount(256, 16),
|
||||
next_id: AtomicU32::new(2),
|
||||
};
|
||||
{
|
||||
|
||||
@@ -24,6 +24,14 @@ struct StringForgeInner {
|
||||
entries: Vec<StringEntry>,
|
||||
}
|
||||
|
||||
/// Global string interning pool shared by all VMs.
|
||||
///
|
||||
/// Concurrency model:
|
||||
/// `StringForge` keeps a single `RwLock` around the interner state.
|
||||
/// - `lookup()` takes a shared read lock and scales across readers.
|
||||
/// - `intern()` uses a read-check then write-check pattern to keep hot-path hits cheap.
|
||||
/// - New string insertion is serialized, which matches the workload here: most strings
|
||||
/// are interned early and later accesses are dominated by reads.
|
||||
pub struct StringForge {
|
||||
inner: RwLock<StringForgeInner>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user