Merge pull request #8242 from webpack/perf/chunk-graph

Optimize chunk graph algorithm
This commit is contained in:
Tobias Koppers 2018-10-20 06:09:58 +02:00 committed by GitHub
commit 5165a90de8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 62 additions and 43 deletions

View File

@ -88,13 +88,6 @@ const compareLocations = require("./compareLocations");
* @property {Dependency[]} dependencies
*/
/**
* @typedef {Object} AvailableModulesChunkGroupMapping
* @property {ChunkGroup} chunkGroup
* @property {Set<Module>} availableModules
* @property {boolean} needCopy
*/
/**
* @typedef {Object} DependenciesBlockLike
* @property {Dependency[]} dependencies
@ -162,6 +155,16 @@ const byNameOrHash = (a, b) => {
return 0;
};
/**
* @template T
* @param {Set<T>} a first set
* @param {Set<T>} b second set
* @returns {number} cmp
*/
const bySetSize = (a, b) => {
return a.size - b.size;
};
/**
* @param {DependenciesBlockVariable[]} variables DepBlock Variables to iterate over
* @param {DepBlockVarDependenciesCallback} fn callback to apply on iterated elements
@ -1791,17 +1794,26 @@ class Compilation extends Tapable {
// PART TWO
/** @type {Set<Module>} */
let availableModules;
/** @type {Set<Module>} */
let newAvailableModules;
/** @type {Queue<AvailableModulesChunkGroupMapping>} */
const queue2 = new Queue(
inputChunkGroups.map(chunkGroup => ({
chunkGroup,
availableModules: new Set(),
needCopy: true
}))
);
/**
* @typedef {Object} ChunkGroupInfo
* @property {Set<Module>} minAvailableModules current minimal set of modules available at this point
* @property {Set<Module>[]} availableModulesToBeMerged enqueued updates to the minimal set of available modules
*/
/** @type {Map<ChunkGroup, ChunkGroupInfo>} */
const chunkGroupInfoMap = new Map();
/** @type {Queue<ChunkGroup>} */
const queue2 = new Queue(inputChunkGroups);
for (const chunkGroup of inputChunkGroups) {
chunkGroupInfoMap.set(chunkGroup, {
minAvailableModules: undefined,
availableModulesToBeMerged: [new Set()]
});
}
/**
* Helper function to check if all modules of a chunk are available
@ -1836,36 +1848,35 @@ class Compilation extends Tapable {
return true;
};
/** @type {Map<ChunkGroup, Set<Module>>} */
const minAvailableModulesMap = new Map();
// Iterative traversing of the basic chunk graph
while (queue2.length) {
const queueItem = queue2.dequeue();
chunkGroup = queueItem.chunkGroup;
availableModules = queueItem.availableModules;
chunkGroup = queue2.dequeue();
const info = chunkGroupInfoMap.get(chunkGroup);
const availableModulesToBeMerged = info.availableModulesToBeMerged;
let minAvailableModules = info.minAvailableModules;
// 1. Get minimal available modules
// It doesn't make sense to traverse a chunk again with more available modules.
// This step calculates the minimal available modules and skips traversal when
// the list didn't shrink.
let minAvailableModules = minAvailableModulesMap.get(chunkGroup);
if (minAvailableModules === undefined) {
minAvailableModulesMap.set(
chunkGroup,
queueItem.needCopy ? new Set(availableModules) : availableModules
);
} else {
let deletedModules = false;
for (const m of minAvailableModules) {
if (!availableModules.has(m)) {
minAvailableModules.delete(m);
deletedModules = true;
availableModulesToBeMerged.sort(bySetSize);
let changed = false;
for (const availableModules of availableModulesToBeMerged) {
if (minAvailableModules === undefined) {
minAvailableModules = new Set(availableModules);
info.minAvailableModules = minAvailableModules;
changed = true;
} else {
for (const m of minAvailableModules) {
if (!availableModules.has(m)) {
minAvailableModules.delete(m);
changed = true;
}
}
}
if (!deletedModules) continue;
availableModules = minAvailableModules;
}
availableModulesToBeMerged.length = 0;
if (!changed) continue;
// 2. Get the edges at this point of the graph
const deps = chunkDependencies.get(chunkGroup);
@ -1873,7 +1884,7 @@ class Compilation extends Tapable {
if (deps.length === 0) continue;
// 3. Create a new Set of available modules at this points
newAvailableModules = new Set(availableModules);
newAvailableModules = new Set(minAvailableModules);
for (const chunk of chunkGroup.chunks) {
for (const m of chunk.modulesIterable) {
newAvailableModules.add(m);
@ -1906,11 +1917,19 @@ class Compilation extends Tapable {
// 7. Enqueue further traversal
for (const nextChunkGroup of nextChunkGroups) {
queue2.enqueue({
chunkGroup: nextChunkGroup,
availableModules: newAvailableModules,
needCopy: nextChunkGroup.size !== 1
});
let nextInfo = chunkGroupInfoMap.get(nextChunkGroup);
if (nextInfo === undefined) {
nextInfo = {
minAvailableModules: undefined,
availableModulesToBeMerged: []
};
chunkGroupInfoMap.set(nextChunkGroup, nextInfo);
}
nextInfo.availableModulesToBeMerged.push(newAvailableModules);
// As queue deduplicates enqueued items this makes sure that a ChunkGroup
// is not enqueued twice
queue2.enqueue(nextChunkGroup);
}
}