Browse Source

Shrink size of `IndexedProperties` (#2757)

Most objects don't have indexed properties, and those who have, have dense properties, this PR uses `ThinVec` to reduce the size of dense properties.

It changes the following:
- Trim `16` bytes from `IndexedProperties`, this reduces all objects size
pull/2765/head
Haled Odat 2 years ago
parent
commit
edb0417543
  1. 1
      Cargo.lock
  2. 2
      boa_engine/Cargo.toml
  3. 3
      boa_engine/src/builtins/array/mod.rs
  4. 17
      boa_engine/src/object/property_map.rs
  5. 6
      boa_gc/Cargo.toml
  6. 13
      boa_gc/src/trace.rs

1
Cargo.lock generated

@ -438,6 +438,7 @@ version = "0.16.0"
dependencies = [ dependencies = [
"boa_macros", "boa_macros",
"boa_profiler", "boa_profiler",
"thin-vec",
] ]
[[package]] [[package]]

2
boa_engine/Cargo.toml

@ -41,7 +41,7 @@ console = []
[dependencies] [dependencies]
boa_interner.workspace = true boa_interner.workspace = true
boa_gc.workspace = true boa_gc = { workspace = true, features = [ "thinvec" ] }
boa_profiler.workspace = true boa_profiler.workspace = true
boa_macros.workspace = true boa_macros.workspace = true
boa_ast.workspace = true boa_ast.workspace = true

3
boa_engine/src/builtins/array/mod.rs

@ -11,6 +11,7 @@
use boa_macros::utf16; use boa_macros::utf16;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use thin_vec::ThinVec;
use crate::{ use crate::{
builtins::iterable::{if_abrupt_close_iterator, IteratorHint}, builtins::iterable::{if_abrupt_close_iterator, IteratorHint},
@ -284,7 +285,7 @@ impl Array {
// b. Set n to n + 1. // b. Set n to n + 1.
// //
// NOTE: This deviates from the spec, but it should have the same behaviour. // NOTE: This deviates from the spec, but it should have the same behaviour.
let elements: Vec<_> = elements.into_iter().collect(); let elements: ThinVec<_> = elements.into_iter().collect();
let length = elements.len(); let length = elements.len();
array array
.borrow_mut() .borrow_mut()

17
boa_engine/src/object/property_map.rs

@ -4,6 +4,7 @@ use boa_gc::{custom_trace, Finalize, Trace};
use indexmap::IndexMap; use indexmap::IndexMap;
use rustc_hash::{FxHashMap, FxHasher}; use rustc_hash::{FxHashMap, FxHasher};
use std::{collections::hash_map, hash::BuildHasherDefault, iter::FusedIterator}; use std::{collections::hash_map, hash::BuildHasherDefault, iter::FusedIterator};
use thin_vec::ThinVec;
/// Type alias to make it easier to work with the string properties on the global object. /// Type alias to make it easier to work with the string properties on the global object.
pub(crate) type GlobalPropertyMap = pub(crate) type GlobalPropertyMap =
@ -44,20 +45,20 @@ enum IndexedProperties {
/// the value field and construct the data property descriptor on demand. /// the value field and construct the data property descriptor on demand.
/// ///
/// This storage method is used by default. /// This storage method is used by default.
Dense(Vec<JsValue>), Dense(ThinVec<JsValue>),
/// Sparse storage this storage is used as a backup if the element keys are not continuous or the property descriptors /// Sparse storage this storage is used as a backup if the element keys are not continuous or the property descriptors
/// are not data descriptors with with a value field, writable field set to `true`, configurable field set to `true`, enumerable field set to `true`. /// are not data descriptors with with a value field, writable field set to `true`, configurable field set to `true`, enumerable field set to `true`.
/// ///
/// This method uses more space, since we also have to store the property descriptors, not just the value. /// This method uses more space, since we also have to store the property descriptors, not just the value.
/// It is also slower because we need to to a hash lookup. /// It is also slower because we need to to a hash lookup.
Sparse(FxHashMap<u32, PropertyDescriptor>), Sparse(Box<FxHashMap<u32, PropertyDescriptor>>),
} }
impl Default for IndexedProperties { impl Default for IndexedProperties {
#[inline] #[inline]
fn default() -> Self { fn default() -> Self {
Self::Dense(Vec::new()) Self::Dense(ThinVec::new())
} }
} }
@ -78,7 +79,7 @@ impl IndexedProperties {
} }
/// Helper function for converting from a dense storage type to sparse storage type. /// Helper function for converting from a dense storage type to sparse storage type.
fn convert_dense_to_sparse(vec: &mut Vec<JsValue>) -> FxHashMap<u32, PropertyDescriptor> { fn convert_dense_to_sparse(vec: &mut ThinVec<JsValue>) -> FxHashMap<u32, PropertyDescriptor> {
let data = std::mem::take(vec); let data = std::mem::take(vec);
data.into_iter() data.into_iter()
@ -143,7 +144,7 @@ impl IndexedProperties {
// Slow path: converting to sparse storage. // Slow path: converting to sparse storage.
let mut map = Self::convert_dense_to_sparse(vec); let mut map = Self::convert_dense_to_sparse(vec);
let old_property = map.insert(key, property); let old_property = map.insert(key, property);
*self = Self::Sparse(map); *self = Self::Sparse(Box::new(map));
old_property old_property
} }
@ -182,7 +183,7 @@ impl IndexedProperties {
// Slow Path: conversion to sparse storage. // Slow Path: conversion to sparse storage.
let mut map = Self::convert_dense_to_sparse(vec); let mut map = Self::convert_dense_to_sparse(vec);
let old_property = map.remove(&key); let old_property = map.remove(&key);
*self = Self::Sparse(map); *self = Self::Sparse(Box::new(map));
old_property old_property
} }
@ -277,12 +278,12 @@ impl PropertyMap {
} }
/// Overrides all the indexed properties, setting it to dense storage. /// Overrides all the indexed properties, setting it to dense storage.
pub(crate) fn override_indexed_properties(&mut self, properties: Vec<JsValue>) { pub(crate) fn override_indexed_properties(&mut self, properties: ThinVec<JsValue>) {
self.indexed_properties = IndexedProperties::Dense(properties); self.indexed_properties = IndexedProperties::Dense(properties);
} }
/// Returns the vec of dense indexed properties if they exist. /// Returns the vec of dense indexed properties if they exist.
pub(crate) const fn dense_indexed_properties(&self) -> Option<&Vec<JsValue>> { pub(crate) const fn dense_indexed_properties(&self) -> Option<&ThinVec<JsValue>> {
if let IndexedProperties::Dense(properties) = &self.indexed_properties { if let IndexedProperties::Dense(properties) = &self.indexed_properties {
Some(properties) Some(properties)
} else { } else {

6
boa_gc/Cargo.toml

@ -10,6 +10,12 @@ license.workspace = true
repository.workspace = true repository.workspace = true
rust-version.workspace = true rust-version.workspace = true
[features]
# Enable default implementatio of trace and finalize thin-vec crate
thinvec = ["thin-vec"]
[dependencies] [dependencies]
boa_profiler.workspace = true boa_profiler.workspace = true
boa_macros.workspace = true boa_macros.workspace = true
thin-vec = { version = "0.2.12", optional = true }

13
boa_gc/src/trace.rs

@ -308,6 +308,19 @@ unsafe impl<T: Trace> Trace for Vec<T> {
}); });
} }
#[cfg(feature = "thin-vec")]
impl<T: Trace> Finalize for thin_vec::ThinVec<T> {}
#[cfg(feature = "thin-vec")]
// SAFETY: All the inner elements of the `Vec` are correctly marked.
unsafe impl<T: Trace> Trace for thin_vec::ThinVec<T> {
custom_trace!(this, {
for e in this {
mark(e);
}
});
}
impl<T: Trace> Finalize for Option<T> {} impl<T: Trace> Finalize for Option<T> {}
// SAFETY: The inner value of the `Option` is correctly marked. // SAFETY: The inner value of the `Option` is correctly marked.
unsafe impl<T: Trace> Trace for Option<T> { unsafe impl<T: Trace> Trace for Option<T> {

Loading…
Cancel
Save