From 4845915c0b9b52f841c75b066a08ac7ad69191ee Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 26 May 2021 15:53:23 +0200 Subject: [PATCH 1/5] Lazy require some modules --- lib/config/target.js | 9 +- lib/serialization/BinaryMiddleware.js | 3 + lib/stats/DefaultStatsFactoryPlugin.js | 9 +- lib/util/serialization.js | 167 ++++++++++++++++--------- types.d.ts | 10 +- 5 files changed, 128 insertions(+), 70 deletions(-) diff --git a/lib/config/target.js b/lib/config/target.js index 60c543c602f..595b8d476f0 100644 --- a/lib/config/target.js +++ b/lib/config/target.js @@ -5,14 +5,18 @@ "use strict"; -const browserslistTargetHandler = require("./browserslistTargetHandler"); +const memoize = require("../util/memoize"); + +const getBrowserslistTargetHandler = memoize(() => + require("./browserslistTargetHandler") +); /** * @param {string} context the context directory * @returns {string} default target */ const getDefaultTarget = context => { - const browsers = browserslistTargetHandler.load(null, context); + const browsers = getBrowserslistTargetHandler().load(null, context); return browsers ? "browserslist" : "web"; }; @@ -78,6 +82,7 @@ const TARGETS = [ "Resolve features from browserslist. Will resolve browserslist config automatically. Only browser or node queries are supported (electron is not supported). Examples: 'browserslist:modern' to use 'modern' environment from browserslist config", /^browserslist(?::(.+))?$/, (rest, context) => { + const browserslistTargetHandler = getBrowserslistTargetHandler(); const browsers = browserslistTargetHandler.load( rest ? rest.trim() : null, context diff --git a/lib/serialization/BinaryMiddleware.js b/lib/serialization/BinaryMiddleware.js index 49dc59040d1..e95d0f35998 100644 --- a/lib/serialization/BinaryMiddleware.js +++ b/lib/serialization/BinaryMiddleware.js @@ -100,6 +100,9 @@ const F64_SIZE = 8; const MEASURE_START_OPERATION = Symbol("MEASURE_START_OPERATION"); const MEASURE_END_OPERATION = Symbol("MEASURE_END_OPERATION"); +/** @typedef {typeof MEASURE_START_OPERATION} MEASURE_START_OPERATION_TYPE */ +/** @typedef {typeof MEASURE_END_OPERATION} MEASURE_END_OPERATION_TYPE */ + const identifyNumber = n => { if (n === (n | 0)) { if (n <= 127 && n >= -128) return 0; diff --git a/lib/stats/DefaultStatsFactoryPlugin.js b/lib/stats/DefaultStatsFactoryPlugin.js index 79e87e1e050..f7ae524eae3 100644 --- a/lib/stats/DefaultStatsFactoryPlugin.js +++ b/lib/stats/DefaultStatsFactoryPlugin.js @@ -10,7 +10,6 @@ const ModuleDependency = require("../dependencies/ModuleDependency"); const formatLocation = require("../formatLocation"); const { LogType } = require("../logging/Logger"); const AggressiveSplittingPlugin = require("../optimize/AggressiveSplittingPlugin"); -const ConcatenatedModule = require("../optimize/ConcatenatedModule"); const SizeLimitsPlugin = require("../performance/SizeLimitsPlugin"); const { countIterable } = require("../util/IterableHelpers"); const { @@ -1227,11 +1226,13 @@ const SIMPLE_EXTRACTORS = { }, nestedModules: (object, module, context, options, factory) => { const { type } = context; - if (module instanceof ConcatenatedModule) { - const modules = module.modules; + const innerModules = /** @type {Module & { modules?: Module[] }} */ ( + module + ).modules; + if (Array.isArray(innerModules)) { const groupedModules = factory.create( `${type.slice(0, -8)}.modules`, - modules, + innerModules, context ); const limited = spaceLimited( diff --git a/lib/util/serialization.js b/lib/util/serialization.js index ba34b3dd1c0..46d97d85172 100644 --- a/lib/util/serialization.js +++ b/lib/util/serialization.js @@ -4,72 +4,121 @@ "use strict"; -const BinaryMiddleware = require("../serialization/BinaryMiddleware"); -const FileMiddleware = require("../serialization/FileMiddleware"); -const ObjectMiddleware = require("../serialization/ObjectMiddleware"); -const Serializer = require("../serialization/Serializer"); -const SerializerMiddleware = require("../serialization/SerializerMiddleware"); -const SingleItemMiddleware = require("../serialization/SingleItemMiddleware"); -const internalSerializables = require("./internalSerializables"); +const memoize = require("./memoize"); +/** @typedef {import("../serialization/BinaryMiddleware").MEASURE_END_OPERATION_TYPE} MEASURE_END_OPERATION */ +/** @typedef {import("../serialization/BinaryMiddleware").MEASURE_START_OPERATION_TYPE} MEASURE_START_OPERATION */ /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */ /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */ +/** @typedef {import("../serialization/Serializer")} Serializer */ -const { register, registerLoader, registerNotSerializable } = ObjectMiddleware; +const getBinaryMiddleware = memoize(() => + require("../serialization/BinaryMiddleware") +); +const getObjectMiddleware = memoize(() => + require("../serialization/ObjectMiddleware") +); +const getSingleItemMiddleware = memoize(() => + require("../serialization/SingleItemMiddleware") +); +const getSerializer = memoize(() => require("../serialization/Serializer")); +const getSerializerMiddleware = memoize(() => + require("../serialization/SerializerMiddleware") +); -const binaryMiddleware = new BinaryMiddleware(); +const getBinaryMiddlewareInstance = memoize( + () => new (getBinaryMiddleware())() +); -// Expose serialization API -exports.register = register; -exports.registerLoader = registerLoader; -exports.registerNotSerializable = registerNotSerializable; -exports.NOT_SERIALIZABLE = ObjectMiddleware.NOT_SERIALIZABLE; -exports.MEASURE_START_OPERATION = BinaryMiddleware.MEASURE_START_OPERATION; -exports.MEASURE_END_OPERATION = BinaryMiddleware.MEASURE_END_OPERATION; -exports.buffersSerializer = new Serializer([ - new SingleItemMiddleware(), - new ObjectMiddleware(context => { - if (context.write) { - context.writeLazy = value => { - context.write(SerializerMiddleware.createLazy(value, binaryMiddleware)); - }; +const registerSerializers = memoize(() => { + require("./registerExternalSerializer"); + + // Load internal paths with a relative require + // This allows bundling all internal serializers + const internalSerializables = require("./internalSerializables"); + getObjectMiddleware().registerLoader(/^webpack\/lib\//, req => { + const loader = internalSerializables[req.slice("webpack/lib/".length)]; + if (loader) { + loader(); + } else { + console.warn(`${req} not found in internalSerializables`); } - }), - binaryMiddleware -]); -exports.createFileSerializer = fs => { - const fileMiddleware = new FileMiddleware(fs); - return new Serializer([ - new SingleItemMiddleware(), - new ObjectMiddleware(context => { - if (context.write) { - context.writeLazy = value => { - context.write( - SerializerMiddleware.createLazy(value, binaryMiddleware) - ); - }; - context.writeSeparate = (value, options) => { - context.write( - SerializerMiddleware.createLazy(value, fileMiddleware, options) - ); - }; - } - }), - binaryMiddleware, - fileMiddleware - ]); -}; + return true; + }); +}); -require("./registerExternalSerializer"); +/** @type {Serializer} */ +let buffersSerializer; -// Load internal paths with a relative require -// This allows bundling all internal serializers -registerLoader(/^webpack\/lib\//, req => { - const loader = internalSerializables[req.slice("webpack/lib/".length)]; - if (loader) { - loader(); - } else { - console.warn(`${req} not found in internalSerializables`); +// Expose serialization API +module.exports = { + get register() { + return getObjectMiddleware().register; + }, + get registerLoader() { + return getObjectMiddleware().registerLoader; + }, + get registerNotSerializable() { + return getObjectMiddleware().registerNotSerializable; + }, + get NOT_SERIALIZABLE() { + return getObjectMiddleware().NOT_SERIALIZABLE; + }, + /** @type {MEASURE_START_OPERATION} */ + get MEASURE_START_OPERATION() { + return getBinaryMiddleware().MEASURE_START_OPERATION; + }, + /** @type {MEASURE_END_OPERATION} */ + get MEASURE_END_OPERATION() { + return getBinaryMiddleware().MEASURE_END_OPERATION; + }, + get buffersSerializer() { + if (buffersSerializer !== undefined) return buffersSerializer; + registerSerializers(); + const Serializer = getSerializer(); + const binaryMiddleware = getBinaryMiddlewareInstance(); + const SerializerMiddleware = getSerializerMiddleware(); + const SingleItemMiddleware = getSingleItemMiddleware(); + return (buffersSerializer = new Serializer([ + new SingleItemMiddleware(), + new (getObjectMiddleware())(context => { + if (context.write) { + context.writeLazy = value => { + context.write( + SerializerMiddleware.createLazy(value, binaryMiddleware) + ); + }; + } + }), + binaryMiddleware + ])); + }, + createFileSerializer: fs => { + registerSerializers(); + const Serializer = getSerializer(); + const FileMiddleware = require("../serialization/FileMiddleware"); + const fileMiddleware = new FileMiddleware(fs); + const binaryMiddleware = getBinaryMiddlewareInstance(); + const SerializerMiddleware = getSerializerMiddleware(); + const SingleItemMiddleware = getSingleItemMiddleware(); + return new Serializer([ + new SingleItemMiddleware(), + new (getObjectMiddleware())(context => { + if (context.write) { + context.writeLazy = value => { + context.write( + SerializerMiddleware.createLazy(value, binaryMiddleware) + ); + }; + context.writeSeparate = (value, options) => { + context.write( + SerializerMiddleware.createLazy(value, fileMiddleware, options) + ); + }; + } + }), + binaryMiddleware, + fileMiddleware + ]); } - return true; -}); +}; diff --git a/types.d.ts b/types.d.ts index ddd7fa69cc6..639ee8ff19d 100644 --- a/types.d.ts +++ b/types.d.ts @@ -12099,19 +12099,19 @@ declare namespace exports { ) => 0 | 1 | -1; } export namespace serialization { - export let register: ( + export const register: ( Constructor: Constructor, request: string, name: string, serializer: ObjectSerializer ) => void; - export let registerLoader: ( + export const registerLoader: ( regExp: RegExp, loader: (arg0: string) => boolean ) => void; - export let registerNotSerializable: (Constructor: Constructor) => void; - export let NOT_SERIALIZABLE: object; - export let buffersSerializer: Serializer; + export const registerNotSerializable: (Constructor: Constructor) => void; + export const NOT_SERIALIZABLE: object; + export const buffersSerializer: Serializer; export let createFileSerializer: (fs?: any) => Serializer; export { MEASURE_START_OPERATION, MEASURE_END_OPERATION }; } From f6b5bc54fe4ce641997b70baffef6d25453f70f4 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 26 May 2021 15:53:56 +0200 Subject: [PATCH 2/5] Create entry dependency only once for better unsafe caching --- lib/EntryPlugin.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/EntryPlugin.js b/lib/EntryPlugin.js index 054bc36d35d..2e36aeaef0e 100644 --- a/lib/EntryPlugin.js +++ b/lib/EntryPlugin.js @@ -41,10 +41,10 @@ class EntryPlugin { } ); - compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => { - const { entry, options, context } = this; + const { entry, options, context } = this; + const dep = EntryPlugin.createDependency(entry, options); - const dep = EntryPlugin.createDependency(entry, options); + compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => { compilation.addEntry(context, dep, options, err => { callback(err); }); From cc5890163a8f1463b784afeda34e615221c084bf Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 26 May 2021 15:58:53 +0200 Subject: [PATCH 3/5] improve ExportsInfo sorting performance by keeping Map size to avoid reorganizing --- lib/ExportsInfo.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/ExportsInfo.js b/lib/ExportsInfo.js index ccac67f347b..b8a726618eb 100644 --- a/lib/ExportsInfo.js +++ b/lib/ExportsInfo.js @@ -137,18 +137,21 @@ class ExportsInfo { _sortExportsMap(exports) { if (exports.size > 1) { - const entriesInOrder = Array.from(exports.values()); - if ( - entriesInOrder.length !== 2 || - entriesInOrder[0].name > entriesInOrder[1].name - ) { - entriesInOrder.sort((a, b) => { - return a.name < b.name ? -1 : 1; - }); - exports.clear(); - for (const entry of entriesInOrder) { - exports.set(entry.name, entry); - } + const namesInOrder = []; + for (const entry of exports.values()) { + namesInOrder.push(entry.name); + } + namesInOrder.sort(); + let i = 0; + for (const entry of exports.values()) { + const name = namesInOrder[i]; + if (entry.name !== name) break; + } + for (; i < namesInOrder.length; i++) { + const name = namesInOrder[i]; + const correctEntry = exports.get(name); + exports.delete(name); + exports.set(name, correctEntry); } } } From bb6040b07fbdf2d9b11d0dc1e946d024400be158 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 26 May 2021 16:01:01 +0200 Subject: [PATCH 4/5] (re)store provided exports sorted --- lib/ExportsInfo.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ExportsInfo.js b/lib/ExportsInfo.js index b8a726618eb..12fd230b986 100644 --- a/lib/ExportsInfo.js +++ b/lib/ExportsInfo.js @@ -725,7 +725,7 @@ class ExportsInfo { const otherCanMangleProvide = this._otherExportsInfo.canMangleProvide; const otherTerminalBinding = this._otherExportsInfo.terminalBinding; const exports = []; - for (const exportInfo of this._exports.values()) { + for (const exportInfo of this.orderedExports) { if ( exportInfo.provided !== otherProvided || exportInfo.canMangleProvide !== otherCanMangleProvide || @@ -757,7 +757,9 @@ class ExportsInfo { otherTerminalBinding, exports }) { + let wasEmpty = true; for (const exportInfo of this._exports.values()) { + wasEmpty = false; exportInfo.provided = otherProvided; exportInfo.canMangleProvide = otherCanMangleProvide; exportInfo.terminalBinding = otherTerminalBinding; @@ -775,6 +777,7 @@ class ExportsInfo { exportsInfo.restoreProvided(exp.exportsInfo); } } + if (wasEmpty) this._exportsAreOrdered = true; } } From b1b9ef34a9585af65fd1fd6c7599d3b628627146 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 26 May 2021 16:02:49 +0200 Subject: [PATCH 5/5] cache buffer in local var --- lib/util/createHash.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/util/createHash.js b/lib/util/createHash.js index bbabc61306c..81cfa0a221c 100644 --- a/lib/util/createHash.js +++ b/lib/util/createHash.js @@ -67,6 +67,7 @@ class BulkUpdateDecorator extends Hash { */ digest(encoding) { let digestCache; + const buffer = this.buffer; if (this.hash === undefined) { // short data for hash, we can use caching const cacheKey = `${this.hashKey}-${encoding}`; @@ -74,18 +75,18 @@ class BulkUpdateDecorator extends Hash { if (digestCache === undefined) { digestCache = digestCaches[cacheKey] = new Map(); } - const cacheEntry = digestCache.get(this.buffer); + const cacheEntry = digestCache.get(buffer); if (cacheEntry !== undefined) return cacheEntry; this.hash = this.hashFactory(); } - if (this.buffer.length > 0) { - this.hash.update(this.buffer); + if (buffer.length > 0) { + this.hash.update(buffer); } const digestResult = this.hash.digest(encoding); const result = typeof digestResult === "string" ? digestResult : digestResult.toString(); if (digestCache !== undefined) { - digestCache.set(this.buffer, result); + digestCache.set(buffer, result); } return result; }