Skip to content

Commit 677eaba

Browse files
authored
Merge pull request webpack#7137 from webpack/performance/multiple-changes
Performance improvements
2 parents ec3fa8b + 64925a8 commit 677eaba

8 files changed

+321
-113
lines changed

lib/Compilation.js

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,61 @@ class Compilation extends Tapable {
11681168
const chunkDependencies = new Map(); // Map<Chunk, Array<{Module, Chunk}>>
11691169
const allCreatedChunkGroups = new Set();
11701170

1171+
// PREPARE
1172+
const blockInfoMap = new Map();
1173+
1174+
const iteratorDependency = d => {
1175+
// We skip Dependencies without Reference
1176+
const ref = d.getReference();
1177+
if (!ref) {
1178+
return;
1179+
}
1180+
// We skip Dependencies without Module pointer
1181+
const refModule = ref.module;
1182+
if (!refModule) {
1183+
return;
1184+
}
1185+
// We skip weak Dependencies
1186+
if (ref.weak) {
1187+
return;
1188+
}
1189+
1190+
blockInfoModules.add(refModule);
1191+
};
1192+
1193+
const iteratorBlockPrepare = b => {
1194+
blockInfoBlocks.push(b);
1195+
blockQueue.push(b);
1196+
};
1197+
1198+
let block, blockQueue, blockInfoModules, blockInfoBlocks;
1199+
for (const module of this.modules) {
1200+
blockQueue = [module];
1201+
while (blockQueue.length > 0) {
1202+
block = blockQueue.pop();
1203+
blockInfoModules = new Set();
1204+
blockInfoBlocks = [];
1205+
1206+
if (block.variables) {
1207+
iterationBlockVariable(block.variables, iteratorDependency);
1208+
}
1209+
1210+
if (block.dependencies) {
1211+
iterationOfArrayCallback(block.dependencies, iteratorDependency);
1212+
}
1213+
1214+
if (block.blocks) {
1215+
iterationOfArrayCallback(block.blocks, iteratorBlockPrepare);
1216+
}
1217+
1218+
const blockInfo = {
1219+
modules: blockInfoModules,
1220+
blocks: blockInfoBlocks
1221+
};
1222+
blockInfoMap.set(block, blockInfo);
1223+
}
1224+
}
1225+
11711226
// PART ONE
11721227

11731228
const blockChunkGroups = new Map();
@@ -1180,7 +1235,7 @@ class Compilation extends Tapable {
11801235
chunkGroup
11811236
}));
11821237

1183-
let module, block, chunk, chunkGroup;
1238+
let module, chunk, chunkGroup;
11841239

11851240
// For each async Block in graph
11861241
const iteratorBlock = b => {
@@ -1227,36 +1282,6 @@ class Compilation extends Tapable {
12271282
});
12281283
};
12291284

1230-
// For each Dependency in the graph
1231-
const iteratorDependency = d => {
1232-
// We skip Dependencies without Reference
1233-
const ref = d.getReference();
1234-
if (!ref) {
1235-
return;
1236-
}
1237-
// We skip Dependencies without Module pointer
1238-
const refModule = ref.module;
1239-
if (!refModule) {
1240-
return;
1241-
}
1242-
// We skip weak Dependencies
1243-
if (ref.weak) {
1244-
return;
1245-
}
1246-
// We connect Module and Chunk when not already done
1247-
if (chunk.addModule(refModule)) {
1248-
refModule.addChunk(chunk);
1249-
1250-
// And enqueue the Module for traversal
1251-
queue.push({
1252-
block: refModule,
1253-
module: refModule,
1254-
chunk,
1255-
chunkGroup
1256-
});
1257-
}
1258-
};
1259-
12601285
// Iterative traversal of the Module graph
12611286
// Recursive would be simpler to write but could result in Stack Overflows
12621287
while (queue.length) {
@@ -1266,18 +1291,27 @@ class Compilation extends Tapable {
12661291
chunk = queueItem.chunk;
12671292
chunkGroup = queueItem.chunkGroup;
12681293

1269-
// Traverse all variables, Dependencies and Blocks
1270-
if (block.variables) {
1271-
iterationBlockVariable(block.variables, iteratorDependency);
1272-
}
1273-
1274-
if (block.dependencies) {
1275-
iterationOfArrayCallback(block.dependencies, iteratorDependency);
1294+
// get prepared block info
1295+
const blockInfo = blockInfoMap.get(block);
1296+
1297+
// Traverse all referenced modules
1298+
for (const refModule of blockInfo.modules) {
1299+
// We connect Module and Chunk when not already done
1300+
if (chunk.addModule(refModule)) {
1301+
refModule.addChunk(chunk);
1302+
1303+
// And enqueue the Module for traversal
1304+
queue.push({
1305+
block: refModule,
1306+
module: refModule,
1307+
chunk,
1308+
chunkGroup
1309+
});
1310+
}
12761311
}
12771312

1278-
if (block.blocks) {
1279-
iterationOfArrayCallback(block.blocks, iteratorBlock);
1280-
}
1313+
// Traverse all Blocks
1314+
iterationOfArrayCallback(blockInfo.blocks, iteratorBlock);
12811315
}
12821316

12831317
// PART TWO

lib/HotModuleReplacementPlugin.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ const ModuleHotDeclineDependency = require("./dependencies/ModuleHotDeclineDepen
1212
const ConstDependency = require("./dependencies/ConstDependency");
1313
const NullFactory = require("./NullFactory");
1414
const ParserHelpers = require("./ParserHelpers");
15-
const createHash = require("./util/createHash");
1615

1716
module.exports = class HotModuleReplacementPlugin {
1817
constructor(options) {
@@ -74,9 +73,7 @@ module.exports = class HotModuleReplacementPlugin {
7473
records.moduleHashs = {};
7574
for (const module of compilation.modules) {
7675
const identifier = module.identifier();
77-
const hash = createHash(compilation.outputOptions.hashFunction);
78-
module.updateHash(hash);
79-
records.moduleHashs[identifier] = hash.digest("hex");
76+
records.moduleHashs[identifier] = module.hash;
8077
}
8178
records.chunkHashs = {};
8279
for (const chunk of compilation.chunks) {
@@ -136,9 +133,7 @@ module.exports = class HotModuleReplacementPlugin {
136133
return;
137134
for (const module of compilation.modules) {
138135
const identifier = module.identifier();
139-
let hash = createHash(compilation.outputOptions.hashFunction);
140-
module.updateHash(hash);
141-
hash = hash.digest("hex");
136+
let hash = module.hash;
142137
module.hotUpdate = records.moduleHashs[identifier] !== hash;
143138
}
144139
const hotUpdateMainContent = {

lib/WebpackOptionsDefaulter.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,17 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
215215
);
216216
this.set("optimization.splitChunks", {});
217217
this.set("optimization.splitChunks.chunks", "async");
218-
this.set("optimization.splitChunks.minSize", 30000);
218+
this.set("optimization.splitChunks.minSize", "make", options => {
219+
return isProductionLikeMode(options) ? 30000 : 10000;
220+
});
219221
this.set("optimization.splitChunks.minChunks", 1);
220-
this.set("optimization.splitChunks.maxAsyncRequests", 5);
222+
this.set("optimization.splitChunks.maxAsyncRequests", "make", options => {
223+
return isProductionLikeMode(options) ? 5 : Infinity;
224+
});
221225
this.set("optimization.splitChunks.automaticNameDelimiter", "~");
222-
this.set("optimization.splitChunks.maxInitialRequests", 3);
226+
this.set("optimization.splitChunks.maxInitialRequests", "make", options => {
227+
return isProductionLikeMode(options) ? 3 : Infinity;
228+
});
223229
this.set("optimization.splitChunks.name", true);
224230
this.set("optimization.splitChunks.cacheGroups", {});
225231
this.set("optimization.splitChunks.cacheGroups.default", {

lib/optimize/FlagIncludedChunksPlugin.js

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,85 @@ class FlagIncludedChunksPlugin {
1010
compilation.hooks.optimizeChunkIds.tap(
1111
"FlagIncludedChunksPlugin",
1212
chunks => {
13+
// prepare two bit integers for each module
14+
// 2^31 is the max number represented as SMI in v8
15+
// we want the bits distributed this way:
16+
// the bit 2^31 is pretty rar and only one module should get it
17+
// so it has a probability of 1 / modulesCount
18+
// the first bit (2^0) is the easiest and every module could get it
19+
// if it doesn't get a better bit
20+
// from bit 2^n to 2^(n+1) there is a probability of p
21+
// so 1 / modulesCount == p^31
22+
// <=> p = sqrt31(1 / modulesCount)
23+
// so we use a modulo of 1 / sqrt31(1 / modulesCount)
24+
const moduleBits = new WeakMap();
25+
const modulesCount = compilation.modules.length;
26+
27+
// precalculate the modulo values for each bit
28+
const modulo = 1 / Math.pow(1 / modulesCount, 1 / 31);
29+
const modulos = Array.from(
30+
{ length: 31 },
31+
(x, i) => Math.pow(modulo, i) | 0
32+
);
33+
34+
// iterate all modules to generate bit values
35+
let i = 0;
36+
for (const module of compilation.modules) {
37+
let bit = 30;
38+
while (i % modulos[bit] !== 0) {
39+
bit--;
40+
}
41+
moduleBits.set(module, 1 << bit);
42+
i++;
43+
}
44+
45+
// interate all chunks to generate bitmaps
46+
const chunkModulesHash = new WeakMap();
47+
for (const chunk of chunks) {
48+
let hash = 0;
49+
for (const module of chunk.modulesIterable) {
50+
hash |= moduleBits.get(module);
51+
}
52+
chunkModulesHash.set(chunk, hash);
53+
}
54+
1355
for (const chunkA of chunks) {
14-
loopB: for (const chunkB of chunks) {
56+
const chunkAHash = chunkModulesHash.get(chunkA);
57+
const chunkAModulesCount = chunkA.getNumberOfModules();
58+
if (chunkAModulesCount === 0) continue;
59+
let bestModule = undefined;
60+
for (const module of chunkA.modulesIterable) {
61+
if (
62+
bestModule === undefined ||
63+
bestModule.getNumberOfChunks() > module.getNumberOfChunks()
64+
)
65+
bestModule = module;
66+
}
67+
loopB: for (const chunkB of bestModule.chunksIterable) {
1568
// as we iterate the same iterables twice
1669
// skip if we find ourselves
17-
if (chunkA === chunkB) continue loopB;
70+
if (chunkA === chunkB) continue;
71+
72+
const chunkBModulesCount = chunkB.getNumberOfModules();
73+
74+
// ids for empty chunks are not included
75+
if (chunkBModulesCount === 0) continue;
1876

1977
// instead of swapping A and B just bail
2078
// as we loop twice the current A will be B and B then A
21-
if (chunkA.getNumberOfModules() < chunkB.getNumberOfModules())
22-
continue loopB;
79+
if (chunkAModulesCount > chunkBModulesCount) continue;
80+
81+
// is chunkA in chunkB?
2382

24-
if (chunkB.getNumberOfModules() === 0) continue loopB;
83+
// we do a cheap check for the hash value
84+
const chunkBHash = chunkModulesHash.get(chunkB);
85+
if ((chunkBHash & chunkAHash) !== chunkAHash) continue;
2586

26-
// is chunkB in chunkA?
27-
for (const m of chunkB.modulesIterable) {
28-
if (!chunkA.containsModule(m)) continue loopB;
87+
// compare all modules
88+
for (const m of chunkA.modulesIterable) {
89+
if (!chunkB.containsModule(m)) continue loopB;
2990
}
30-
chunkA.ids.push(chunkB.id);
91+
chunkB.ids.push(chunkA.id);
3192
}
3293
}
3394
}

lib/optimize/RemoveEmptyChunksPlugin.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,18 @@ class RemoveEmptyChunksPlugin {
2222
"RemoveEmptyChunksPlugin",
2323
handler
2424
);
25+
compilation.hooks.optimizeChunksAdvanced.tap(
26+
"RemoveEmptyChunksPlugin",
27+
handler
28+
);
2529
compilation.hooks.optimizeExtractedChunksBasic.tap(
2630
"RemoveEmptyChunksPlugin",
2731
handler
2832
);
33+
compilation.hooks.optimizeExtractedChunksAdvanced.tap(
34+
"RemoveEmptyChunksPlugin",
35+
handler
36+
);
2937
});
3038
}
3139
}

lib/optimize/RemoveParentModulesPlugin.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class RemoveParentModulesPlugin {
2929
compiler.hooks.compilation.tap("RemoveParentModulesPlugin", compilation => {
3030
const handler = (chunks, chunkGroups) => {
3131
const queue = new Queue();
32-
const availableModulesMap = new Map();
32+
const availableModulesMap = new WeakMap();
3333

3434
for (const chunkGroup of compilation.entrypoints.values()) {
3535
// initialize available modules for chunks without parents
@@ -81,7 +81,10 @@ class RemoveParentModulesPlugin {
8181
chunkGroup => availableModulesMap.get(chunkGroup)
8282
);
8383
if (availableModulesSets.some(s => s === undefined)) continue; // No info about this chunk group
84-
const availableModules = intersect(availableModulesSets);
84+
const availableModules =
85+
availableModulesSets.length === 1
86+
? availableModulesSets[0]
87+
: intersect(availableModulesSets);
8588
const numberOfModules = chunk.getNumberOfModules();
8689
const toRemove = new Set();
8790
if (numberOfModules < availableModules.size) {

0 commit comments

Comments
 (0)