From d72954154bb06402e8b7e70b99a21b6194adc261 Mon Sep 17 00:00:00 2001 From: Hana Date: Tue, 19 Sep 2023 13:30:11 +0800 Subject: [PATCH] perf: parallelize swc js minify (#4213) perf: parallelize minify --- Cargo.lock | 1 + .../rspack_plugin_swc_js_minimizer/Cargo.toml | 1 + .../rspack_plugin_swc_js_minimizer/src/lib.rs | 128 +++++++++--------- .../src/minify.rs | 10 +- crates/rspack_util/src/lib.rs | 13 ++ 5 files changed, 82 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6fc138d6df0a..d7936e2a3bb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3020,6 +3020,7 @@ version = "0.1.0" dependencies = [ "async-recursion", "async-trait", + "rayon", "regex", "rspack_core", "rspack_error", diff --git a/crates/rspack_plugin_swc_js_minimizer/Cargo.toml b/crates/rspack_plugin_swc_js_minimizer/Cargo.toml index 8356eb8e30b8..0529bd7b1c8f 100644 --- a/crates/rspack_plugin_swc_js_minimizer/Cargo.toml +++ b/crates/rspack_plugin_swc_js_minimizer/Cargo.toml @@ -8,6 +8,7 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +rayon = { workspace = true } rspack_core = { path = "../rspack_core" } rspack_error = { path = "../rspack_error" } rspack_plugin_javascript = { path = "../rspack_plugin_javascript" } diff --git a/crates/rspack_plugin_swc_js_minimizer/src/lib.rs b/crates/rspack_plugin_swc_js_minimizer/src/lib.rs index eb3af0d26f89..84c3ca78ce4a 100644 --- a/crates/rspack_plugin_swc_js_minimizer/src/lib.rs +++ b/crates/rspack_plugin_swc_js_minimizer/src/lib.rs @@ -1,14 +1,10 @@ mod minify; -use std::{ - collections::HashMap, - hash::Hash, - sync::{mpsc, Mutex}, -}; +use std::{collections::HashMap, hash::Hash, sync::Mutex}; -use async_recursion::async_recursion; use async_trait::async_trait; use minify::{match_object, minify}; +use rayon::prelude::*; use rspack_core::{ rspack_sources::{ MapOptions, RawSource, SourceExt, SourceMap, SourceMapSource, SourceMapSourceOptions, @@ -16,9 +12,9 @@ use rspack_core::{ AssetInfo, CompilationAsset, JsChunkHashArgs, Plugin, PluginContext, PluginJsChunkHashHookOutput, PluginProcessAssetsOutput, ProcessAssetsArgs, }; -use rspack_error::{internal_error, Diagnostic}; +use rspack_error::{internal_error, Result}; use rspack_regex::RspackRegex; -use rspack_util::try_any; +use rspack_util::try_any_sync; use swc_config::config_types::BoolOrDataConfig; use swc_ecma_minifier::option::{ terser::{TerserCompressorOptions, TerserEcmaVersion}, @@ -47,8 +43,7 @@ pub enum SwcJsMinimizerRule { } impl SwcJsMinimizerRule { - #[async_recursion] - pub async fn try_match(&self, data: &str) -> rspack_error::Result { + pub fn try_match(&self, data: &str) -> rspack_error::Result { match self { Self::String(s) => Ok(data.starts_with(s)), Self::Regexp(r) => Ok(r.test(data)), @@ -64,12 +59,11 @@ pub enum SwcJsMinimizerRules { } impl SwcJsMinimizerRules { - #[async_recursion] - pub async fn try_match(&self, data: &str) -> rspack_error::Result { + pub fn try_match(&self, data: &str) -> rspack_error::Result { match self { Self::String(s) => Ok(data.starts_with(s)), Self::Regexp(r) => Ok(r.test(data)), - Self::Array(l) => try_any(l, |i| async { i.try_match(data).await }).await, + Self::Array(l) => try_any_sync(l, |i| i.try_match(data)), } } } @@ -105,7 +99,6 @@ impl Plugin for SwcJsMinimizerRspackPlugin { .as_ref() .is_some_and(|library| library.library_type == "module"); - let (tx, rx) = mpsc::channel::>(); // collect all extracted comments info let all_extracted_comments = Mutex::new(HashMap::new()); let extract_comments_option = &minify_options.extract_comments.clone(); @@ -136,68 +129,73 @@ impl Plugin for SwcJsMinimizerRspackPlugin { ..Default::default() }; - for (filename, original) in compilation.assets_mut() { + let result = compilation + .assets_mut() + .par_iter_mut() + .filter(|(filename, original)| { if !(filename.ends_with(".js") || filename.ends_with(".cjs") || filename.ends_with(".mjs")) { - continue; + return false } let is_matched = match_object(minify_options, filename) - .await .unwrap_or(false); if !is_matched || original.get_info().minimized { - continue; + return false } - if let Some(original_source) = original.get_source() { - let input = original_source.source().to_string(); - let input_source_map = original_source.map(&MapOptions::default()); - let js_minify_options = JsMinifyOptions { - compress: BoolOrDataConfig::from_obj(compress.clone()), - mangle: BoolOrDataConfig::from_obj(mangle.clone()), - format: format.clone(), - source_map: BoolOrDataConfig::from_bool(input_source_map.is_some()), - inline_sources_content: true, /* Using true so original_source can be None in SourceMapSource */ - emit_source_map_columns, - module: is_module, - ..Default::default() - }; + true + }) + .try_for_each(|(filename, original)| -> Result<()> { + if let Some(original_source) = original.get_source() { + let input = original_source.source().to_string(); + let input_source_map = original_source.map(&MapOptions::default()); + let js_minify_options = JsMinifyOptions { + compress: BoolOrDataConfig::from_obj(compress.clone()), + mangle: BoolOrDataConfig::from_obj(mangle.clone()), + format: format.clone(), + source_map: BoolOrDataConfig::from_bool(input_source_map.is_some()), + inline_sources_content: true, /* Using true so original_source can be None in SourceMapSource */ + emit_source_map_columns, + module: is_module, + ..Default::default() + }; + let output = match minify( + &js_minify_options, + input, + filename, + &all_extracted_comments, + extract_comments_option, + ) { + Ok(r) => r, + Err(e) => { + return Err(e) + } + }; + let source = if let Some(map) = &output.map { + SourceMapSource::new(SourceMapSourceOptions { + value: output.code, + name: filename, + source_map: SourceMap::from_json(map).map_err(|e| internal_error!(e.to_string()))?, + original_source: None, + inner_source_map: input_source_map, + remove_original_source: true, + }) + .boxed() + } else { + RawSource::from(output.code).boxed() + }; + original.set_source(Some(source)); + original.get_info_mut().minimized = true; + } - let output = match minify( - &js_minify_options, - input, - filename, - &all_extracted_comments, - extract_comments_option, - ) { - Ok(r) => r, - Err(e) => { - tx.send(e.into()) - .map_err(|e| internal_error!(e.to_string()))?; - continue; - } - }; - let source = if let Some(map) = &output.map { - SourceMapSource::new(SourceMapSourceOptions { - value: output.code, - name: filename, - source_map: SourceMap::from_json(map).map_err(|e| internal_error!(e.to_string()))?, - original_source: None, - inner_source_map: input_source_map, - remove_original_source: true, - }) - .boxed() - } else { - RawSource::from(output.code).boxed() - }; - original.set_source(Some(source)); - original.get_info_mut().minimized = true; - } - } + Ok(()) + }); - drop(tx); - - compilation.push_batch_diagnostic(rx.into_iter().flatten().collect::>()); + match result { + Ok(_) => {} + Err(e) => compilation.push_batch_diagnostic(e.into()), + }; // write all extracted comments to assets all_extracted_comments diff --git a/crates/rspack_plugin_swc_js_minimizer/src/minify.rs b/crates/rspack_plugin_swc_js_minimizer/src/minify.rs index 0f39e9f6e086..cc57fd709f28 100644 --- a/crates/rspack_plugin_swc_js_minimizer/src/minify.rs +++ b/crates/rspack_plugin_swc_js_minimizer/src/minify.rs @@ -3,7 +3,6 @@ use std::{ sync::{mpsc, Arc, Mutex}, }; -use async_recursion::async_recursion; use regex::Regex; use rspack_core::{ rspack_sources::{RawSource, SourceExt}, @@ -43,20 +42,19 @@ use swc_ecma_minifier::{ use crate::{JsMinifyCommentOption, JsMinifyOptions, SwcJsMinimizerRspackPluginOptions}; -#[async_recursion] -pub async fn match_object(obj: &SwcJsMinimizerRspackPluginOptions, str: &str) -> Result { +pub fn match_object(obj: &SwcJsMinimizerRspackPluginOptions, str: &str) -> Result { if let Some(condition) = &obj.test { - if !condition.try_match(str).await? { + if !condition.try_match(str)? { return Ok(false); } } if let Some(condition) = &obj.include { - if !condition.try_match(str).await? { + if !condition.try_match(str)? { return Ok(false); } } if let Some(condition) = &obj.exclude { - if condition.try_match(str).await? { + if condition.try_match(str)? { return Ok(false); } } diff --git a/crates/rspack_util/src/lib.rs b/crates/rspack_util/src/lib.rs index 5dcd3be0e848..fd252cce4eda 100644 --- a/crates/rspack_util/src/lib.rs +++ b/crates/rspack_util/src/lib.rs @@ -21,6 +21,19 @@ where Ok(false) } +pub fn try_any_sync(it: impl IntoIterator, f: F) -> Result +where + F: Fn(T) -> Result, +{ + let it = it.into_iter(); + for i in it { + if f(i)? { + return Ok(true); + } + } + Ok(false) +} + pub async fn try_all(it: impl IntoIterator, f: F) -> Result where Fut: Future>,