Skip to content

Commit

Permalink
further optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
sokra committed Feb 23, 2021
1 parent 365a362 commit 5c42b91
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 128 deletions.
45 changes: 28 additions & 17 deletions lib/ids/OccurrenceModuleIdsPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,40 +66,51 @@ class OccurrenceModuleIdsPlugin {
}

/**
* @param {Iterable<ModuleGraphConnection>} connections connections
* @param {Module} module module
* @returns {number} count of occurs
*/
const countOccursInEntry = connections => {
const countOccursInEntry = module => {
let sum = 0;
for (const c of connections) {
if (!c.isTargetActive(undefined)) continue;
if (!c.originModule) continue;
sum += initialChunkChunkMap.get(c.originModule);
for (const [
originModule,
connections
] of moduleGraph.getIncomingConnectionsByOriginModule(module)) {
if (!originModule) continue;
if (!connections.some(c => c.isTargetActive(undefined))) continue;
sum += initialChunkChunkMap.get(originModule);
}
return sum;
};

/**
* @param {Iterable<ModuleGraphConnection>} connections connections
* @param {Module} module module
* @returns {number} count of occurs
*/
const countOccurs = connections => {
const countOccurs = module => {
let sum = 0;
for (const c of connections) {
if (!c.isTargetActive(undefined)) continue;
if (!c.originModule) continue;
if (!c.dependency) continue;
const factor = c.dependency.getNumberOfIdOccurrences();
if (factor === 0) continue;
sum += factor * chunkGraph.getNumberOfModuleChunks(c.originModule);
for (const [
originModule,
connections
] of moduleGraph.getIncomingConnectionsByOriginModule(module)) {
if (!originModule) continue;
const chunkModules = chunkGraph.getNumberOfModuleChunks(
originModule
);
for (const c of connections) {
if (!c.isTargetActive(undefined)) continue;
if (!c.dependency) continue;
const factor = c.dependency.getNumberOfIdOccurrences();
if (factor === 0) continue;
sum += factor * chunkModules;
}
}
return sum;
};

if (prioritiseInitial) {
for (const m of modulesInOccurrenceOrder) {
const result =
countOccursInEntry(moduleGraph.getIncomingConnections(m)) +
countOccursInEntry(m) +
initialChunkChunkMap.get(m) +
entryCountMap.get(m);
occursInInitialChunksMap.set(m, result);
Expand All @@ -108,7 +119,7 @@ class OccurrenceModuleIdsPlugin {

for (const m of modules) {
const result =
countOccurs(moduleGraph.getIncomingConnections(m)) +
countOccurs(m) +
chunkGraph.getNumberOfModuleChunks(m) +
entryCountMap.get(m);
occursInAllChunksMap.set(m, result);
Expand Down
10 changes: 5 additions & 5 deletions lib/javascript/JavascriptModulesPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -901,12 +901,12 @@ class JavascriptModulesPlugin {
if (
result.allowInlineStartup &&
someInIterable(
moduleGraph.getIncomingConnections(entryModule),
c =>
c.originModule &&
c.isTargetActive(chunk.runtime) &&
moduleGraph.getIncomingConnectionsByOriginModule(entryModule),
([originModule, connections]) =>
originModule &&
connections.some(c => c.isTargetActive(chunk.runtime)) &&
someInIterable(
chunkGraph.getModuleRuntimes(c.originModule),
chunkGraph.getModuleRuntimes(originModule),
runtime =>
intersectRuntime(runtime, chunk.runtime) !== undefined
)
Expand Down
230 changes: 124 additions & 106 deletions lib/optimize/ModuleConcatenationPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const ConcatenatedModule = require("./ConcatenatedModule");
* @property {number} invalidModule
* @property {number} incorrectChunks
* @property {number} incorrectDependency
* @property {number} incorrectModuleDependency
* @property {number} incorrectChunksOfImporter
* @property {number} incorrectRuntimeCondition
* @property {number} importerFailed
Expand Down Expand Up @@ -230,6 +231,7 @@ class ModuleConcatenationPlugin {
invalidModule: 0,
incorrectChunks: 0,
incorrectDependency: 0,
incorrectModuleDependency: 0,
incorrectChunksOfImporter: 0,
incorrectRuntimeCondition: 0,
importerFailed: 0,
Expand Down Expand Up @@ -340,7 +342,7 @@ class ModuleConcatenationPlugin {
}), ${statsEmptyConfigurations} bailed out completely`
);
logger.debug(
`${statsCandidates} candidates were considered for adding (${stats.cached} cached failure, ${stats.alreadyInConfig} already in config, ${stats.invalidModule} invalid module, ${stats.incorrectChunks} incorrect chunks, ${stats.incorrectDependency} incorrect dependency, ${stats.incorrectChunksOfImporter} incorrect chunks of importer, ${stats.incorrectRuntimeCondition} incorrect runtime condition, ${stats.importerFailed} importer failed, ${stats.added} added)`
`${statsCandidates} candidates were considered for adding (${stats.cached} cached failure, ${stats.alreadyInConfig} already in config, ${stats.invalidModule} invalid module, ${stats.incorrectChunks} incorrect chunks, ${stats.incorrectDependency} incorrect dependency, ${stats.incorrectChunksOfImporter} incorrect chunks of importer, ${stats.incorrectModuleDependency} incorrect module dependency, ${stats.incorrectRuntimeCondition} incorrect runtime condition, ${stats.importerFailed} importer failed, ${stats.added} added)`
);
// HACK: Sort configurations by length and start with the longest one
// to get the biggest groups possible. Used modules are marked with usedModules
Expand Down Expand Up @@ -608,97 +610,65 @@ class ModuleConcatenationPlugin {

const moduleGraph = compilation.moduleGraph;

const incomingConnections = Array.from(
moduleGraph.getIncomingConnections(module)
).filter(connection => {
// We are not interested in inactive connections
if (!connection.isActive(runtime)) return false;

// Include, but do not analyse further, connections from non-modules
if (!connection.originModule) return true;

// Ignore connection from orphan modules
if (chunkGraph.getNumberOfModuleChunks(connection.originModule) === 0)
return false;
const incomingConnections = moduleGraph.getIncomingConnectionsByOriginModule(
module
);

// We don't care for connections from other runtimes
let originRuntime = undefined;
for (const r of chunkGraph.getModuleRuntimes(connection.originModule)) {
originRuntime = mergeRuntimeOwned(originRuntime, r);
const incomingConnectionsFromNonModules =
incomingConnections.get(null) || incomingConnections.get(undefined);
if (incomingConnectionsFromNonModules) {
const activeNonModulesConnections = incomingConnectionsFromNonModules.filter(
connection => {
// We are not interested in inactive connections
// or connections without dependency
return connection.isActive(runtime) || connection.dependency;
}
);
if (activeNonModulesConnections.length > 0) {
const problem = requestShortener => {
const importingExplanations = new Set(
activeNonModulesConnections.map(c => c.explanation).filter(Boolean)
);
const explanations = Array.from(importingExplanations).sort();
return `Module ${module.readableIdentifier(
requestShortener
)} is referenced ${
explanations.length > 0
? `by: ${explanations.join(", ")}`
: "in an unsupported way"
}`;
};
statistics.incorrectDependency++;
failureCache.set(module, problem); // cache failures for performance
return problem;
}
}

return intersectRuntime(runtime, originRuntime);
});
/** @type {Map<Module, readonly ModuleGraph.ModuleGraphConnection[]>} */
const incomingConnectionsFromModules = new Map();
for (const [originModule, connections] of incomingConnections) {
if (originModule) {
// Ignore connection from orphan modules
if (chunkGraph.getNumberOfModuleChunks(originModule) === 0) continue;

// We don't care for connections from other runtimes
let originRuntime = undefined;
for (const r of chunkGraph.getModuleRuntimes(originModule)) {
originRuntime = mergeRuntimeOwned(originRuntime, r);
}

const nonHarmonyConnections = incomingConnections.filter(
connection =>
!connection.originModule ||
!connection.dependency ||
!(connection.dependency instanceof HarmonyImportDependency)
);
if (nonHarmonyConnections.length > 0) {
const problem = requestShortener => {
const importingModules = new Set(
nonHarmonyConnections.map(c => c.originModule).filter(Boolean)
);
const importingExplanations = new Set(
nonHarmonyConnections.map(c => c.explanation).filter(Boolean)
);
const importingModuleTypes = new Map(
Array.from(importingModules).map(
m =>
/** @type {[Module, Set<string>]} */ ([
m,
new Set(
nonHarmonyConnections
.filter(c => c.originModule === m)
.map(c => c.dependency.type)
.sort()
)
])
)
if (!intersectRuntime(runtime, originRuntime)) continue;

// We are not interested in inactive connections
const activeConnections = connections.filter(connection =>
connection.isActive(runtime)
);
const names = Array.from(importingModules)
.map(
m =>
`${m.readableIdentifier(
requestShortener
)} (referenced with ${Array.from(
importingModuleTypes.get(m)
).join(", ")})`
)
.sort();
const explanations = Array.from(importingExplanations).sort();
if (names.length > 0 && explanations.length === 0) {
return `Module ${module.readableIdentifier(
requestShortener
)} is referenced from these modules with unsupported syntax: ${names.join(
", "
)}`;
} else if (names.length === 0 && explanations.length > 0) {
return `Module ${module.readableIdentifier(
requestShortener
)} is referenced by: ${explanations.join(", ")}`;
} else if (names.length > 0 && explanations.length > 0) {
return `Module ${module.readableIdentifier(
requestShortener
)} is referenced from these modules with unsupported syntax: ${names.join(
", "
)} and by: ${explanations.join(", ")}`;
} else {
return `Module ${module.readableIdentifier(
requestShortener
)} is referenced in a unsupported way`;
}
};
statistics.incorrectDependency++;
failureCache.set(module, problem); // cache failures for performance
return problem;
if (activeConnections.length > 0)
incomingConnectionsFromModules.set(originModule, activeConnections);
}
}

const incomingModules = Array.from(
new Set(incomingConnections.map(c => c.originModule))
);
const incomingModules = Array.from(incomingConnectionsFromModules.keys());

// Module must be in the same chunks like the referencing module
const otherChunkModules = incomingModules.filter(originModule => {
Expand Down Expand Up @@ -727,37 +697,85 @@ class ModuleConcatenationPlugin {
return problem;
}

/** @type {Map<Module, readonly ModuleGraph.ModuleGraphConnection[]>} */
const nonHarmonyConnections = new Map();
for (const [originModule, connections] of incomingConnectionsFromModules) {
const selected = connections.filter(
connection =>
!connection.dependency ||
!(connection.dependency instanceof HarmonyImportDependency)
);
if (selected.length > 0)
nonHarmonyConnections.set(originModule, connections);
}
if (nonHarmonyConnections.size > 0) {
const problem = requestShortener => {
const names = Array.from(nonHarmonyConnections)
.map(([originModule, connections]) => {
return `${originModule.readableIdentifier(
requestShortener
)} (referenced with ${Array.from(
new Set(
connections
.map(c => c.dependency && c.dependency.type)
.filter(Boolean)
)
)
.sort()
.join(", ")})`;
})
.sort();
return `Module ${module.readableIdentifier(
requestShortener
)} is referenced from these modules with unsupported syntax: ${names.join(
", "
)}`;
};
statistics.incorrectModuleDependency++;
failureCache.set(module, problem); // cache failures for performance
return problem;
}

if (runtime !== undefined && typeof runtime !== "string") {
// Module must be consistently referenced in the same runtimes
/** @type {Map<Module, boolean | RuntimeSpec>} */
const runtimeConditionMap = new Map();
for (const connection of incomingConnections) {
const runtimeCondition = filterRuntime(runtime, runtime => {
return connection.isTargetActive(runtime);
});
if (runtimeCondition === false) continue;
const old = runtimeConditionMap.get(connection.originModule) || false;
if (old === true) continue;
if (old !== false && runtimeCondition !== true) {
runtimeConditionMap.set(
connection.originModule,
mergeRuntime(old, runtimeCondition)
);
} else {
runtimeConditionMap.set(connection.originModule, runtimeCondition);
/** @type {{ originModule: Module, runtimeCondition: RuntimeSpec }[]} */
const otherRuntimeConnections = [];
outer: for (const [
originModule,
connections
] of incomingConnectionsFromModules) {
/** @type {false | RuntimeSpec} */
let currentRuntimeCondition = false;
for (const connection of connections) {
const runtimeCondition = filterRuntime(runtime, runtime => {
return connection.isTargetActive(runtime);
});
if (runtimeCondition === false) continue;
if (runtimeCondition === true) continue outer;
if (currentRuntimeCondition !== false) {
currentRuntimeCondition = mergeRuntime(
currentRuntimeCondition,
runtimeCondition
);
} else {
currentRuntimeCondition = runtimeCondition;
}
}
if (currentRuntimeCondition !== false) {
otherRuntimeConnections.push({
originModule,
runtimeCondition: currentRuntimeCondition
});
}
}
const otherRuntimeConnections = Array.from(runtimeConditionMap).filter(
([, runtimeCondition]) => typeof runtimeCondition !== "boolean"
);
if (otherRuntimeConnections.length > 0) {
const problem = requestShortener => {
return `Module ${module.readableIdentifier(
requestShortener
)} is runtime-dependent referenced by these modules: ${Array.from(
otherRuntimeConnections,
([module, runtimeCondition]) =>
`${module.readableIdentifier(
({ originModule, runtimeCondition }) =>
`${originModule.readableIdentifier(
requestShortener
)} (expected runtime ${runtimeToString(
runtime
Expand Down

0 comments on commit 5c42b91

Please sign in to comment.