Skip to content

Commit 3e58966

Browse files
authored
Merge pull request webpack#6016 from webpack/performance/improvements
Performance improvements
2 parents f1efe03 + 5db5512 commit 3e58966

File tree

4 files changed

+120
-44
lines changed

4 files changed

+120
-44
lines changed

lib/DependenciesBlock.js

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66

77
const DependenciesBlockVariable = require("./DependenciesBlockVariable");
88

9-
const disconnect = i => i.disconnect();
10-
11-
const unseal = i => i.unseal();
12-
139
class DependenciesBlock {
1410
constructor() {
1511
this.dependencies = [];
@@ -42,39 +38,54 @@ class DependenciesBlock {
4238
}
4339

4440
updateHash(hash) {
45-
const updateHash = i => i.updateHash(hash);
46-
47-
this.dependencies.forEach(updateHash);
48-
this.blocks.forEach(updateHash);
49-
this.variables.forEach(updateHash);
41+
for(const dep of this.dependencies)
42+
dep.updateHash(hash);
43+
for(const block of this.blocks)
44+
block.updateHash(hash);
45+
for(const variable of this.variables)
46+
variable.updateHash(hash);
5047
}
5148

5249
disconnect() {
53-
this.dependencies.forEach(disconnect);
54-
this.blocks.forEach(disconnect);
55-
this.variables.forEach(disconnect);
50+
for(const dep of this.dependencies)
51+
dep.disconnect();
52+
for(const block of this.blocks)
53+
block.disconnect();
54+
for(const variable of this.variables)
55+
variable.disconnect();
5656
}
5757

5858
unseal() {
59-
this.blocks.forEach(unseal);
59+
for(const block of this.blocks)
60+
block.unseal();
6061
}
6162

6263
hasDependencies(filter) {
6364
if(filter) {
64-
if(this.dependencies.some(filter)) {
65-
return true;
65+
for(const dep of this.dependencies) {
66+
if(filter(dep))
67+
return true;
6668
}
6769
} else {
6870
if(this.dependencies.length > 0) {
6971
return true;
7072
}
7173
}
7274

73-
return this.blocks.concat(this.variables).some(item => item.hasDependencies(filter));
75+
for(const block of this.blocks) {
76+
if(block.hasDependencies(filter))
77+
return true;
78+
}
79+
for(const variable of this.variables) {
80+
if(variable.hasDependencies(filter))
81+
return true;
82+
}
83+
return false;
7484
}
7585

7686
sortItems() {
77-
this.blocks.forEach(block => block.sortItems());
87+
for(const block of this.blocks)
88+
block.sortItems();
7889
}
7990
}
8091

lib/Module.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,14 @@ class Module extends DependenciesBlock {
8787
this.usedExports = null;
8888
this.providedExports = null;
8989
this.optimizationBailout = [];
90+
91+
// delayed operations
92+
this._rewriteChunkInReasons = undefined;
9093
}
9194

9295
disconnect() {
9396
this.reasons.length = 0;
97+
this._rewriteChunkInReasons = undefined;
9498
this._chunks.clear();
9599

96100
this.id = null;
@@ -196,6 +200,11 @@ class Module extends DependenciesBlock {
196200
}
197201

198202
hasReasonForChunk(chunk) {
203+
if(this._rewriteChunkInReasons) {
204+
for(const operation of this._rewriteChunkInReasons)
205+
this._doRewriteChunkInReasons(operation.oldChunk, operation.newChunks);
206+
this._rewriteChunkInReasons = undefined;
207+
}
199208
for(let i = 0; i < this.reasons.length; i++) {
200209
if(this.reasons[i].hasChunk(chunk))
201210
return true;
@@ -208,6 +217,16 @@ class Module extends DependenciesBlock {
208217
}
209218

210219
rewriteChunkInReasons(oldChunk, newChunks) {
220+
// This is expensive. Delay operation until we really need the data
221+
if(this._rewriteChunkInReasons === undefined)
222+
this._rewriteChunkInReasons = [];
223+
this._rewriteChunkInReasons.push({
224+
oldChunk,
225+
newChunks
226+
});
227+
}
228+
229+
_doRewriteChunkInReasons(oldChunk, newChunks) {
211230
for(let i = 0; i < this.reasons.length; i++) {
212231
this.reasons[i].rewriteChunks(oldChunk, newChunks);
213232
}

lib/RequestShortener.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,27 @@ class RequestShortener {
4242
this.buildinsAsModule = buildinsAsModule;
4343
this.buildinsRegExp = createRegExpForPath(buildins);
4444
}
45+
46+
this.cache = new Map();
4547
}
4648

4749
shorten(request) {
4850
if(!request) return request;
49-
request = normalizeBackSlashDirection(request);
51+
const cacheEntry = this.cache.get(request);
52+
if(cacheEntry !== undefined) return cacheEntry;
53+
let result = normalizeBackSlashDirection(request);
5054
if(this.buildinsAsModule && this.buildinsRegExp)
51-
request = request.replace(this.buildinsRegExp, "!(webpack)");
55+
result = result.replace(this.buildinsRegExp, "!(webpack)");
5256
if(this.currentDirectoryRegExp)
53-
request = request.replace(this.currentDirectoryRegExp, "!.");
57+
result = result.replace(this.currentDirectoryRegExp, "!.");
5458
if(this.parentDirectoryRegExp)
55-
request = request.replace(this.parentDirectoryRegExp, "!..");
59+
result = result.replace(this.parentDirectoryRegExp, "!..");
5660
if(!this.buildinsAsModule && this.buildinsRegExp)
57-
request = request.replace(this.buildinsRegExp, "!(webpack)");
58-
request = request.replace(INDEX_JS_REGEXP, "$1");
59-
return request.replace(FRONT_OR_BACK_BANG_REGEXP, "");
61+
result = result.replace(this.buildinsRegExp, "!(webpack)");
62+
result = result.replace(INDEX_JS_REGEXP, "$1");
63+
result = result.replace(FRONT_OR_BACK_BANG_REGEXP, "");
64+
this.cache.set(request, result);
65+
return result;
6066
}
6167
}
6268

lib/optimize/RemoveParentModulesPlugin.js

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
"use strict";
66

7+
const Queue = require("../util/Queue");
8+
79
const hasModule = (chunk, module, checkedChunks) => {
810
if(chunk.containsModule(module)) return [chunk];
911
if(chunk.getNumberOfParents() === 0) return false;
@@ -33,30 +35,68 @@ class RemoveParentModulesPlugin {
3335
apply(compiler) {
3436
compiler.plugin("compilation", (compilation) => {
3537
compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => {
36-
for(var index = 0; index < chunks.length; index++) {
37-
var chunk = chunks[index];
38-
if(chunk.getNumberOfParents() === 0) continue;
38+
const queue = new Queue(chunks);
39+
const availableModulesMap = new Map();
3940

40-
// TODO consider Map when performance has improved https://gist.github.com/sokra/b36098368da7b8f6792fd7c85fca6311
41-
var cache = Object.create(null);
42-
var modules = chunk.getModules();
43-
for(var i = 0; i < modules.length; i++) {
44-
var module = modules[i];
41+
for(const chunk of chunks) {
42+
// initialize available modules for chunks without parents
43+
if(chunk.getNumberOfParents() === 0)
44+
availableModulesMap.set(chunk, new Set());
45+
}
4546

46-
var dId = module.getChunkIdsIdent();
47-
var parentChunksWithModule;
48-
if(dId === null) {
49-
parentChunksWithModule = allHaveModule(chunk.getParents(), module);
50-
} else if(dId in cache) {
51-
parentChunksWithModule = cache[dId];
52-
} else {
53-
parentChunksWithModule = cache[dId] = allHaveModule(chunk.getParents(), module);
54-
}
55-
if(parentChunksWithModule) {
56-
module.rewriteChunkInReasons(chunk, Array.from(parentChunksWithModule));
57-
chunk.removeModule(module);
47+
while(queue.length > 0) {
48+
const chunk = queue.dequeue();
49+
let availableModules = availableModulesMap.get(chunk);
50+
let changed = false;
51+
for(const parent of chunk.parentsIterable) {
52+
const availableModulesInParent = availableModulesMap.get(parent);
53+
if(availableModulesInParent !== undefined) {
54+
// If we know the available modules in parent: process these
55+
if(availableModules === undefined) {
56+
// if we have not own info yet: create new entry
57+
availableModules = new Set(availableModulesInParent);
58+
for(const m of parent.modulesIterable)
59+
availableModules.add(m);
60+
availableModulesMap.set(chunk, availableModules);
61+
changed = true;
62+
} else {
63+
for(const m of availableModules) {
64+
if(!parent.containsModule(m) && !availableModulesInParent.has(m)) {
65+
availableModules.delete(m);
66+
changed = true;
67+
}
68+
}
69+
}
5870
}
5971
}
72+
if(changed) {
73+
// if something changed: enqueue our children
74+
for(const child of chunk.chunksIterable)
75+
queue.enqueue(child);
76+
}
77+
}
78+
79+
// now we have available modules for every chunk
80+
81+
for(const chunk of chunks) {
82+
// remove modules from chunk if they are already available
83+
const availableModules = availableModulesMap.get(chunk);
84+
const modules = new Set(chunk.modulesIterable);
85+
const toRemove = new Set();
86+
if(modules.size < availableModules.size) {
87+
for(const m of modules)
88+
if(availableModules.has(m))
89+
toRemove.add(m);
90+
} else {
91+
for(const m of availableModules)
92+
if(modules.has(m))
93+
toRemove.add(m);
94+
}
95+
for(const module of toRemove) {
96+
const parentChunksWithModule = allHaveModule(chunk.getParents(), module);
97+
module.rewriteChunkInReasons(chunk, Array.from(parentChunksWithModule));
98+
chunk.removeModule(module);
99+
}
60100
}
61101
});
62102
});

0 commit comments

Comments
 (0)