Skip to content

Commit 96860f3

Browse files
module: avoid allocating a cache key string for every require()
The relative resolve cache was keyed by a concatenated ${parent.path}\x00${request} string, allocating a new key for every require() call including fully cached ones. Key the cache by the parent directory first (a Map keyed by the already-retained module.path string) and then by the request (a dictionary object, whose property access internalizes dynamically-constructed specifiers). Faster on every measured workload shape and slightly smaller in memory, since the concatenated keys are no longer retained. Signed-off-by: Sam Attard <sattard@anthropic.com>
1 parent 141a504 commit 96860f3

1 file changed

Lines changed: 21 additions & 13 deletions

File tree

‎lib/internal/modules/cjs/loader.js‎

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ let { startTimer, endTimer } = debugWithTimer('module_timer', (start, end) => {
223223
const { tracingChannel } = require('diagnostics_channel');
224224
const onRequire = getLazy(() => tracingChannel('module.require'));
225225

226-
const relativeResolveCache = { __proto__: null };
226+
const relativeResolveCache = new SafeMap();
227227

228228
let requireDepth = 0;
229229
let isPreloading = false;
@@ -1273,17 +1273,23 @@ function loadBuiltinWithHooks(id, url, format) {
12731273
* @returns {object}
12741274
*/
12751275
Module._load = function(request, parent, isMain, internalOptions = kEmptyObject) {
1276-
let relResolveCacheIdentifier;
1276+
let relResolveCacheByDir;
12771277
if (parent) {
12781278
debug('Module._load REQUEST %s parent: %s', request, parent.id);
1279-
// Fast path for (lazy loaded) modules in the same directory. The indirect
1280-
// caching is required to allow cache invalidation without changing the old
1281-
// cache key names.
1282-
relResolveCacheIdentifier = `${parent.path}\x00${request}`;
1283-
const filename = relativeResolveCache[relResolveCacheIdentifier];
1284-
reportModuleToWatchMode(filename);
1285-
reportModuleToWatchModeFromWorker(filename);
1279+
// Fast path for (lazy loaded) modules in the same directory. Keyed by
1280+
// parent directory and then request, so no concatenated cache key
1281+
// string is allocated per require() call.
1282+
relResolveCacheByDir = relativeResolveCache.get(parent.path);
1283+
if (relResolveCacheByDir === undefined) {
1284+
// A plain object handles dynamically built specifier strings
1285+
// better than a Map here.
1286+
relResolveCacheByDir = { __proto__: null };
1287+
relativeResolveCache.set(parent.path, relResolveCacheByDir);
1288+
}
1289+
const filename = relResolveCacheByDir[request];
12861290
if (filename !== undefined) {
1291+
reportModuleToWatchMode(filename);
1292+
reportModuleToWatchModeFromWorker(filename);
12871293
const cachedModule = Module._cache[filename];
12881294
if (cachedModule !== undefined) {
12891295
updateChildren(parent, cachedModule, true);
@@ -1292,7 +1298,7 @@ Module._load = function(request, parent, isMain, internalOptions = kEmptyObject)
12921298
}
12931299
return cachedModule.exports;
12941300
}
1295-
delete relativeResolveCache[relResolveCacheIdentifier];
1301+
delete relResolveCacheByDir[request];
12961302
}
12971303
}
12981304

@@ -1395,8 +1401,8 @@ Module._load = function(request, parent, isMain, internalOptions = kEmptyObject)
13951401
module[kFormat] ??= format;
13961402
}
13971403

1398-
if (parent !== undefined) {
1399-
relativeResolveCache[relResolveCacheIdentifier] = filename;
1404+
if (relResolveCacheByDir !== undefined) {
1405+
relResolveCacheByDir[request] = filename;
14001406
}
14011407

14021408
let threw = true;
@@ -1407,7 +1413,9 @@ Module._load = function(request, parent, isMain, internalOptions = kEmptyObject)
14071413
if (threw) {
14081414
delete Module._cache[filename];
14091415
if (parent !== undefined) {
1410-
delete relativeResolveCache[relResolveCacheIdentifier];
1416+
if (relResolveCacheByDir !== undefined) {
1417+
delete relResolveCacheByDir[request];
1418+
}
14111419
const children = parent?.children;
14121420
if (ArrayIsArray(children)) {
14131421
const index = ArrayPrototypeIndexOf(children, module);

0 commit comments

Comments
 (0)