Skip to content

Commit 1168410

Browse files
BridgeARaddaleax
authored andcommitted
util: improve util.inspect performance
* improve util.inspect performance This is a huge performance improvement in case of sparse arrays when using util.inspect as the hole will simple be skipped. * use faster visibleKeys property lookup * add inspect-array benchmark PR-URL: #14492 Fixes: #14487 Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
1 parent 1c00875 commit 1168410

File tree

3 files changed

+82
-19
lines changed

3 files changed

+82
-19
lines changed

benchmark/util/inspect-array.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const util = require('util');
5+
6+
const bench = common.createBenchmark(main, {
7+
n: [1e2],
8+
len: [1e5],
9+
type: [
10+
'denseArray',
11+
'sparseArray',
12+
'mixedArray'
13+
]
14+
});
15+
16+
function main(conf) {
17+
const { n, len, type } = conf;
18+
var arr = Array(len);
19+
var i;
20+
21+
switch (type) {
22+
case 'denseArray':
23+
arr = arr.fill(0);
24+
break;
25+
case 'sparseArray':
26+
break;
27+
case 'mixedArray':
28+
for (i = 0; i < n; i += 2)
29+
arr[i] = i;
30+
break;
31+
default:
32+
throw new Error(`Unsupported type ${type}`);
33+
}
34+
bench.start();
35+
for (i = 0; i < n; i++) {
36+
util.inspect(arr);
37+
}
38+
bench.end(n);
39+
}

lib/util.js

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ const inspectDefaultOptions = Object.seal({
6464

6565
const numbersOnlyRE = /^\d+$/;
6666

67-
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
6867
const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
6968
const regExpToString = RegExp.prototype.toString;
7069
const dateToISOString = Date.prototype.toISOString;
@@ -683,22 +682,36 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
683682
var output = [];
684683
let visibleLength = 0;
685684
let index = 0;
686-
while (index < value.length && visibleLength < ctx.maxArrayLength) {
687-
let emptyItems = 0;
688-
while (index < value.length && !hasOwnProperty(value, String(index))) {
689-
emptyItems++;
690-
index++;
691-
}
692-
if (emptyItems > 0) {
685+
for (const elem of keys) {
686+
if (visibleLength === ctx.maxArrayLength)
687+
break;
688+
// Symbols might have been added to the keys
689+
if (typeof elem !== 'string')
690+
continue;
691+
const i = +elem;
692+
if (index !== i) {
693+
// Skip zero and negative numbers as well as non numbers
694+
if (i > 0 === false)
695+
continue;
696+
const emptyItems = i - index;
693697
const ending = emptyItems > 1 ? 's' : '';
694698
const message = `<${emptyItems} empty item${ending}>`;
695699
output.push(ctx.stylize(message, 'undefined'));
696-
} else {
697-
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
698-
String(index), true));
699-
index++;
700+
index = i;
701+
if (++visibleLength === ctx.maxArrayLength)
702+
break;
700703
}
704+
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
705+
elem, true));
701706
visibleLength++;
707+
index++;
708+
}
709+
if (index < value.length && visibleLength !== ctx.maxArrayLength) {
710+
const len = value.length - index;
711+
const ending = len > 1 ? 's' : '';
712+
const message = `<${len} empty item${ending}>`;
713+
output.push(ctx.stylize(message, 'undefined'));
714+
index = value.length;
702715
}
703716
var remaining = value.length - index;
704717
if (remaining > 0) {
@@ -814,7 +827,7 @@ function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
814827
str = ctx.stylize('[Setter]', 'special');
815828
}
816829
}
817-
if (!hasOwnProperty(visibleKeys, key)) {
830+
if (visibleKeys[key] === undefined) {
818831
if (typeof key === 'symbol') {
819832
name = `[${ctx.stylize(key.toString(), 'symbol')}]`;
820833
} else {
@@ -996,11 +1009,6 @@ function _extend(target, source) {
9961009
return target;
9971010
}
9981011

999-
function hasOwnProperty(obj, prop) {
1000-
return objectHasOwnProperty.call(obj, prop);
1001-
}
1002-
1003-
10041012
// Deprecated old stuff.
10051013

10061014
function print(...args) {

test/parallel/test-util-inspect.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,24 @@ assert.strictEqual(
298298
get: function() { this.push(true); return this.length; }
299299
}
300300
);
301+
Object.defineProperty(
302+
value,
303+
'-1',
304+
{
305+
enumerable: true,
306+
value: -1
307+
}
308+
);
301309
assert.strictEqual(util.inspect(value),
302-
'[ 1, 2, 3, growingLength: [Getter] ]');
310+
'[ 1, 2, 3, growingLength: [Getter], \'-1\': -1 ]');
311+
}
312+
313+
// Array with inherited number properties
314+
{
315+
class CustomArray extends Array {}
316+
CustomArray.prototype[5] = 'foo';
317+
const arr = new CustomArray(50);
318+
assert.strictEqual(util.inspect(arr), 'CustomArray [ <50 empty items> ]');
303319
}
304320

305321
// Function with properties

0 commit comments

Comments
 (0)