Skip to content

Commit

Permalink
Merge pull request #15899 from webpack/bugfix/async-modules-performance
Browse files Browse the repository at this point in the history
fix quadratic evaluation performance of async modules
  • Loading branch information
sokra committed Jun 2, 2022
2 parents 096efc3 + 509a060 commit 520d314
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 63 deletions.
92 changes: 34 additions & 58 deletions lib/runtime/AsyncModuleRuntimeModule.js
Expand Up @@ -20,12 +20,13 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
const { runtimeTemplate } = this.compilation;
const fn = RuntimeGlobals.asyncModule;
return Template.asString([
'var webpackThen = typeof Symbol === "function" ? Symbol("webpack then") : "__webpack_then__";',
'var webpackQueues = typeof Symbol === "function" ? Symbol("webpack queues") : "__webpack_queues__";',
'var webpackExports = typeof Symbol === "function" ? Symbol("webpack exports") : "__webpack_exports__";',
'var webpackError = typeof Symbol === "function" ? Symbol("webpack error") : "__webpack_error__";',
`var completeQueue = ${runtimeTemplate.basicFunction("queue", [
"if(queue) {",
`var resolveQueue = ${runtimeTemplate.basicFunction("queue", [
"if(queue && !queue.d) {",
Template.indent([
"queue.d = 1;",
`queue.forEach(${runtimeTemplate.expressionFunction(
"fn.r--",
"fn"
Expand All @@ -37,91 +38,58 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
]),
"}"
])}`,
`var completeFunction = ${runtimeTemplate.expressionFunction(
"!--fn.r && fn()",
"fn"
)};`,
`var queueFunction = ${runtimeTemplate.expressionFunction(
"queue ? queue.push(fn) : completeFunction(fn)",
"queue, fn"
)};`,
`var wrapDeps = ${runtimeTemplate.returningFunction(
`deps.map(${runtimeTemplate.basicFunction("dep", [
'if(dep !== null && typeof dep === "object") {',
Template.indent([
"if(dep[webpackThen]) return dep;",
"if(dep[webpackQueues]) return dep;",
"if(dep.then) {",
Template.indent([
"var queue = [];",
"queue.d = 0;",
`dep.then(${runtimeTemplate.basicFunction("r", [
"obj[webpackExports] = r;",
"completeQueue(queue);",
"queue = 0;"
"resolveQueue(queue);"
])}, ${runtimeTemplate.basicFunction("e", [
"obj[webpackError] = e;",
"completeQueue(queue);",
"queue = 0;"
"resolveQueue(queue);"
])});`,
"var obj = {};",
`obj[webpackThen] = ${runtimeTemplate.expressionFunction(
"queueFunction(queue, fn), dep['catch'](reject)",
"fn, reject"
`obj[webpackQueues] = ${runtimeTemplate.expressionFunction(
`fn(queue)`,
"fn"
)};`,
"return obj;"
]),
"}"
]),
"}",
"var ret = {};",
`ret[webpackThen] = ${runtimeTemplate.expressionFunction(
"completeFunction(fn)",
"fn"
)};`,
`ret[webpackQueues] = ${runtimeTemplate.emptyFunction()};`,
"ret[webpackExports] = dep;",
"return ret;"
])})`,
"deps"
)};`,
`${fn} = ${runtimeTemplate.basicFunction("module, body, hasAwait", [
"var queue = hasAwait && [];",
"var queue;",
"hasAwait && ((queue = []).d = 1);",
"if(queue) queue.moduleId = module.id;",
"var depQueues = new Set();",
"var exports = module.exports;",
"var currentDeps;",
"var outerResolve;",
"var reject;",
"var isEvaluating = true;",
"var nested = false;",
`var whenAll = ${runtimeTemplate.basicFunction(
"deps, onResolve, onReject",
[
"if (nested) return;",
"nested = true;",
"onResolve.r += deps.length;",
`deps.map(${runtimeTemplate.expressionFunction(
"dep[webpackThen](onResolve, onReject)",
"dep, i"
)});`,
"nested = false;"
]
)};`,
`var promise = new Promise(${runtimeTemplate.basicFunction(
"resolve, rej",
[
"reject = rej;",
`outerResolve = ${runtimeTemplate.expressionFunction(
"resolve(exports), completeQueue(queue), queue = 0"
)};`
]
["reject = rej;", "outerResolve = resolve;"]
)});`,
"promise[webpackExports] = exports;",
`promise[webpackThen] = ${runtimeTemplate.basicFunction(
"fn, rejectFn",
[
"if (isEvaluating) { return completeFunction(fn); }",
"if (currentDeps) whenAll(currentDeps, fn, rejectFn);",
"queueFunction(queue, fn);",
"promise['catch'](rejectFn);"
]
`promise[webpackQueues] = ${runtimeTemplate.expressionFunction(
`queue && fn(queue), depQueues.forEach(fn), promise["catch"](${runtimeTemplate.emptyFunction()})`,
"fn"
)};`,
"promise.moduleId = module.id;",
"module.exports = promise;",
`body(${runtimeTemplate.basicFunction("deps", [
"currentDeps = wrapDeps(deps);",
Expand All @@ -133,21 +101,29 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
])})`
)}`,
`var promise = new Promise(${runtimeTemplate.basicFunction(
"resolve, reject",
"resolve",
[
`fn = ${runtimeTemplate.expressionFunction(
"resolve(getResult)"
"resolve(getResult)",
""
)};`,
"fn.r = 0;",
"whenAll(currentDeps, fn, reject);"
`var fnQueue = ${runtimeTemplate.expressionFunction(
"q !== queue && !depQueues.has(q) && (depQueues.add(q), q && !q.d && (fn.r++, q.push(fn)))",
"q"
)};`,
`currentDeps.map(${runtimeTemplate.expressionFunction(
"dep[webpackQueues](fnQueue)",
"dep"
)});`
]
)});`,
"return fn.r ? promise : getResult();"
])}, ${runtimeTemplate.expressionFunction(
"err && reject(promise[webpackError] = err), outerResolve()",
"(err ? reject(promise[webpackError] = err) : outerResolve(exports)), resolveQueue(queue)",
"err"
)});`,
"isEvaluating = false;"
"queue && (queue.d = 0);"
])};`
]);
}
Expand Down
10 changes: 5 additions & 5 deletions test/__snapshots__/StatsTestCases.basictest.js.snap
Expand Up @@ -4611,8 +4611,8 @@ webpack x.x.x compiled with 1 warning in X ms"
`;
exports[`StatsTestCases should print correct stats for wasm-explorer-examples-sync 1`] = `
"assets by path *.js 22.2 KiB
asset bundle.js 16.7 KiB [emitted] (name: main)
"assets by path *.js 21.8 KiB
asset bundle.js 16.3 KiB [emitted] (name: main)
asset 325.bundle.js 3.9 KiB [emitted]
asset 795.bundle.js 557 bytes [emitted]
asset 526.bundle.js 366 bytes [emitted] (id hint: vendors)
Expand All @@ -4628,8 +4628,8 @@ assets by path *.wasm 1.37 KiB
asset 0301cb3f9f4151b567f5.module.wasm 120 bytes [emitted] [immutable]
chunk (runtime: main) 20.bundle.js 50 bytes (javascript) 531 bytes (webassembly) [rendered]
./duff.wasm 50 bytes (javascript) 531 bytes (webassembly) [built] [code generated]
chunk (runtime: main) bundle.js (main) 586 bytes (javascript) 9.49 KiB (runtime) [entry] [rendered]
runtime modules 9.49 KiB 11 modules
chunk (runtime: main) bundle.js (main) 586 bytes (javascript) 9.19 KiB (runtime) [entry] [rendered]
runtime modules 9.19 KiB 11 modules
./index.js 586 bytes [built] [code generated]
chunk (runtime: main) 189.bundle.js 50 bytes (javascript) 156 bytes (webassembly) [rendered]
./Q_rsqrt.wasm 50 bytes (javascript) 156 bytes (webassembly) [built] [code generated]
Expand All @@ -4643,7 +4643,7 @@ chunk (runtime: main) 526.bundle.js (id hint: vendors) 34 bytes [rendered] split
chunk (runtime: main) 795.bundle.js 110 bytes (javascript) 444 bytes (webassembly) [rendered]
./fact.wasm 50 bytes (javascript) 154 bytes (webassembly) [built] [code generated]
./fast-math.wasm 60 bytes (javascript) 290 bytes (webassembly) [built] [code generated]
runtime modules 9.49 KiB 11 modules
runtime modules 9.19 KiB 11 modules
cacheable modules 2.31 KiB (javascript) 1.37 KiB (webassembly)
webassembly modules 310 bytes (javascript) 1.37 KiB (webassembly)
./Q_rsqrt.wasm 50 bytes (javascript) 156 bytes (webassembly) [built] [code generated]
Expand Down
2 changes: 2 additions & 0 deletions test/cases/async-modules/runtime-performance/async.js
@@ -0,0 +1,2 @@
await 1;
export default 1;
5 changes: 5 additions & 0 deletions test/cases/async-modules/runtime-performance/index.js
@@ -0,0 +1,5 @@
it("should not take too long to evaluate nested async modules", async () => {
const start = Date.now();
await import(/* webpackMode: "eager" */ "./loader.js?i=40!./loader.js");
expect(Date.now() - start).toBeLessThan(100);
});
14 changes: 14 additions & 0 deletions test/cases/async-modules/runtime-performance/loader.js
@@ -0,0 +1,14 @@
/** @type {import("../../../../").LoaderDefinition<{ i: string }>} */
module.exports = function () {
const options = this.getOptions();
const i = +options.i;
let src = `import n from "./async.js";\n`;
if (i > 0) {
src += `import a from "./loader.js?i=${i - 1}&a!./loader.js";\n`;
src += `import b from "./loader.js?i=${i - 1}&b!./loader.js";\n`;
src += `export default n + a + b;\n`;
} else {
src += `export default n;\n`;
}
return src;
};

0 comments on commit 520d314

Please sign in to comment.