Skip to content

Commit

Permalink
feat(splitChunks): support splitChunks.minSizeReduction (#9341)
Browse files Browse the repository at this point in the history
* feat(splitChunks): support splitChunks.minSizeReduction

* Update website/docs/zh/plugins/webpack/split-chunks-plugin.mdx

Co-authored-by: neverland <[email protected]>

* Update website/docs/en/plugins/webpack/split-chunks-plugin.mdx

Co-authored-by: neverland <[email protected]>

* chore: correct descriptions

---------

Co-authored-by: neverland <[email protected]>
  • Loading branch information
JSerFeng and chenjiahan authored Feb 19, 2025
1 parent 708ce24 commit 15737ab
Show file tree
Hide file tree
Showing 13 changed files with 203 additions and 26 deletions.
2 changes: 2 additions & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,7 @@ export interface RawCacheGroupOptions {
automaticNameDelimiter?: string
minChunks?: number
minSize?: number | RawSplitChunkSizes
minSizeReduction?: number | RawSplitChunkSizes
maxSize?: number | RawSplitChunkSizes
maxAsyncSize?: number | RawSplitChunkSizes
maxInitialSize?: number | RawSplitChunkSizes
Expand Down Expand Up @@ -2230,6 +2231,7 @@ export interface RawSplitChunksOptions {
minChunks?: number
hidePathInfo?: boolean
minSize?: number | RawSplitChunkSizes
minSizeReduction?: number | RawSplitChunkSizes
enforceSizeThreshold?: number
minRemainingSize?: number | RawSplitChunkSizes
maxSize?: number | RawSplitChunkSizes
Expand Down
11 changes: 11 additions & 0 deletions crates/node_binding/src/raw_options/raw_split_chunks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub struct RawSplitChunksOptions {
pub min_chunks: Option<u32>,
pub hide_path_info: Option<bool>,
pub min_size: Option<Either<f64, RawSplitChunkSizes>>,
pub min_size_reduction: Option<Either<f64, RawSplitChunkSizes>>,
// pub min_size_reduction: usize,
pub enforce_size_threshold: Option<f64>,
pub min_remaining_size: Option<Either<f64, RawSplitChunkSizes>>,
Expand Down Expand Up @@ -84,6 +85,7 @@ pub struct RawCacheGroupOptions {
// pub max_initial_requests: usize,
pub min_chunks: Option<u32>,
pub min_size: Option<Either<f64, RawSplitChunkSizes>>,
pub min_size_reduction: Option<Either<f64, RawSplitChunkSizes>>,
// pub min_size_reduction: usize,
// pub enforce_size_threshold: usize,
// pub min_remaining_size: usize,
Expand Down Expand Up @@ -134,6 +136,8 @@ impl From<RawSplitChunksOptions> for rspack_plugin_split_chunks::PluginOptions {

let overall_min_size = create_sizes(raw_opts.min_size);

let overall_min_size_reduction = create_sizes(raw_opts.min_size_reduction);

let overall_max_size = create_sizes(raw_opts.max_size);

let overall_max_async_size = create_sizes(raw_opts.max_async_size).merge(&overall_max_size);
Expand All @@ -158,6 +162,12 @@ impl From<RawSplitChunksOptions> for rspack_plugin_split_chunks::PluginOptions {
&overall_min_size
});

let min_size_reduction = create_sizes(v.min_size_reduction).merge(if enforce {
&empty_sizes
} else {
&overall_min_size_reduction
});

let max_size = create_sizes(v.max_size);

let max_async_size = create_sizes(v.max_async_size)
Expand Down Expand Up @@ -214,6 +224,7 @@ impl From<RawSplitChunksOptions> for rspack_plugin_split_chunks::PluginOptions {
}),
min_chunks,
min_size,
min_size_reduction,
automatic_name_delimiter: v
.automatic_name_delimiter
.unwrap_or(overall_automatic_name_delimiter.clone()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub struct CacheGroup {
pub name: ChunkNameGetter,
pub priority: f64,
pub min_size: SplitChunkSizes,
pub min_size_reduction: SplitChunkSizes,
pub reuse_existing_chunk: bool,
/// number of referenced chunks
pub min_chunks: u32,
Expand Down
32 changes: 30 additions & 2 deletions crates/rspack_plugin_split_chunks/src/plugin/min_size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,33 @@ use std::ops::Deref;
use rspack_core::{Compilation, SourceType};

use super::ModuleGroupMap;
use crate::{module_group::ModuleGroup, CacheGroup, SplitChunksPlugin};
use crate::{module_group::ModuleGroup, CacheGroup, SplitChunkSizes, SplitChunksPlugin};

impl SplitChunksPlugin {
pub(crate) fn check_min_size_reduction(
sizes: &SplitChunkSizes,
min_size_reduction: &SplitChunkSizes,
chunk_count: usize,
) -> bool {
for (ty, min_reduction_size) in min_size_reduction.iter() {
if *min_reduction_size == 0.0f64 {
continue;
}

let Some(size) = sizes.get(ty) else {
continue;
};
if *size == 0.0f64 {
continue;
}
if size * (chunk_count as f64) < *min_reduction_size {
return false;
}
}

true
}

/// Return `true` if the `ModuleGroup` become empty.
pub(crate) fn remove_min_size_violating_modules(
module_group_key: &str,
Expand All @@ -14,7 +38,7 @@ impl SplitChunksPlugin {
cache_group: &CacheGroup,
) -> bool {
// Find out what `SourceType`'s size is not fit the min_size
let violating_source_types = module_group
let violating_source_types: Box<[SourceType]> = module_group
.sizes
.iter()
.filter_map(|(module_group_ty, module_group_ty_size)| {
Expand Down Expand Up @@ -95,6 +119,10 @@ impl SplitChunksPlugin {
compilation,
module_group,
cache_group,
) || !Self::check_min_size_reduction(
&module_group.sizes,
&cache_group.min_size_reduction,
module_group.chunks.len(),
) {
Some(module_group_key.clone())
} else {
Expand Down
3 changes: 2 additions & 1 deletion crates/rspack_plugin_split_chunks/src/plugin/module_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,8 @@ impl SplitChunksPlugin {
}

// Validate `min_size` again
if Self::remove_min_size_violating_modules(key, compilation, other_module_group, cache_group) {
if Self::remove_min_size_violating_modules(key, compilation, other_module_group, cache_group)
|| !Self::check_min_size_reduction(&other_module_group.sizes, &cache_group.min_size_reduction, other_module_group.chunks.len()) {
tracing::trace!(
"{key} is deleted for violating min_size {:#?}",
cache_group.min_size,
Expand Down
1 change: 1 addition & 0 deletions packages/rspack/etc/core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -10203,6 +10203,7 @@ type SharedOptimizationSplitChunksCacheGroup = {
name?: false | OptimizationSplitChunksName;
filename?: Filename;
minSize?: OptimizationSplitChunksSizes;
minSizeReduction?: OptimizationSplitChunksSizes;
maxSize?: OptimizationSplitChunksSizes;
maxAsyncSize?: OptimizationSplitChunksSizes;
maxInitialSize?: OptimizationSplitChunksSizes;
Expand Down
4 changes: 4 additions & 0 deletions packages/rspack/src/builtin-plugin/SplitChunksPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ function toRawSplitChunksOptions(
cacheGroups = {},
fallbackCacheGroup,
minSize,
minSizeReduction,
maxSize,
maxAsyncSize,
maxInitialSize,
Expand All @@ -112,6 +113,7 @@ function toRawSplitChunksOptions(
name,
chunks,
minSize,
minSizeReduction,
maxSize,
maxAsyncSize,
maxInitialSize,
Expand All @@ -123,6 +125,7 @@ function toRawSplitChunksOptions(
name: getName(name),
chunks: getChunks(chunks),
minSize: JsSplitChunkSizes.__to_binding(minSize),
minSizeReduction: JsSplitChunkSizes.__to_binding(minSizeReduction),
maxSize: JsSplitChunkSizes.__to_binding(maxSize),
maxAsyncSize: JsSplitChunkSizes.__to_binding(maxAsyncSize),
maxInitialSize: JsSplitChunkSizes.__to_binding(maxInitialSize),
Expand All @@ -135,6 +138,7 @@ function toRawSplitChunksOptions(
...fallbackCacheGroup
},
minSize: JsSplitChunkSizes.__to_binding(minSize),
minSizeReduction: JsSplitChunkSizes.__to_binding(minSizeReduction),
maxSize: JsSplitChunkSizes.__to_binding(maxSize),
maxAsyncSize: JsSplitChunkSizes.__to_binding(maxAsyncSize),
maxInitialSize: JsSplitChunkSizes.__to_binding(maxInitialSize),
Expand Down
2 changes: 2 additions & 0 deletions packages/rspack/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2162,6 +2162,8 @@ type SharedOptimizationSplitChunksCacheGroup = {
*/
minSize?: OptimizationSplitChunksSizes;

minSizeReduction?: OptimizationSplitChunksSizes;

/** Maximum size, in bytes, for a chunk to be generated. */
maxSize?: OptimizationSplitChunksSizes;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2397,16 +2397,18 @@ Rspack x.x.x compiled successfully"
`;
exports[`StatsTestCases should print correct stats for split-chunks-min-size-reduction 1`] = `
"Entrypoint main 9.58 KiB = default/main.js
"Entrypoint main 9.49 KiB = default/main.js
chunk (runtime: main) default/async-c.js (async-c) 50 bytes <{909}> ={44}= [rendered]
> ./c ./index.js 3:0-47
./c.js 50 bytes [built] [code generated]
chunk (runtime: main) default/async-a.js (async-a) 50 bytes <{909}> ={728}= [rendered]
chunk (runtime: main) default/async-a.js (async-a) 176 bytes <{909}> [rendered]
> ./a ./index.js 1:0-47
./a.js 50 bytes [built] [code generated]
chunk (runtime: main) default/async-b.js (async-b) 50 bytes <{909}> ={728}= [rendered]
./node_modules/shared.js?1 126 bytes [dependent] [built] [code generated]
chunk (runtime: main) default/async-b.js (async-b) 176 bytes <{909}> [rendered]
> ./b ./index.js 2:0-47
./b.js 50 bytes [built] [code generated]
./node_modules/shared.js?1 126 bytes [dependent] [built] [code generated]
chunk (runtime: main) default/async-e.js (async-e) 50 bytes <{909}> ={44}= [rendered]
> ./e ./index.js 5:0-47
./e.js 50 bytes [built] [code generated]
Expand All @@ -2418,11 +2420,7 @@ chunk (runtime: main) default/44.js (id hint: vendors) 126 bytes <{909}> ={172}=
chunk (runtime: main) default/async-d.js (async-d) 50 bytes <{909}> ={44}= [rendered]
> ./d ./index.js 4:0-47
./d.js 50 bytes [built] [code generated]
chunk (runtime: main) default/728.js (id hint: vendors) 126 bytes <{909}> ={250}= ={262}= [rendered] split chunk (cache group: defaultVendors)
> ./a ./index.js 1:0-47
> ./b ./index.js 2:0-47
./node_modules/shared.js?1 126 bytes [built] [code generated]
chunk (runtime: main) default/main.js (main) 245 bytes (javascript) 7.39 KiB (runtime) >{172}< >{250}< >{262}< >{407}< >{44}< >{602}< >{728}< [entry] [rendered]
chunk (runtime: main) default/main.js (main) 245 bytes (javascript) 7.39 KiB (runtime) >{172}< >{250}< >{262}< >{407}< >{44}< >{602}< [entry] [rendered]
> ./ main
./index.js 245 bytes [built] [code generated]
Rspack x.x.x compiled successfully"
Expand Down

This file was deleted.

6 changes: 1 addition & 5 deletions website/components/PluginSupportStatusTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,7 @@ const pluginSupportStatusList: PluginSupportStatus[] = [
{
name: 'SplitChunksPlugin',
url: '/plugins/webpack/split-chunks-plugin',
status: SupportStatus.PartiallySupported,
notes: {
en: '`minSizeReduction`, `usedExports` options not supported',
zh: '不支持 `minSizeReduction`、`usedExports` 选项',
},
status: SupportStatus.FullySupported,
},
{
name: 'WatchIgnorePlugin',
Expand Down
74 changes: 71 additions & 3 deletions website/docs/en/plugins/webpack/split-chunks-plugin.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,65 @@ Prevents exposing path info when creating names for parts splitted by maxSize.

### splitChunks.minSize

#### splitChunks.cacheGroups.\{cacheGroup\}.minSize

- **Type:** `number | Record<string, number>`
- **Default:** `20000` in production and `10000` in others

Minimum size, in bytes, for a chunk to be generated.
When using the `number` type of configuration, the same `minSize` will be configured for all module types defined in [`splitChunks.defaultSizeTypes`](/plugins/webpack/split-chunks-plugin#splitchunksdefaultsizetypes).

```js title="rspack.config.mjs"
export default {
//...
optimization: {
splitChunks: {
minSize: 100 * 1000,
},
},
};
```

When configured with the object form, different `minSize` can be set for different types of module types defined in `splitChunks.defaultSizeTypes`.

```js title="rspack.config.mjs"
export default {
//...
optimization: {
splitChunks: {
minSize: {
javascript: 100 * 1000,
css: 300 * 1000,
},
},
},
};
```

For example, the above configuration means that the minimum size of javascript modules in the split chunks needs to be at least 100KB, and the minimum size of css modules needs to be at least 300KB.

### splitChunks.minSizeReduction

#### splitChunks.cacheGroups.\{cacheGroup\}.minSizeReduction

- **Type: ** `number | Record<string, number>`
- **Default:** `0`

If there are several small modules in the build output, developers may not want to generate separate chunks for them even if their total size exceeds the `minSize` threshold. In this case, you can use the `minSizeReduction` parameter to set the minimum size reduction threshold required for module splitting.

The calculation rule for this parameter is: splitting will only occur when the total size reduction across all parent chunks after splitting the module is not less than the specified value.

Assuming the following scenario, suppose there is a 40KB module that is referenced by 2 chunks, and we set `minSizeReduction: 100`. If we were to split this module, each parent chunk would be reduced by 40KB, resulting in a total reduction of `40KB × 2 = 80KB`. As this is less than 100KB, the split will not be triggered.

```js title="rspack.config.mjs"
export default {
//...
optimization: {
splitChunks: {
minSizeReduction: 100 * 1000,
},
},
};
```

### splitChunks.maxSize

Expand Down Expand Up @@ -302,9 +357,22 @@ chunk baz

### splitChunks.defaultSizeTypes

- **Types:** `string[]`
- **Type:** `string[]`
- **Default:** `["javascript", "unknown"]`, and if `experiments.css` is enabled, it will also include `"css"`

When calculating the size of chunks, only the sizes of javascript modules and built-in css modules are taken into account by default. For example, when configuring `minSize: 300`, both javascript modules and css modules need to meet the requirement in order to be split.

Sets the size types which are used when a number is used for sizes.
You can configure additional module types, for example, if you want WebAssembly modules to be split as well:

```js title="rspack.config.mjs"
export default {
optimization: {
splitChunks: {
defaultSizeTypes: ['wasm', '...'],
},
},
};
```

### splitChunks.cacheGroups

Expand Down
Loading

2 comments on commit 15737ab

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented on 15737ab Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

Name Base (2025-02-19 a81bd20) Current Change
10000_big_production-mode_disable-minimize + exec 36.9 s ± 476 ms 37.9 s ± 480 ms +2.47 %
10000_development-mode + exec 1.76 s ± 20 ms 1.74 s ± 66 ms -1.38 %
10000_development-mode_hmr + exec 672 ms ± 7 ms 681 ms ± 27 ms +1.30 %
10000_production-mode + exec 2.23 s ± 54 ms 2.24 s ± 196 ms +0.56 %
10000_production-mode_persistent-cold + exec 2.37 s ± 98 ms 2.31 s ± 37 ms -2.26 %
10000_production-mode_persistent-hot + exec 1.63 s ± 48 ms 1.61 s ± 72 ms -1.01 %
arco-pro_development-mode + exec 1.76 s ± 60 ms 1.77 s ± 116 ms +0.78 %
arco-pro_development-mode_hmr + exec 386 ms ± 3.5 ms 384 ms ± 1.1 ms -0.28 %
arco-pro_production-mode + exec 3.67 s ± 70 ms 3.53 s ± 190 ms -3.79 %
arco-pro_production-mode_generate-package-json-webpack-plugin + exec 3.66 s ± 258 ms 3.56 s ± 169 ms -2.88 %
arco-pro_production-mode_persistent-cold + exec 3.71 s ± 205 ms 3.63 s ± 147 ms -2.36 %
arco-pro_production-mode_persistent-hot + exec 2.3 s ± 79 ms 2.37 s ± 152 ms +3.42 %
arco-pro_production-mode_traverse-chunk-modules + exec 3.56 s ± 131 ms 3.54 s ± 129 ms -0.66 %
large-dyn-imports_development-mode + exec 1.99 s ± 47 ms 1.98 s ± 45 ms -0.40 %
large-dyn-imports_production-mode + exec 2.04 s ± 31 ms 2.07 s ± 85 ms +1.06 %
threejs_development-mode_10x + exec 1.52 s ± 7.6 ms 1.55 s ± 13 ms +2.44 %
threejs_development-mode_10x_hmr + exec 774 ms ± 5.1 ms 816 ms ± 31 ms +5.50 %
threejs_production-mode_10x + exec 5.15 s ± 62 ms 5.16 s ± 98 ms +0.16 %
threejs_production-mode_10x_persistent-cold + exec 5.2 s ± 92 ms 5.28 s ± 298 ms +1.58 %
threejs_production-mode_10x_persistent-hot + exec 4.44 s ± 201 ms 4.44 s ± 27 ms -0.07 %
10000_big_production-mode_disable-minimize + rss memory 8666 MiB ± 41 MiB 8713 MiB ± 143 MiB +0.54 %
10000_development-mode + rss memory 647 MiB ± 23.7 MiB 656 MiB ± 13.4 MiB +1.46 %
10000_development-mode_hmr + rss memory 1326 MiB ± 157 MiB 1339 MiB ± 157 MiB +0.96 %
10000_production-mode + rss memory 617 MiB ± 8.92 MiB 644 MiB ± 16.6 MiB +4.49 %
10000_production-mode_persistent-cold + rss memory 724 MiB ± 23 MiB 741 MiB ± 23.8 MiB +2.36 %
10000_production-mode_persistent-hot + rss memory 690 MiB ± 28.4 MiB 726 MiB ± 30.6 MiB +5.36 %
arco-pro_development-mode + rss memory 566 MiB ± 42.3 MiB 590 MiB ± 38.8 MiB +4.17 %
arco-pro_development-mode_hmr + rss memory 654 MiB ± 47.4 MiB 655 MiB ± 94.6 MiB +0.19 %
arco-pro_production-mode + rss memory 716 MiB ± 21.3 MiB 731 MiB ± 38.2 MiB +1.96 %
arco-pro_production-mode_generate-package-json-webpack-plugin + rss memory 729 MiB ± 38.1 MiB 743 MiB ± 28.4 MiB +1.90 %
arco-pro_production-mode_persistent-cold + rss memory 798 MiB ± 61 MiB 800 MiB ± 73.7 MiB +0.30 %
arco-pro_production-mode_persistent-hot + rss memory 646 MiB ± 20.1 MiB 701 MiB ± 16.8 MiB +8.49 %
arco-pro_production-mode_traverse-chunk-modules + rss memory 724 MiB ± 42.3 MiB 742 MiB ± 22.4 MiB +2.56 %
large-dyn-imports_development-mode + rss memory 636 MiB ± 1.7 MiB 657 MiB ± 5.44 MiB +3.28 %
large-dyn-imports_production-mode + rss memory 519 MiB ± 4.18 MiB 546 MiB ± 12.6 MiB +5.15 %
threejs_development-mode_10x + rss memory 550 MiB ± 14.7 MiB 559 MiB ± 11.7 MiB +1.65 %
threejs_development-mode_10x_hmr + rss memory 1138 MiB ± 114 MiB 1123 MiB ± 84.5 MiB -1.35 %
threejs_production-mode_10x + rss memory 837 MiB ± 31.6 MiB 829 MiB ± 37.1 MiB -0.89 %
threejs_production-mode_10x_persistent-cold + rss memory 930 MiB ± 31.9 MiB 923 MiB ± 49.4 MiB -0.79 %
threejs_production-mode_10x_persistent-hot + rss memory 794 MiB ± 26.3 MiB 804 MiB ± 33.4 MiB +1.22 %

Threshold exceeded: ["threejs_development-mode_10x_hmr + exec"]

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented on 15737ab Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ecosystem CI detail: Open

suite result
modernjs ❌ failure
rspress ✅ success
rslib ❌ failure
rsbuild ❌ failure
rsdoctor ❌ failure
examples ✅ success
devserver ✅ success
nuxt ✅ success

Please sign in to comment.