-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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 util inspect performance #22503
Conversation
acb806d
to
2224329
Compare
src/node_types.cc
Outdated
@@ -51,6 +51,15 @@ static void IsAnyArrayBuffer(const FunctionCallbackInfo<Value>& args) { | |||
args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer()); | |||
} | |||
|
|||
static void IsBoxedPrimitive(const FunctionCallbackInfo<Value>& args) { |
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.
Would it be faster to do this in javascript somehow? For example: typeof arg === 'object' && arg !== null && arg.constructor === String
?
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 did not test it but I would not want to use it as it's not a safe check. Someone could easily manipulate the constructor.
const s = new String('test')
s.constructor = Number
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.
Alright, how about:
if (typeof arg === 'object' &&
arg !== null &&
Object.prototype.toString.call(arg) === '[object String]')
? Object.prototype.toString
could even be cached at startup in case someone overrides it later on.
The only issue would be symbols and big integers as they don't have constructors that return object types, but at least maybe we could speed up the other type checks if this js method ends up being faster or avoid crossing the C++<->js boundary entirely if we place the symbol and bigint checks last.
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.
That is also not a safe check:
const a = new String('test')
a[Symbol.toStringTag] = 'Foo'
console.log(Object.prototype.toString.call(a))
// '[object Foo]'
I'll move the bigint check in the C++ function down and I'll see if I can work around the performance penalty. Do you expect a lot of people using boxed primitives? Because for all other cases there are now less calls to C++ than before.
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.
Seems like the main regression came due to moving the functionality to an extra function. It's probably not inlined anymore as other code is hotter and the main function is already big. I moved it back in to address the performance regression.
src/node_types.cc
Outdated
args[0]->IsStringObject() || | ||
args[0]->IsSymbolObject()); | ||
} | ||
|
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.
@BridgeAR 👆 Ideally this helper should have a JS form (that calls it) exposed on util.types
like the other isNumberObject
, etc.
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.
That is automatically the case. But good that you bring it up! I'll move this to an individual commit that also adds documentation for the function.
I'm not sure I'd call |
Ok. But then I'd assume log appending will generate I/O that will shadow the CPU work by |
This is goodness for |
Then this needs reframing. Call it |
@refack The PR here doesn't appear to make things any harder to reason about, simplifies an expensive set of operations, and opens the possibility of carrying over useful helpers to Would it help if this PR moved/saved any of the source refactoring and cleanup bits for another PR? |
@refack I actually tried to reduce the overall complexity while significantly improving the performance.
Definitely! I want to do that as well. |
I just pushed two new commits: one to document the new function and one to address the boxed regression.
|
@nodejs/util PTAL |
Should this be backported to |
PR-URL: nodejs#22503 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: John-David Dalton <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
This significantly reduces the benchmark runtime. It removes to many variations that do not provide any benefit and reduces the iterations. PR-URL: nodejs#22503 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: John-David Dalton <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
This significantly improves the inspection performance for all array types. From now on only the visible elements cause work instead of having to process all array keys no matter how many entries are visible. This also moves some code out of the main function to reduce the overall function complexity. PR-URL: nodejs#22503 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: John-David Dalton <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
Backport-PR-URL: #23039 PR-URL: #22503 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: John-David Dalton <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
This significantly reduces the benchmark runtime. It removes to many variations that do not provide any benefit and reduces the iterations. Backport-PR-URL: #23039 PR-URL: #22503 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: John-David Dalton <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
This significantly improves the inspection performance for all array types. From now on only the visible elements cause work instead of having to process all array keys no matter how many entries are visible. This also moves some code out of the main function to reduce the overall function complexity. Backport-PR-URL: #23039 PR-URL: #22503 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: John-David Dalton <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
This significantly improves the inspection performance for all array
types. From now on only the visible elements cause work instead of
having to process all array keys no matter how many entries are
visible. So the complexity for (typed) arrays is reduced from
O(n + m)
toO(m)
, whilen
is the number of keys andm
is thenumber of visible entries.
m
is set to 100 by default(
maxArrayLength
).This also moves some code out of the main function to reduce the
overall function complexity.
It also adds a specific boxed primitives c++ function to reduce the
boundary crossing for the average use case (boxed primitives are rare).
I also reduced the runtime for the util benchmarks as they were significantly
to high and removed some obsolete options.
Intermediate benchmark results:
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes