Skip to content

Commit 35180ba

Browse files
authored
path: improve path.resolve() performance when used as process.cwd()
`path.resolve()` and `path.resolve('.')` is frequently called as alternative to process.cwd(). This minimized overhead for these specific cases. PR-URL: #58362 Reviewed-By: Jordan Harband <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Marco Ippolito <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Mohammed Keyvanzadeh <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Rafael Gonzaga <[email protected]> Reviewed-By: Ulises Gascón <[email protected]>
1 parent 1ef9923 commit 35180ba

File tree

8 files changed

+40
-15
lines changed

8 files changed

+40
-15
lines changed

benchmark/path/join-posix.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ function main({ n, paths }) {
1616

1717
bench.start();
1818
for (let i = 0; i < n; i++) {
19-
if (i % 3 === 0) {
20-
copy[1] = `${orig}${i}`;
19+
if (i % 5 === 0) {
20+
copy[1] = `${orig}/${i}`;
2121
posix.join(...copy);
2222
} else {
2323
posix.join(...args);

benchmark/path/join-win32.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ function main({ n, paths }) {
1616

1717
bench.start();
1818
for (let i = 0; i < n; i++) {
19-
if (i % 3 === 0) {
20-
copy[1] = `${orig}${i}`;
19+
if (i % 5 === 0) {
20+
copy[1] = `${orig}\\${i}`;
2121
win32.join(...copy);
2222
} else {
2323
win32.join(...args);

benchmark/path/normalize-posix.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const bench = common.createBenchmark(main, {
1717
function main({ n, path }) {
1818
bench.start();
1919
for (let i = 0; i < n; i++) {
20-
posix.normalize(i % 3 === 0 ? `${path}${i}` : path);
20+
posix.normalize(i % 5 === 0 ? `${path}/${i}` : path);
2121
}
2222
bench.end(n);
2323
}

benchmark/path/normalize-win32.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const bench = common.createBenchmark(main, {
1717
function main({ n, path }) {
1818
bench.start();
1919
for (let i = 0; i < n; i++) {
20-
win32.normalize(i % 3 === 0 ? `${path}${i}` : path);
20+
win32.normalize(i % 5 === 0 ? `${path}\\${i}` : path);
2121
}
2222
bench.end(n);
2323
}

benchmark/path/resolve-posix.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,26 @@ const { posix } = require('path');
44

55
const bench = common.createBenchmark(main, {
66
paths: [
7+
'empty',
78
'',
9+
'.',
810
['', ''].join('|'),
911
['foo/bar', '/tmp/file/', '..', 'a/../subfile'].join('|'),
1012
['a/b/c/', '../../..'].join('|'),
13+
['/a/b/c/', 'abc'].join('|'),
1114
],
1215
n: [1e5],
1316
});
1417

1518
function main({ n, paths }) {
16-
const args = paths.split('|');
19+
const args = paths === 'empty' ? [] : paths.split('|');
1720
const copy = [...args];
18-
const orig = copy[0];
21+
const orig = copy[0] ?? '';
1922

2023
bench.start();
2124
for (let i = 0; i < n; i++) {
22-
if (i % 3 === 0) {
23-
copy[0] = `${orig}${i}`;
25+
if (i % 5 === 0) {
26+
copy[0] = `${orig}/${i}`;
2427
posix.resolve(...copy);
2528
} else {
2629
posix.resolve(...args);

benchmark/path/resolve-win32.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ const { win32 } = require('path');
44

55
const bench = common.createBenchmark(main, {
66
paths: [
7+
'empty',
78
'',
9+
'.',
810
['', ''].join('|'),
911
['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'].join('|'),
1012
['c:/blah\\blah', 'd:/games', 'c:../a'].join('|'),
@@ -13,14 +15,14 @@ const bench = common.createBenchmark(main, {
1315
});
1416

1517
function main({ n, paths }) {
16-
const args = paths.split('|');
18+
const args = paths === 'empty' ? [] : paths.split('|');
1719
const copy = [...args];
18-
const orig = copy[0];
20+
const orig = copy[0] ?? '';
1921

2022
bench.start();
2123
for (let i = 0; i < n; i++) {
22-
if (i % 3 === 0) {
23-
copy[0] = `${orig}${i}`;
24+
if (i % 5 === 0) {
25+
copy[0] = `${orig}\\${i}`;
2426
win32.resolve(...copy);
2527
} else {
2628
win32.resolve(...args);

lib/path.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ function normalizeString(path, allowAboveRoot, separator, isPathSeparator) {
9595
StringPrototypeCharCodeAt(res, res.length - 1) !== CHAR_DOT ||
9696
StringPrototypeCharCodeAt(res, res.length - 2) !== CHAR_DOT) {
9797
if (res.length > 2) {
98-
const lastSlashIndex = StringPrototypeLastIndexOf(res, separator);
98+
const lastSlashIndex = res.length - lastSegmentLength - 1;
9999
if (lastSlashIndex === -1) {
100100
res = '';
101101
lastSegmentLength = 0;
@@ -163,6 +163,8 @@ function _format(sep, pathObject) {
163163
return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`;
164164
}
165165

166+
const forwardSlashRegExp = /\//g;
167+
166168
const win32 = {
167169
/**
168170
* path.resolve([from ...], to)
@@ -186,6 +188,14 @@ const win32 = {
186188
}
187189
} else if (resolvedDevice.length === 0) {
188190
path = process.cwd();
191+
// Fast path for current directory
192+
if (args.length === 0 || ((args.length === 1 && (args[0] === '' || args[0] === '.')) &&
193+
isPathSeparator(StringPrototypeCharCodeAt(path, 0)))) {
194+
if (!isWindows) {
195+
path = StringPrototypeReplace(path, forwardSlashRegExp, '\\');
196+
}
197+
return path;
198+
}
189199
} else {
190200
// Windows has the concept of drive-specific current working
191201
// directories. If we've resolved a drive letter but not yet an
@@ -1171,6 +1181,12 @@ const posix = {
11711181
* @returns {string}
11721182
*/
11731183
resolve(...args) {
1184+
if (args.length === 0 || (args.length === 1 && (args[0] === '' || args[0] === '.'))) {
1185+
const cwd = posixCwd();
1186+
if (StringPrototypeCharCodeAt(cwd, 0) === CHAR_FORWARD_SLASH) {
1187+
return cwd;
1188+
}
1189+
}
11741190
let resolvedPath = '';
11751191
let resolvedAbsolute = false;
11761192

test/parallel/test-path-resolve.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const resolveTests = [
2424
[['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'],
2525
[['c:/ignore', 'c:/some/file'], 'c:\\some\\file'],
2626
[['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'],
27+
[[], process.cwd()],
28+
[[''], process.cwd()],
2729
[['.'], process.cwd()],
2830
[['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'],
2931
[['c:/', '//'], 'c:\\'],
@@ -42,6 +44,8 @@ const resolveTests = [
4244
[[['/var/lib', '../', 'file/'], '/var/file'],
4345
[['/var/lib', '/../', 'file/'], '/file'],
4446
[['a/b/c/', '../../..'], posixyCwd],
47+
[[], posixyCwd],
48+
[[''], posixyCwd],
4549
[['.'], posixyCwd],
4650
[['/some/dir', '.', '/absolute/'], '/absolute'],
4751
[['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js'],

0 commit comments

Comments
 (0)