-
Notifications
You must be signed in to change notification settings - Fork 29.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
util: improve inspect performance #14881
Conversation
lib/util.js
Outdated
formatter = formatArray; | ||
} else if (isMapIterator(value)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a long series of if - else if - else if statements can also be formatted as a switch (true) { ... }
(note that switch does a strict comparison, ie matches not truthy but true
). Strictly a layout issue, otherwise completely equivalent, both O(n) in the number of tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I you prefer that I can change it, otherwise I would just keep it as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great work, thank you!
lib/util.js
Outdated
if (ctx.seen === undefined) { | ||
ctx.seen = []; | ||
// A customInspect could be set only for partial objects. | ||
// To prevent a breaking change, fill the lazy added array with all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grammar nit: lazy → lazily
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
lib/util.js
Outdated
const customInspect = isCustomInspect(ctx, value); | ||
if (customInspect !== undefined) { | ||
if (ctx.seen === undefined) { | ||
ctx.seen = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ctx.seen = [...ctx.seenSet]
?
Also, I, personally, would be fine with having seen
just be a getter/setter pair.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ref #14881 (comment)
lib/util.js
Outdated
@@ -300,12 +298,9 @@ function stylizeNoColor(str, styleType) { | |||
|
|||
function arrayToHash(array) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this could just be turned into a Set
as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that and it was actually a bit slower in my benchmarks. When 6.1 lands I might have another look at it.
lib/util.js
Outdated
@@ -318,6 +313,21 @@ function ensureDebugIsInitialized() { | |||
} | |||
|
|||
|
|||
function isCustomInspect(ctx, value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This really sounds like it would return a boolean ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Also, sorry for the merge conflict, I fixed Evan’s nit while landing my PR. |
lib/util.js
Outdated
} | ||
// Using an array here is actually better for the average case then using | ||
// a Set. `seen` will only check for the depth and will never grow to large. | ||
if (ctx.seen.includes(value)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reverted my change to a Set here: the seen
part will only contain up to ctx.depth + 1
elements and in the regular case that is three. Using a Set for such a low amount of elements seems unnecessary, as it increases the code complexity due to having to handle both, an array and a set in case customInspect is set.
33adc94
to
6b87703
Compare
I rebased and added a new array fast path. So PTAL Just for fun - this is how the profiling looks like with
0.6 % JavaScript 🤣 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 nit otherwise LGTM
lib/util.js
Outdated
@@ -211,28 +211,28 @@ function debuglog(set) { | |||
* @param {Object} obj The object to print out. | |||
* @param {Object} opts Optional options object that alters the output. | |||
*/ | |||
/* legacy: obj, showHidden, depth, colors*/ | |||
function inspect(obj, opts) { | |||
function inspect(obj, opts, /* legacy */depth, /* legacy */colors) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not worth it.
- changes
util.inspect.length
- drives my IDE crazy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I do not have a strong opinion.
I went ahead and changed it back to a similar way as before to make you happy ;-)
So I pushed a bit more code and updated the benchmarks. The result is pretty decent now. |
Ok, the new benchmarks are outdated again as I was able to improve some more things that had significant impact on |
30d4685
to
2716b99
Compare
So after looking at it again I added some final changes. The benchmarks are updated in the main part. As the code still changed significantly, please take a close look again. @addaleax @refack @andrasq And I have an issue with one test running this locally but I can not explain why the test is failing. I get the following output. @bnoordhuis you wrote that test, would you be so kind and have a look at it? Because I do not see the connection of the error to my changes...
|
@BridgeAR That’s a really new test, and I’m pretty sure it’s not related to your PR |
@addaleax the weird part is that is it happening only with this PR. On master it does not fail. It is reproducible for me on my local machine and also the CI fails exactly at this test. [EDIT] And the test passes if I call |
@bnoordhuis it would be nice if you could have a look at this PR because it somehow breaks this test even though there should not be any connection. The only way for me to fix the test is by calling |
@nodejs/collaborators PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with both CI and CITGM green. Those type of large scale perf improvements can lead to broken production code.
This should be semver-minor at least, and it should bake for LTS for some month before being backported.
In addition use the newer common.expectsError version. PR-URL: nodejs/node#14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
This needs a backport pr to land in v8.x |
PR-URL: #14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
In addition use the newer common.expectsError version. PR-URL: #14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
PR-URL: nodejs#14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
The main optimizations are - Removed visibleKeys - Removed proxy cache - Removed Object.assign - No key concatenating anymore - No key recalculating anymore - Improved indentation logic - Improved string escape logic - Added many fast paths - Optimized code branches a lot - Optimized (boxed) primitive handling - Inline code if possible - Only check extra keys if necessary - Guard against unnecessary more expensive calls This also fixes a bug with special array number keys as e.g. "00". Besides that there were lots of smaller optimizations, the code got a bit cleaned up and a few more tests got in. PR-URL: nodejs#14881 Fixes: nodejs#15288 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
This fixes a issue brought up in nodejs#15288. PR-URL: nodejs#14881 Refs: nodejs#15288 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
Remove unnecessary code parts and outdated code PR-URL: nodejs#14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
PR-URL: #14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
The main optimizations are - Removed visibleKeys - Removed proxy cache - Removed Object.assign - No key concatenating anymore - No key recalculating anymore - Improved indentation logic - Improved string escape logic - Added many fast paths - Optimized code branches a lot - Optimized (boxed) primitive handling - Inline code if possible - Only check extra keys if necessary - Guard against unnecessary more expensive calls This also fixes a bug with special array number keys as e.g. "00". Besides that there were lots of smaller optimizations, the code got a bit cleaned up and a few more tests got in. PR-URL: #14881 Fixes: #15288 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
This fixes a issue brought up in #15288. PR-URL: #14881 Refs: #15288 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
Remove unnecessary code parts and outdated code PR-URL: #14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
PR-URL: nodejs/node#14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
PR-URL: nodejs/node#14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
The main optimizations are - Removed visibleKeys - Removed proxy cache - Removed Object.assign - No key concatenating anymore - No key recalculating anymore - Improved indentation logic - Improved string escape logic - Added many fast paths - Optimized code branches a lot - Optimized (boxed) primitive handling - Inline code if possible - Only check extra keys if necessary - Guard against unnecessary more expensive calls This also fixes a bug with special array number keys as e.g. "00". Besides that there were lots of smaller optimizations, the code got a bit cleaned up and a few more tests got in. PR-URL: nodejs/node#14881 Fixes: nodejs/node#15288 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
This fixes a issue brought up in #15288. PR-URL: nodejs/node#14881 Refs: nodejs/node#15288 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
Remove unnecessary code parts and outdated code PR-URL: nodejs/node#14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
In addition use the newer common.expectsError version. PR-URL: nodejs/node#14881 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
should this land in LTS? If so it will need to bake a bit longer. Please change labels as appropriate |
I reworked a couple of things in util inspect for performance. The code is fully covered.
EDIT: Updated the comment here and benchmarks.
The main optimizations are
Besides that there were lots of smaller optimizations, the
code got a bit cleaned up and a few more tests got in.
Also two bug fixes came.
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
util, benchmark