diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b7f658d8d9a3..db07380077ca0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -306,6 +306,8 @@ jobs: - 'crates/oxc_ast/**' - 'crates/oxc_data_structures/**' - 'crates/oxc_parser/**' + - 'crates/oxc_minifier/**' + - 'crates/oxc_mangler/**' - 'crates/oxc_allocator/**' - 'tasks/allocs/**' diff --git a/Cargo.lock b/Cargo.lock index 5284c39f56d82..d67c70f2203cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2375,6 +2375,7 @@ dependencies = [ "humansize", "mimalloc-safe", "oxc_allocator", + "oxc_minifier", "oxc_parser", "oxc_tasks_common", ] diff --git a/tasks/track_memory_allocations/Cargo.toml b/tasks/track_memory_allocations/Cargo.toml index 8f2ab0497f4ea..1d8a443776d72 100644 --- a/tasks/track_memory_allocations/Cargo.toml +++ b/tasks/track_memory_allocations/Cargo.toml @@ -19,6 +19,7 @@ doctest = false [dependencies] oxc_allocator = { workspace = true, features = ["track_allocations"] } +oxc_minifier = { workspace = true } oxc_parser = { workspace = true } oxc_tasks_common = { workspace = true } diff --git a/tasks/track_memory_allocations/allocs_minifier.snap b/tasks/track_memory_allocations/allocs_minifier.snap new file mode 100644 index 0000000000000..f5a74a79bd2f5 --- /dev/null +++ b/tasks/track_memory_allocations/allocs_minifier.snap @@ -0,0 +1,14 @@ +File | File size || Sys allocs | Sys reallocs || Arena allocs | Arena reallocs | Arena bytes +------------------------------------------------------------------------------------------------------------------------------------------- +checker.ts | 2.92 MB || 100524 | 15486 || 152861 | 29447 | 5.609 MB + +cal.com.tsx | 1.06 MB || 52008 | 2586 || 40221 | 4695 | 1.667 MB + +RadixUIAdoptionSection.jsx | 2.52 kB || 87 | 4 || 20 | 6 | 688 B + +pdf.mjs | 567.30 kB || 19647 | 2899 || 47435 | 7813 | 1.628 MB + +antd.js | 4.12 MB || 117743 | 15151 || 275692 | 60674 | 14.776 MB + +binder.ts | 193.08 kB || 6267 | 1095 || 6999 | 833 | 199.952 kB + diff --git a/tasks/track_memory_allocations/src/lib.rs b/tasks/track_memory_allocations/src/lib.rs index cc9f3a9baa08b..739ab6afe78a3 100644 --- a/tasks/track_memory_allocations/src/lib.rs +++ b/tasks/track_memory_allocations/src/lib.rs @@ -1,5 +1,3 @@ -#![expect(clippy::print_stdout)] - use std::{ fs::File, io::{self, Write}, @@ -9,6 +7,7 @@ use humansize::{DECIMAL, format_size}; use mimalloc_safe::MiMalloc; use oxc_allocator::Allocator; +use oxc_minifier::{CompressOptions, MangleOptions, Minifier, MinifierOptions}; use oxc_parser::{ParseOptions, Parser}; use oxc_tasks_common::{TestFiles, project_root}; @@ -128,24 +127,36 @@ pub fn run() -> Result<(), io::Error> { // Table header, which should be same for each file let table_header = format_table_header(fixture_width, width); - let mut parser_out = table_header; + let mut parser_out = table_header.clone(); + let mut minifier_out = table_header; let mut allocator = Allocator::default(); - let options = ParseOptions { parse_regular_expression: true, ..ParseOptions::default() }; + let parse_options = ParseOptions { parse_regular_expression: true, ..ParseOptions::default() }; + let minifier_options = MinifierOptions { + mangle: Some(MangleOptions::default()), + compress: Some(CompressOptions::smallest()), + }; // Warm-up by parsing each file first, and then measuring the actual allocations. This reduces variance // in the number of allocations, because we ensure that the bump allocator has already requested all // of the space it will need from the system allocator to parse the largest file in the set. for file in files.files() { - Parser::new(&allocator, &file.source_text, file.source_type).with_options(options).parse(); + let mut parsed = Parser::new(&allocator, &file.source_text, file.source_type) + .with_options(parse_options) + .parse(); + Minifier::new(minifier_options.clone()).minify(&allocator, &mut parsed.program); } for file in files.files() { + let minifier_options = minifier_options.clone(); + allocator.reset(); reset_global_allocs(); - Parser::new(&allocator, &file.source_text, file.source_type).with_options(options).parse(); + let mut parsed = Parser::new(&allocator, &file.source_text, file.source_type) + .with_options(parse_options) + .parse(); let parser_stats = record_stats(&allocator); @@ -156,14 +167,24 @@ pub fn run() -> Result<(), io::Error> { fixture_width, width, )); - } - println!("{parser_out}"); + let before_minify_stats = record_stats(&allocator); - let mut snapshot = - File::create(project_root().join("tasks/track_memory_allocations/allocs_parser.snap"))?; - snapshot.write_all(parser_out.as_bytes())?; - snapshot.flush()?; + Minifier::new(minifier_options).minify(&allocator, &mut parsed.program); + + let minifier_stats = record_stats_diff(&allocator, &before_minify_stats); + + minifier_out.push_str(&format_table_row( + file.file_name.as_str(), + file.source_text.len(), + &minifier_stats, + fixture_width, + width, + )); + } + + write_snapshot("tasks/track_memory_allocations/allocs_parser.snap", &parser_out)?; + write_snapshot("tasks/track_memory_allocations/allocs_minifier.snap", &minifier_out)?; Ok(()) } @@ -181,6 +202,19 @@ fn record_stats(allocator: &Allocator) -> AllocatorStats { AllocatorStats { sys_allocs, sys_reallocs, arena_allocs, arena_reallocs, arena_bytes } } +/// Record current allocation stats since the last recorded stats in `prev`. This is useful +/// for measuring allocations made during a specific operation without needing to reset the stats. +fn record_stats_diff(allocator: &Allocator, prev: &AllocatorStats) -> AllocatorStats { + let stats = record_stats(allocator); + AllocatorStats { + sys_allocs: stats.sys_allocs.saturating_sub(prev.sys_allocs), + sys_reallocs: stats.sys_reallocs.saturating_sub(prev.sys_reallocs), + arena_allocs: stats.arena_allocs.saturating_sub(prev.arena_allocs), + arena_reallocs: stats.arena_reallocs.saturating_sub(prev.arena_reallocs), + arena_bytes: stats.arena_bytes.saturating_sub(prev.arena_bytes), + } +} + /// Formats a single row of the allocator stats table fn format_table_row( file_name: &str, @@ -220,3 +254,10 @@ fn format_table_header(fixture_width: usize, width: usize) -> String { out.push('\n'); out } + +fn write_snapshot(file_path: &str, contents: &str) -> Result<(), io::Error> { + let mut snapshot = File::create(project_root().join(file_path))?; + snapshot.write_all(contents.as_bytes())?; + snapshot.flush()?; + Ok(()) +}