mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(semantic): add SemanticBuilder::with_excess_capacity (#5762)
Add `SemanticBuilder::with_excess_capacity` method to request that `SemanticBuilder` over-allocate space in `Semantic`'s `Vec`s. Use this method to reserve 200% extra capacity for transformer to create more scopes, symbols and references. 200% is an unscientific guess of how much extra capacity is required. Obviously it depends on what transforms are enabled and content of the source code.
This commit is contained in:
parent
f61e8b53dd
commit
3230ae5842
7 changed files with 61 additions and 5 deletions
|
|
@ -186,7 +186,14 @@ pub trait CompilerInterface {
|
|||
source_text: &'a str,
|
||||
source_path: &Path,
|
||||
) -> SemanticBuilderReturn<'a> {
|
||||
SemanticBuilder::new(source_text)
|
||||
let mut builder = SemanticBuilder::new(source_text);
|
||||
|
||||
if self.transform_options().is_some() {
|
||||
// Estimate transformer will triple scopes, symbols, references
|
||||
builder = builder.with_excess_capacity(2.0);
|
||||
}
|
||||
|
||||
builder
|
||||
.with_check_syntax_error(self.check_semantic_error())
|
||||
.with_scope_tree_child_ids(self.semantic_child_scope_ids())
|
||||
.build_module_record(source_path, program)
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ pub struct SemanticBuilder<'a> {
|
|||
build_jsdoc: bool,
|
||||
jsdoc: JSDocBuilder<'a>,
|
||||
stats: Option<Stats>,
|
||||
excess_capacity: f64,
|
||||
|
||||
/// Should additional syntax checks be performed?
|
||||
///
|
||||
|
|
@ -146,6 +147,7 @@ impl<'a> SemanticBuilder<'a> {
|
|||
build_jsdoc: false,
|
||||
jsdoc: JSDocBuilder::new(source_text, trivias),
|
||||
stats: None,
|
||||
excess_capacity: 0.0,
|
||||
check_syntax_error: false,
|
||||
cfg: None,
|
||||
class_table_builder: ClassTableBuilder::new(),
|
||||
|
|
@ -208,6 +210,24 @@ impl<'a> SemanticBuilder<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Request `SemanticBuilder` to allocate excess capacity for scopes, symbols, and references.
|
||||
///
|
||||
/// `excess_capacity` is provided as a fraction.
|
||||
/// e.g. to over-allocate by 20%, pass `0.2` as `excess_capacity`.
|
||||
///
|
||||
/// Has no effect if a `Stats` object is provided with [`SemanticBuilder::with_stats`],
|
||||
/// only if `SemanticBuilder` is calculating stats itself.
|
||||
///
|
||||
/// This is useful when you intend to modify `Semantic`, adding more `nodes`, `scopes`, `symbols`,
|
||||
/// or `references`. Allocating excess capacity for these additions at the outset prevents
|
||||
/// `Semantic`'s data structures needing to grow later on which involves memory copying.
|
||||
/// For large ASTs with a lot of semantic data, re-allocation can be very costly.
|
||||
#[must_use]
|
||||
pub fn with_excess_capacity(mut self, excess_capacity: f64) -> Self {
|
||||
self.excess_capacity = excess_capacity;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the built module record from `build_module_record`
|
||||
pub fn module_record(&self) -> Arc<ModuleRecord> {
|
||||
Arc::clone(&self.module_record)
|
||||
|
|
@ -248,10 +268,11 @@ impl<'a> SemanticBuilder<'a> {
|
|||
//
|
||||
// If user did not provide existing `Stats`, calculate them by visiting AST.
|
||||
let (stats, check_stats) = if let Some(stats) = self.stats {
|
||||
(stats, false)
|
||||
(stats, None)
|
||||
} else {
|
||||
let stats = Stats::count(program);
|
||||
(stats, true)
|
||||
let stats_with_excess = stats.increase_by(self.excess_capacity);
|
||||
(stats_with_excess, Some(stats))
|
||||
};
|
||||
self.nodes.reserve(stats.nodes as usize);
|
||||
self.scope.reserve(stats.scopes as usize);
|
||||
|
|
@ -262,7 +283,7 @@ impl<'a> SemanticBuilder<'a> {
|
|||
|
||||
// Check that estimated counts accurately (unless in release mode)
|
||||
#[cfg(debug_assertions)]
|
||||
if check_stats {
|
||||
if let Some(stats) = check_stats {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let actual_stats = Stats::new(
|
||||
self.nodes.len() as u32,
|
||||
|
|
|
|||
|
|
@ -96,6 +96,23 @@ impl Stats {
|
|||
counter.stats
|
||||
}
|
||||
|
||||
/// Increase scope, symbol, and reference counts by provided `excess`.
|
||||
///
|
||||
/// `excess` is provided as a fraction.
|
||||
/// e.g. to over-allocate by 20%, pass `0.2` as `excess`.
|
||||
#[must_use]
|
||||
pub fn increase_by(mut self, excess: f64) -> Self {
|
||||
let factor = excess + 1.0;
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_lossless)]
|
||||
let increase = |n: u32| (n as f64 * factor) as u32;
|
||||
|
||||
self.scopes = increase(self.scopes);
|
||||
self.symbols = increase(self.symbols);
|
||||
self.references = increase(self.references);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Assert that estimated [`Stats`] match actual.
|
||||
///
|
||||
/// # Panics
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ fn main() {
|
|||
let mut program = ret.program;
|
||||
|
||||
let (symbols, scopes) = SemanticBuilder::new(&source_text)
|
||||
// Estimate transformer will triple scopes, symbols, references
|
||||
.with_excess_capacity(2.0)
|
||||
.build(&program)
|
||||
.semantic
|
||||
.into_symbol_table_and_scope_tree();
|
||||
|
|
|
|||
|
|
@ -192,7 +192,12 @@ impl Oxc {
|
|||
self.ir = format!("{:#?}", program.body);
|
||||
self.ast = program.serialize(&self.serializer)?;
|
||||
|
||||
let semantic_ret = SemanticBuilder::new(source_text)
|
||||
let mut semantic_builder = SemanticBuilder::new(source_text);
|
||||
if run_options.transform.unwrap_or_default() {
|
||||
// Estimate transformer will triple scopes, symbols, references
|
||||
semantic_builder = semantic_builder.with_excess_capacity(2.0);
|
||||
}
|
||||
let semantic_ret = semantic_builder
|
||||
.with_trivias(trivias.clone())
|
||||
.with_check_syntax_error(true)
|
||||
.with_cfg(true)
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@ pub fn transform(
|
|||
|
||||
fn transpile(ctx: &TransformContext<'_>) -> CodegenReturn {
|
||||
let (symbols, scopes) = SemanticBuilder::new(ctx.source_text())
|
||||
// Estimate transformer will triple scopes, symbols, references
|
||||
.with_excess_capacity(2.0)
|
||||
.build(&ctx.program())
|
||||
.semantic
|
||||
.into_symbol_table_and_scope_tree();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ fn bench_transformer(criterion: &mut Criterion) {
|
|||
Parser::new(&allocator, source_text, source_type).parse();
|
||||
let program = allocator.alloc(program);
|
||||
let (symbols, scopes) = SemanticBuilder::new(source_text)
|
||||
// Estimate transformer will triple scopes, symbols, references
|
||||
.with_excess_capacity(2.0)
|
||||
.build(program)
|
||||
.semantic
|
||||
.into_symbol_table_and_scope_tree();
|
||||
|
|
|
|||
Loading…
Reference in a new issue