We're seeing a threading and concurrency problem trying to upgrade our CC webpack plugin.
We've moved from the old v3 version:
https://www.npmjs.com/package/webpack-closure-compiler
To the latest v4 plugin:
https://www.npmjs.com/package/closure-webpack-plugin
We're seeing a pretty wild performance degradation here. The previous v3 version used a tuneable concurrency flag to limit the number of parallel compilations performed by the plugin:
https://github.com/roman01la/webpack-closure-compiler/blob/3677e5e6672af68eb4608491d39d9e39ba8bf55e/index.js#L99
This would use async.queue to only execute CC at a specified concurrency.
Testing the new plugin with the default configuration from the docs we see a separate thread spun for every chunk in our webpack compilation. We saw thousands of java threads, and knocked down our development environments.
For additional detail, we tested some different combinations of options. Using any of the platform options does not affect the result. We can use platform: javascript and then still see hundreds of node threads, as opposed to JVM threads.
We also tested switching the mode to AGRESSIVE_BUNDLE. When we run in AGRESSIVE mode though, we do not see more than one compilation at a time. Our compilation is not compatible with that option, but it is worth noting that the problem does not happen when using the AGRESSIVE option.
On investigation, it seems like the cause of this explosion in threads might be a bug. The code bifurcates on the bundling type:
|
if (this.options.mode === 'AGGRESSIVE_BUNDLE') { |
|
this.aggressiveBundle(compilation, originalChunks, cb); |
|
} else { |
|
this.standardBundle(compilation, originalChunks, cb); |
|
} |
In the standard codepath, there is a forEach over each chunk in the compilation:
|
originalChunks.forEach((chunk) => { |
which pushes the chunk onto a compilations list:
|
compilations.push( |
|
this.runCompiler(compilation, compilationOptions, sources, chunkDefs) |
And then
Promise.alls them:
|
Promise.all(compilations) |
But the AGRESSIVE code path only does a single compilation promise:
|
this.runCompiler(compilation, compilationOptions, allSources) |
|
.then((outputFiles) => { |
We suspect this explains the explosion in background work, and the difference in behavior between STANDARD and AGRESSIVE.
We're running webpack v4 on node 12 with an otherwise uninteresting configuration. 270 entrypoints, 2500 modules, and about 300 emitted chunks.
We're seeing a threading and concurrency problem trying to upgrade our CC webpack plugin.
We've moved from the old v3 version:
https://www.npmjs.com/package/webpack-closure-compiler
To the latest v4 plugin:
https://www.npmjs.com/package/closure-webpack-plugin
We're seeing a pretty wild performance degradation here. The previous v3 version used a tuneable
concurrencyflag to limit the number of parallel compilations performed by the plugin:https://github.com/roman01la/webpack-closure-compiler/blob/3677e5e6672af68eb4608491d39d9e39ba8bf55e/index.js#L99
This would use
async.queueto only execute CC at a specified concurrency.Testing the new plugin with the default configuration from the docs we see a separate thread spun for every chunk in our webpack compilation. We saw thousands of java threads, and knocked down our development environments.
For additional detail, we tested some different combinations of options. Using any of the
platformoptions does not affect the result. We can useplatform: javascriptand then still see hundreds of node threads, as opposed to JVM threads.We also tested switching the
modetoAGRESSIVE_BUNDLE. When we run in AGRESSIVE mode though, we do not see more than one compilation at a time. Our compilation is not compatible with that option, but it is worth noting that the problem does not happen when using the AGRESSIVE option.On investigation, it seems like the cause of this explosion in threads might be a bug. The code bifurcates on the bundling type:
closure-webpack-plugin/src/closure-compiler-plugin.js
Lines 448 to 452 in 1246f6e
In the standard codepath, there is a
forEachover each chunk in the compilation:closure-webpack-plugin/src/closure-compiler-plugin.js
Line 467 in 1246f6e
which pushes the chunk onto a compilations list:
closure-webpack-plugin/src/closure-compiler-plugin.js
Lines 497 to 498 in 1246f6e
And then
Promise.alls them:closure-webpack-plugin/src/closure-compiler-plugin.js
Line 553 in 1246f6e
But the AGRESSIVE code path only does a single compilation promise:
closure-webpack-plugin/src/closure-compiler-plugin.js
Lines 776 to 777 in 1246f6e
We suspect this explains the explosion in background work, and the difference in behavior between STANDARD and AGRESSIVE.
We're running webpack v4 on node 12 with an otherwise uninteresting configuration. 270 entrypoints, 2500 modules, and about 300 emitted chunks.