-
Notifications
You must be signed in to change notification settings - Fork 30.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
assert: improve partialDeepStrictEqual performance and add benchmark #56555
base: main
Are you sure you want to change the base?
Conversation
Review requested:
|
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #56555 +/- ##
==========================================
+ Coverage 89.17% 90.23% +1.05%
==========================================
Files 662 630 -32
Lines 191672 184968 -6704
Branches 36884 36192 -692
==========================================
- Hits 170922 166904 -4018
+ Misses 13614 11084 -2530
+ Partials 7136 6980 -156
|
ac146c0
to
762399f
Compare
Is anything else needed here? |
@puskin94 Can you share the results of your benchmarking? |
of course 😄 ➜ node git:(partial-deep-strict-equal-perf) ./node --no-warnings benchmark/assert/partial-deep-strict-equal.js
assert/partial-deep-strict-equal.js datasetName="objects" size=1000 n=50: 169.87881572367604
assert/partial-deep-strict-equal.js datasetName="sets" size=1000 n=50: 141.24912248982653
assert/partial-deep-strict-equal.js datasetName="maps" size=1000 n=50: 151.36189759383777
assert/partial-deep-strict-equal.js datasetName="arrayBuffers" size=1000 n=50: 14.16071061277997
assert/partial-deep-strict-equal.js datasetName="dataViewArrayBuffers" size=1000 n=50: 16.398050206238278 |
lib/assert.js
Outdated
// Create a map for faster lookups | ||
const actualMap = new SafeMap(); | ||
const actualIterator = FunctionPrototypeCall(SafeSet.prototype[SymbolIterator], actual); |
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.
Why use a Map
if we only store true
in it?
// Create a map for faster lookups | |
const actualMap = new SafeMap(); | |
const actualIterator = FunctionPrototypeCall(SafeSet.prototype[SymbolIterator], actual); | |
const actualSet = new SafeSet( | |
FunctionPrototypeCall(SafeSet.prototype[SymbolIterator], actual), | |
); |
762399f
to
d061759
Compare
lib/assert.js
Outdated
|
||
for (const expectedItem of safeExpected) { | ||
// Check if the item is a zero or a -0, as these need to be handled separately | ||
for (const expectedItem of new SafeArrayIterator(expected)) { |
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.
If we're looking for performance, we should probably be using a classic for(;;)
loop. I'd recommend rolling back this change (i.e. put back the safeExpected
assignment, because it has nothing to do with performance) for this PR, and make a new one switching to a for(;;)
loop on which we can run benchmarks
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.
if you do this tho you would have to call the ArrayFrom
primordial. The performance enhancement was to adjust the "low hanging fruit" stuff I could get, "structure" wise, without influencing readability. IMHO the current implementation is better, but happy to change if you don't agree!
reverted the assignment tho
d061759
to
f1f220c
Compare
lib/assert.js
Outdated
if (isPrimitive(expectedItem)) { | ||
if (actualSet.has(expectedItem)) { | ||
actualSet.delete(expectedItem); |
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 could be improved further by just adding entries that are not detected. So we just lazily allocate the set for objects that are not reference identical on the other side.
This is how it's implemented in the current deepStrictEqual comparison.
f1f220c
to
e932899
Compare
Is anything else needed here? |
is any change required here? |
@BridgeAR can you clarify whether you're still blocking? |
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
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.
Sorry for the late reply. I guess this improves a couple of cases, so that's great! :)
I believe there is more room for improvement by reusing the existing implementation. That could be used for a follow-up PR.
Before landing: could we run the benchmark to see the improvement as percentage? :)
Benchmark CI: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1664/ Results
|
e932899
to
a07cf19
Compare
I must have messed up something with my local codebase. I just pushed again with new performance improvements, this should make some difference |
a07cf19
to
b53b85c
Compare
b53b85c
to
989b529
Compare
989b529
to
2e4fbdb
Compare
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.
If I am not mistaken, this mainly improves the performance for sets and maps that have mixed entries (primitives + objects). The current benchmark has no such entry.
I also guess that the primordial usage of the iterator is costly. Could you check if that's the case?
lib/assert.js
Outdated
function isPrimitive(value) { | ||
return typeof value !== 'object' || value === null; | ||
} |
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 should probably be inlined again. I guess that is where the performance degradation in the benchmark is coming from.
2e4fbdb
to
4502b76
Compare
now that #54630 has landed, I took the liberty to review the code I wrote to look for improvements that would squeeze some more performance out of the comparison mechanism
partialDeepStrictEqual
Refs: #54630