mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
34a242e134
Use `Ident` in the property mangler. This reduces the `Set::contains` operation faster as the hashing doesn't happen. The property names are not always an "ident" so it may not make sense to use the `Ident` type. But currently the `Ident` type allows characters that are not allowed in identifiers so the code works. I used AI to generate the code and then reviewed the code and iterated a bit.
164 lines
6.9 KiB
Rust
164 lines
6.9 KiB
Rust
use std::path::Path;
|
|
|
|
use oxc_allocator::Allocator;
|
|
use oxc_ast::ast::Program;
|
|
use oxc_benchmark::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
|
use oxc_mangler::{MangleOptions, MangleOptionsKeepNames, Mangler};
|
|
use oxc_minifier::{CompressOptions, Compressor, ManglePropertiesOptions, PropertyMangler};
|
|
use oxc_parser::Parser;
|
|
use oxc_semantic::SemanticBuilder;
|
|
use oxc_span::SourceType;
|
|
use oxc_tasks_common::TestFiles;
|
|
use oxc_transformer::{TransformOptions, Transformer};
|
|
|
|
fn bench_minifier(criterion: &mut Criterion) {
|
|
let mut group = criterion.benchmark_group("minifier");
|
|
|
|
for file in TestFiles::minimal().files().iter().skip(1) {
|
|
let id = BenchmarkId::from_parameter(&file.file_name);
|
|
let source_text = &file.source_text;
|
|
let source_type = file.source_type;
|
|
|
|
// Create `Allocator` outside of `bench_function`, so same allocator is used for
|
|
// both the warmup and measurement phases
|
|
let mut allocator = Allocator::default();
|
|
|
|
group.bench_function(id, |b| {
|
|
b.iter_with_setup_wrapper(|runner| {
|
|
// Reset allocator at start of each iteration
|
|
allocator.reset();
|
|
|
|
// Create fresh AST + semantic data for each iteration
|
|
let mut program = Parser::new(&allocator, source_text, source_type).parse().program;
|
|
let scoping = SemanticBuilder::new()
|
|
.with_enum_eval(true)
|
|
.build(&program)
|
|
.semantic
|
|
.into_scoping();
|
|
|
|
// Minifier only works on esnext.
|
|
let transform_options = TransformOptions::from_target("esnext").unwrap();
|
|
let transformer_ret =
|
|
Transformer::new(&allocator, Path::new(&file.file_name), &transform_options)
|
|
.build_with_scoping(scoping, &mut program);
|
|
assert!(transformer_ret.diagnostics.is_empty());
|
|
let scoping = SemanticBuilder::new().build(&program).semantic.into_scoping();
|
|
|
|
let options = CompressOptions::smallest();
|
|
runner.run(|| {
|
|
Compressor::new(&allocator).build_with_scoping(&mut program, scoping, options);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
/// Transform a parsed program to plain JavaScript (targeting esnext), the way the real pipeline
|
|
/// feeds the mangler: TypeScript is stripped before minification, so the mangler only ever runs
|
|
/// on JS - mangling an untransformed `.ts`/`.tsx` AST would not be representative. Returns the
|
|
/// transformed program; the caller builds semantic data from it and mangles.
|
|
fn transform_to_js<'a>(
|
|
allocator: &'a Allocator,
|
|
source_text: &'a str,
|
|
source_type: SourceType,
|
|
path: &Path,
|
|
) -> Program<'a> {
|
|
let mut program = Parser::new(allocator, source_text, source_type).parse().program;
|
|
let scoping =
|
|
SemanticBuilder::new().with_enum_eval(true).build(&program).semantic.into_scoping();
|
|
let transform_options = TransformOptions::from_target("esnext").unwrap();
|
|
let transformer_ret = Transformer::new(allocator, path, &transform_options)
|
|
.build_with_scoping(scoping, &mut program);
|
|
assert!(transformer_ret.diagnostics.is_empty());
|
|
program
|
|
}
|
|
|
|
fn bench_mangler(criterion: &mut Criterion) {
|
|
let mut group = criterion.benchmark_group("mangler");
|
|
for file in TestFiles::minimal().files() {
|
|
let id = BenchmarkId::from_parameter(&file.file_name);
|
|
let source_type = SourceType::from_path(&file.file_name).unwrap();
|
|
let source_text = file.source_text.as_str();
|
|
let path = Path::new(&file.file_name);
|
|
let mut allocator = Allocator::default();
|
|
let mut temp_allocator = Allocator::default();
|
|
group.bench_function(id, |b| {
|
|
b.iter_with_setup_wrapper(|runner| {
|
|
allocator.reset();
|
|
temp_allocator.reset();
|
|
let program = transform_to_js(&allocator, source_text, source_type, path);
|
|
let mut semantic =
|
|
SemanticBuilder::new().with_build_nodes(true).build(&program).semantic;
|
|
runner.run(|| {
|
|
Mangler::new_with_temp_allocator(&temp_allocator)
|
|
.build_with_semantic(&mut semantic, &program);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
{
|
|
let files = TestFiles::minimal();
|
|
let first_file = files.files().first().unwrap();
|
|
let id = BenchmarkId::from_parameter(format!("{}_keep_names", first_file.file_name));
|
|
let source_type = SourceType::from_path(&first_file.file_name).unwrap();
|
|
let source_text = first_file.source_text.as_str();
|
|
let path = Path::new(&first_file.file_name);
|
|
let mut allocator = Allocator::default();
|
|
let mut temp_allocator = Allocator::default();
|
|
group.bench_function(id, |b| {
|
|
b.iter_with_setup_wrapper(|runner| {
|
|
allocator.reset();
|
|
temp_allocator.reset();
|
|
let program = transform_to_js(&allocator, source_text, source_type, path);
|
|
let mut semantic =
|
|
SemanticBuilder::new().with_build_nodes(true).build(&program).semantic;
|
|
runner.run(|| {
|
|
Mangler::new_with_temp_allocator(&temp_allocator)
|
|
.with_options(MangleOptions {
|
|
top_level: None,
|
|
keep_names: MangleOptionsKeepNames::all_true(),
|
|
..MangleOptions::default()
|
|
})
|
|
.build_with_semantic(&mut semantic, &program);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn bench_property_mangler(criterion: &mut Criterion) {
|
|
let mut group = criterion.benchmark_group("property_mangler");
|
|
let options = ManglePropertiesOptions::from_pattern("(^_|_$)").expect("valid benchmark regex");
|
|
|
|
for file in TestFiles::minimal().files() {
|
|
let id = BenchmarkId::from_parameter(&file.file_name);
|
|
let source_type = SourceType::from_path(&file.file_name).unwrap();
|
|
let source_text = file.source_text.as_str();
|
|
let path = Path::new(&file.file_name);
|
|
let mut allocator = Allocator::default();
|
|
group.bench_function(id, |b| {
|
|
b.iter_with_setup_wrapper(|runner| {
|
|
allocator.reset();
|
|
let mut program = transform_to_js(&allocator, source_text, source_type, path);
|
|
runner.run(|| {
|
|
let mut mangler = PropertyMangler::new_in(options.clone(), &allocator);
|
|
mangler.collect(&program);
|
|
mangler.assign();
|
|
mangler.rewrite(&mut program);
|
|
std::hint::black_box(mangler.into_cache());
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(minifier, bench_minifier, bench_mangler, bench_property_mangler);
|
|
criterion_main!(minifier);
|