perf(buildChunkGraph): avoid unneeded re-visit

This commit is contained in:
Alexander Akait 2024-09-03 14:30:22 +03:00 committed by GitHub
commit 456c70432a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 77 additions and 62 deletions

View File

@ -38,6 +38,7 @@ const { getEntryRuntime, mergeRuntime } = require("./util/runtime");
* @typedef {object} ChunkGroupInfo * @typedef {object} ChunkGroupInfo
* @property {ChunkGroup} chunkGroup the chunk group * @property {ChunkGroup} chunkGroup the chunk group
* @property {RuntimeSpec} runtime the runtimes * @property {RuntimeSpec} runtime the runtimes
* @property {boolean} init is this chunk group initialized
* @property {bigint | undefined} minAvailableModules current minimal set of modules available at this point * @property {bigint | undefined} minAvailableModules current minimal set of modules available at this point
* @property {bigint[]} availableModulesToBeMerged enqueued updates to the minimal set of available modules * @property {bigint[]} availableModulesToBeMerged enqueued updates to the minimal set of available modules
* @property {Set<Module>=} skippedItems modules that were skipped because module is already available in parent chunks (need to reconsider when minAvailableModules is shrinking) * @property {Set<Module>=} skippedItems modules that were skipped because module is already available in parent chunks (need to reconsider when minAvailableModules is shrinking)
@ -345,8 +346,8 @@ const visitModules = (
/** @type {Map<DependenciesBlock, ChunkGroupInfo>} */ /** @type {Map<DependenciesBlock, ChunkGroupInfo>} */
const blockChunkGroups = new Map(); const blockChunkGroups = new Map();
/** @type {Map<ChunkGroupInfo, DependenciesBlock>} */ /** @type {Map<ChunkGroupInfo, Set<DependenciesBlock>>} */
const blockByChunkGroups = new Map(); const blocksByChunkGroups = new Map();
/** @type {Map<string, ChunkGroupInfo>} */ /** @type {Map<string, ChunkGroupInfo>} */
const namedChunkGroups = new Map(); const namedChunkGroups = new Map();
@ -367,7 +368,7 @@ const visitModules = (
/** @type {QueueItem[]} */ /** @type {QueueItem[]} */
let queue = []; let queue = [];
/** @type {Map<ChunkGroupInfo, Set<ChunkGroupInfo>>} */ /** @type {Map<ChunkGroupInfo, Set<[ChunkGroupInfo, ChunkGroup, Module | null]>>} */
const queueConnect = new Map(); const queueConnect = new Map();
/** @type {Set<ChunkGroupInfo>} */ /** @type {Set<ChunkGroupInfo>} */
const chunkGroupsForCombining = new Set(); const chunkGroupsForCombining = new Set();
@ -382,6 +383,7 @@ const visitModules = (
); );
/** @type {ChunkGroupInfo} */ /** @type {ChunkGroupInfo} */
const chunkGroupInfo = { const chunkGroupInfo = {
init: false,
chunkGroup, chunkGroup,
runtime, runtime,
minAvailableModules: undefined, minAvailableModules: undefined,
@ -452,7 +454,7 @@ const visitModules = (
/** @type {Set<ChunkGroupInfo>} */ /** @type {Set<ChunkGroupInfo>} */
const outdatedChunkGroupInfo = new Set(); const outdatedChunkGroupInfo = new Set();
/** @type {Set<ChunkGroupInfo>} */ /** @type {Set<[ChunkGroupInfo, ChunkGroup, Module | null]>} */
const chunkGroupsForMerging = new Set(); const chunkGroupsForMerging = new Set();
/** @type {QueueItem[]} */ /** @type {QueueItem[]} */
let queueDelayed = []; let queueDelayed = [];
@ -505,6 +507,7 @@ const visitModules = (
entrypoint.index = nextChunkGroupIndex++; entrypoint.index = nextChunkGroupIndex++;
cgi = { cgi = {
chunkGroup: entrypoint, chunkGroup: entrypoint,
init: false,
runtime: entrypoint.options.runtime || entrypoint.name, runtime: entrypoint.options.runtime || entrypoint.name,
minAvailableModules: ZERO_BIGINT, minAvailableModules: ZERO_BIGINT,
availableModulesToBeMerged: [], availableModulesToBeMerged: [],
@ -572,6 +575,7 @@ const visitModules = (
maskByChunk.set(c.chunks[0], ZERO_BIGINT); maskByChunk.set(c.chunks[0], ZERO_BIGINT);
c.index = nextChunkGroupIndex++; c.index = nextChunkGroupIndex++;
cgi = { cgi = {
init: false,
chunkGroup: c, chunkGroup: c,
runtime: chunkGroupInfo.runtime, runtime: chunkGroupInfo.runtime,
minAvailableModules: undefined, minAvailableModules: undefined,
@ -614,7 +618,14 @@ const visitModules = (
blockConnections.set(b, []); blockConnections.set(b, []);
} }
blockChunkGroups.set(b, /** @type {ChunkGroupInfo} */ (cgi)); blockChunkGroups.set(b, /** @type {ChunkGroupInfo} */ (cgi));
blockByChunkGroups.set(/** @type {ChunkGroupInfo} */ (cgi), b); let blocks = blocksByChunkGroups.get(/** @type {ChunkGroupInfo} */ (cgi));
if (!blocks) {
blocksByChunkGroups.set(
/** @type {ChunkGroupInfo} */ (cgi),
(blocks = new Set())
);
}
blocks.add(b);
} else if (entryOptions) { } else if (entryOptions) {
entrypoint = /** @type {Entrypoint} */ (cgi.chunkGroup); entrypoint = /** @type {Entrypoint} */ (cgi.chunkGroup);
} else { } else {
@ -636,19 +647,9 @@ const visitModules = (
connectList = new Set(); connectList = new Set();
queueConnect.set(chunkGroupInfo, connectList); queueConnect.set(chunkGroupInfo, connectList);
} }
connectList.add(/** @type {ChunkGroupInfo} */ (cgi)); connectList.add(
/** @type {[ChunkGroupInfo, ChunkGroup, Module]} */ ([cgi, c, module])
// TODO check if this really need to be done for each traversal );
// or if it is enough when it's queued when created
// 4. We enqueue the DependenciesBlock for traversal
queueDelayed.push({
action: PROCESS_BLOCK,
block: b,
module,
chunk: c.chunks[0],
chunkGroup: c,
chunkGroupInfo: /** @type {ChunkGroupInfo} */ (cgi)
});
} else if (entrypoint !== undefined) { } else if (entrypoint !== undefined) {
chunkGroupInfo.chunkGroup.addAsyncEntrypoint(entrypoint); chunkGroupInfo.chunkGroup.addAsyncEntrypoint(entrypoint);
} }
@ -901,11 +902,10 @@ const visitModules = (
for (const [chunkGroupInfo, targets] of queueConnect) { for (const [chunkGroupInfo, targets] of queueConnect) {
// 1. Add new targets to the list of children // 1. Add new targets to the list of children
if (chunkGroupInfo.children === undefined) { if (chunkGroupInfo.children === undefined) {
chunkGroupInfo.children = targets; chunkGroupInfo.children = new Set();
} else { }
for (const target of targets) { for (const [target] of targets) {
chunkGroupInfo.children.add(target); chunkGroupInfo.children.add(target);
}
} }
// 2. Calculate resulting available modules // 2. Calculate resulting available modules
@ -915,9 +915,9 @@ const visitModules = (
const runtime = chunkGroupInfo.runtime; const runtime = chunkGroupInfo.runtime;
// 3. Update chunk group info // 3. Update chunk group info
for (const target of targets) { for (const [target, chunkGroup, module] of targets) {
target.availableModulesToBeMerged.push(resultingAvailableModules); target.availableModulesToBeMerged.push(resultingAvailableModules);
chunkGroupsForMerging.add(target); chunkGroupsForMerging.add([target, chunkGroup, module]);
const oldRuntime = target.runtime; const oldRuntime = target.runtime;
const newRuntime = mergeRuntime(oldRuntime, runtime); const newRuntime = mergeRuntime(oldRuntime, runtime);
if (oldRuntime !== newRuntime) { if (oldRuntime !== newRuntime) {
@ -935,7 +935,7 @@ const visitModules = (
statProcessedChunkGroupsForMerging += chunkGroupsForMerging.size; statProcessedChunkGroupsForMerging += chunkGroupsForMerging.size;
// Execute the merge // Execute the merge
for (const info of chunkGroupsForMerging) { for (const [info, chunkGroup, module] of chunkGroupsForMerging) {
const availableModulesToBeMerged = info.availableModulesToBeMerged; const availableModulesToBeMerged = info.availableModulesToBeMerged;
const cachedMinAvailableModules = info.minAvailableModules; const cachedMinAvailableModules = info.minAvailableModules;
let minAvailableModules = cachedMinAvailableModules; let minAvailableModules = cachedMinAvailableModules;
@ -958,6 +958,20 @@ const visitModules = (
info.resultingAvailableModules = undefined; info.resultingAvailableModules = undefined;
outdatedChunkGroupInfo.add(info); outdatedChunkGroupInfo.add(info);
} }
if ((!info.init || changed) && module) {
info.init = true;
for (const b of blocksByChunkGroups.get(info)) {
queueDelayed.push({
action: PROCESS_BLOCK,
block: b,
module,
chunk: chunkGroup.chunks[0],
chunkGroup,
chunkGroupInfo: info
});
}
}
} }
chunkGroupsForMerging.clear(); chunkGroupsForMerging.clear();
}; };
@ -1057,7 +1071,7 @@ const visitModules = (
connectList = new Set(); connectList = new Set();
queueConnect.set(info, connectList); queueConnect.set(info, connectList);
} }
connectList.add(cgi); connectList.add([cgi, cgi.chunkGroup, module]);
} }
} }
@ -1117,48 +1131,44 @@ const visitModules = (
for (const info of outdatedOrderIndexChunkGroups) { for (const info of outdatedOrderIndexChunkGroups) {
const { chunkGroup, runtime } = info; const { chunkGroup, runtime } = info;
const block = blockByChunkGroups.get(info); const blocks = blocksByChunkGroups.get(info);
if (!block) { if (!blocks) {
continue; continue;
} }
let preOrderIndex = 0; for (const block of blocks) {
let postOrderIndex = 0; let preOrderIndex = 0;
let postOrderIndex = 0;
/**
* @param {DependenciesBlock} current current
* @param {BlocksWithNestedBlocks} visited visited dependencies blocks
*/
const process = (current, visited) => {
const blockModules = getBlockModules(current, runtime);
for (let i = 0, len = blockModules.length; i < len; i += 3) {
const activeState = /** @type {ConnectionState} */ (
blockModules[i + 1]
);
if (activeState === false) {
continue;
}
const refModule = /** @type {Module} */ (blockModules[i]);
if (visited.has(refModule)) {
continue;
}
/** visited.add(refModule);
* @param {DependenciesBlock} current current
* @param {BlocksWithNestedBlocks} visited visited dependencies blocks
*/
const process = (current, visited) => {
const blockModules = getBlockModules(current, runtime);
if (blockModules === undefined) {
return;
}
for (let i = 0, len = blockModules.length; i < len; i += 3) { if (refModule) {
const activeState = /** @type {ConnectionState} */ ( chunkGroup.setModulePreOrderIndex(refModule, preOrderIndex++);
blockModules[i + 1] process(refModule, visited);
); chunkGroup.setModulePostOrderIndex(refModule, postOrderIndex++);
if (activeState === false) { }
continue;
} }
const refModule = /** @type {Module} */ (blockModules[i]); };
if (visited.has(refModule)) { process(block, new Set());
continue; }
}
visited.add(refModule);
if (refModule) {
chunkGroup.setModulePreOrderIndex(refModule, preOrderIndex++);
process(refModule, visited);
chunkGroup.setModulePostOrderIndex(refModule, postOrderIndex++);
}
}
};
process(block, new Set());
} }
outdatedOrderIndexChunkGroups.clear(); outdatedOrderIndexChunkGroups.clear();
ordinalByModule.clear(); ordinalByModule.clear();

View File

@ -0,0 +1,5 @@
module.exports = {
optimization: {
moduleIds: "named"
}
};