Skip to content
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,assert: improve comparison performance #22258

Closed
wants to merge 1 commit into from

Conversation

BridgeAR
Copy link
Member

@BridgeAR BridgeAR commented Aug 11, 2018

This improves the performance of util.isDeepStrictEqual and assert.deepStrictEqual. It is mainly just code split from #22197 to reduce the overall change. This commit can also be backported. It should have similar results on older V8 versions.

The complete key comparison got reworked and short cuts got in where possible.

I only benchmarked the strict mode as it should be the only important part. For the legacy mode the performance should stay similar to the one before (+ / - a few percent here and there). I used the benchmark from #22211.

Some of the functions used also got safer by instrumenting uncurryThis.

Benchmark
 assert/deepequal-buffer.js method='deepEqual' strict=1 len=100 n=20000                                                                  2.35 %       ±2.37%   ±3.16%   ±4.14%
 assert/deepequal-buffer.js method='deepEqual' strict=1 len=1000 n=20000                                                                -0.97 %       ±1.58%   ±2.11%   ±2.78%
 assert/deepequal-buffer.js method='notDeepEqual' strict=1 len=100 n=20000                                                      ***     13.62 %       ±4.99%   ±6.64%   ±8.65%
 assert/deepequal-buffer.js method='notDeepEqual' strict=1 len=1000 n=20000                                                       *      7.78 %       ±5.96%   ±7.94%  ±10.34%
 assert/deepequal-map.js method='deepEqual_mixed' strict=1 len=500 n=500                                                        ***     30.98 %       ±2.48%   ±3.32%   ±4.36%
 assert/deepequal-map.js method='deepEqual_objectOnly' strict=1 len=500 n=500                                                   ***     33.05 %       ±2.96%   ±3.96%   ±5.19%
 assert/deepequal-map.js method='deepEqual_primitiveOnly' strict=1 len=500 n=500                                                        -0.35 %       ±3.14%   ±4.18%   ±5.45%
 assert/deepequal-map.js method='notDeepEqual_mixed' strict=1 len=500 n=500                                                             -1.59 %       ±3.01%   ±4.00%   ±5.21%
 assert/deepequal-map.js method='notDeepEqual_objectOnly' strict=1 len=500 n=500                                                ***     30.12 %       ±2.38%   ±3.18%   ±4.16%
 assert/deepequal-map.js method='notDeepEqual_primitiveOnly' strict=1 len=500 n=500                                                     -0.64 %       ±3.29%   ±4.38%   ±5.70%
 assert/deepequal-object.js method='deepEqual' strict=1 size=100 n=5000                                                         ***     18.25 %       ±5.16%   ±6.90%   ±9.04%
 assert/deepequal-object.js method='deepEqual' strict=1 size=1000 n=5000                                                        ***     23.78 %       ±1.88%   ±2.50%   ±3.25%
 assert/deepequal-object.js method='deepEqual' strict=1 size=50000 n=5000                                                       ***     66.63 %       ±2.73%   ±3.64%   ±4.76%
 assert/deepequal-object.js method='notDeepEqual' strict=1 size=100 n=5000                                                        *      5.35 %       ±5.33%   ±7.09%   ±9.23%
 assert/deepequal-object.js method='notDeepEqual' strict=1 size=1000 n=5000                                                     ***     28.52 %       ±4.49%   ±5.98%   ±7.78%
 assert/deepequal-object.js method='notDeepEqual' strict=1 size=50000 n=5000                                                    ***     53.75 %       ±6.11%   ±8.23%  ±10.91%
 assert/deepequal-prims-and-objs-big-array-set.js method='deepEqual_Array' strict=1 len=20000 n=25 primitive='array'            ***     76.65 %       ±2.12%   ±2.83%   ±3.70%
 assert/deepequal-prims-and-objs-big-array-set.js method='deepEqual_Array' strict=1 len=20000 n=25 primitive='number'           ***     79.50 %       ±2.31%   ±3.09%   ±4.04%
 assert/deepequal-prims-and-objs-big-array-set.js method='deepEqual_Array' strict=1 len=20000 n=25 primitive='object'           ***     74.47 %       ±3.94%   ±5.29%   ±7.01%
 assert/deepequal-prims-and-objs-big-array-set.js method='deepEqual_Array' strict=1 len=20000 n=25 primitive='string'           ***     78.85 %       ±3.66%   ±4.87%   ±6.34%
 assert/deepequal-prims-and-objs-big-array-set.js method='deepEqual_Set' strict=1 len=20000 n=25 primitive='array'                      -5.18 %       ±8.30%  ±11.05%  ±14.39%
 assert/deepequal-prims-and-objs-big-array-set.js method='deepEqual_Set' strict=1 len=20000 n=25 primitive='number'              **    -11.85 %       ±7.77%  ±10.35%  ±13.47%
 assert/deepequal-prims-and-objs-big-array-set.js method='deepEqual_Set' strict=1 len=20000 n=25 primitive='object'                     -1.18 %       ±7.44%   ±9.90%  ±12.89%
 assert/deepequal-prims-and-objs-big-array-set.js method='deepEqual_Set' strict=1 len=20000 n=25 primitive='string'              **    -11.27 %       ±8.17%  ±10.87%  ±14.15%
 assert/deepequal-prims-and-objs-big-array-set.js method='notDeepEqual_Array' strict=1 len=20000 n=25 primitive='array'         ***     80.53 %       ±4.14%   ±5.56%   ±7.34%
 assert/deepequal-prims-and-objs-big-array-set.js method='notDeepEqual_Array' strict=1 len=20000 n=25 primitive='number'        ***     77.87 %       ±4.93%   ±6.59%   ±8.65%
 assert/deepequal-prims-and-objs-big-array-set.js method='notDeepEqual_Array' strict=1 len=20000 n=25 primitive='object'        ***     76.43 %       ±3.70%   ±4.97%   ±6.54%
 assert/deepequal-prims-and-objs-big-array-set.js method='notDeepEqual_Array' strict=1 len=20000 n=25 primitive='string'        ***     77.90 %       ±1.73%   ±2.30%   ±3.00%
 assert/deepequal-prims-and-objs-big-array-set.js method='notDeepEqual_Set' strict=1 len=20000 n=25 primitive='array'           ***     29.18 %      ±10.95%  ±14.58%  ±18.99%
 assert/deepequal-prims-and-objs-big-array-set.js method='notDeepEqual_Set' strict=1 len=20000 n=25 primitive='number'          ***     26.29 %       ±8.99%  ±11.96%  ±15.58%
 assert/deepequal-prims-and-objs-big-array-set.js method='notDeepEqual_Set' strict=1 len=20000 n=25 primitive='object'          ***     32.19 %       ±8.86%  ±11.79%  ±15.35%
 assert/deepequal-prims-and-objs-big-array-set.js method='notDeepEqual_Set' strict=1 len=20000 n=25 primitive='string'           **     17.01 %      ±10.21%  ±13.61%  ±17.76%
 assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=20000 primitive='array'                              ***     26.84 %       ±3.13%   ±4.18%   ±5.47%
 assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=20000 primitive='number'                             ***     26.15 %       ±4.25%   ±5.68%   ±7.45%
 assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=20000 primitive='object'                             ***     30.46 %       ±2.63%   ±3.50%   ±4.56%
 assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=20000 primitive='string'                             ***     26.75 %       ±3.93%   ±5.26%   ±6.91%
 assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=20000 primitive='array'                           ***     28.66 %       ±5.13%   ±6.87%   ±9.03%
 assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=20000 primitive='number'                          ***     30.91 %       ±3.72%   ±4.95%   ±6.46%
 assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=20000 primitive='object'                          ***     31.59 %       ±1.75%   ±2.33%   ±3.03%
 assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=20000 primitive='string'                          ***     27.50 %       ±5.25%   ±7.05%   ±9.30%
 assert/deepequal-set.js method='deepEqual_mixed' strict=1 len=500 n=500                                                        ***     36.67 %       ±2.79%   ±3.71%   ±4.83%
 assert/deepequal-set.js method='deepEqual_objectOnly' strict=1 len=500 n=500                                                   ***     38.16 %       ±2.41%   ±3.20%   ±4.17%
 assert/deepequal-set.js method='deepEqual_primitiveOnly' strict=1 len=500 n=500                                                        -2.61 %       ±2.99%   ±3.98%   ±5.18%
 assert/deepequal-set.js method='notDeepEqual_mixed' strict=1 len=500 n=500                                                             -3.18 %       ±4.08%   ±5.42%   ±7.06%
 assert/deepequal-set.js method='notDeepEqual_objectOnly' strict=1 len=500 n=500                                                ***     35.45 %       ±2.72%   ±3.64%   ±4.80%
 assert/deepequal-set.js method='notDeepEqual_primitiveOnly' strict=1 len=500 n=500                                                     -2.36 %       ±3.94%   ±5.25%   ±6.83%
 assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=1 n=500 type='Float32Array'                 ***     19.90 %       ±4.56%  ±6.09%  ±7.98%
 assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=1 n=500 type='Float64Array'                 ***     19.50 %       ±2.67%  ±3.56%  ±4.65%
 assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=1 n=500 type='Int8Array'                    ***     60.17 %       ±5.53%  ±7.36%  ±9.60%
 assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=1 n=500 type='Uint8Array'                   ***     62.95 %       ±5.42%  ±7.22%  ±9.40%
 assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=1 n=500 type='Uint8ClampedArray'            ***     65.83 %       ±8.33% ±11.09% ±14.45%
 assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=1 n=500 type='Float32Array'              ***     40.78 %       ±3.13%  ±4.20%  ±5.54%
 assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=1 n=500 type='Float64Array'              ***     42.95 %       ±1.76%  ±2.34%  ±3.05%
 assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=1 n=500 type='Int8Array'                 ***    164.55 %      ±11.26% ±15.02% ±19.61%
 assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=1 n=500 type='Uint8Array'                ***    156.12 %       ±9.02% ±12.08% ±15.89%
 assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=1 n=500 type='Uint8ClampedArray'         ***    157.07 %       ±9.25% ±12.37% ±16.22%
 assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=1 n=500 type='Float32Array'                         1.50 %       ±1.99%  ±2.64%  ±3.44%
 assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=1 n=500 type='Float64Array'                         1.57 %       ±1.85%  ±2.47%  ±3.22%
 assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=1 n=500 type='Int8Array'                           -0.58 %       ±1.76%  ±2.35%  ±3.07%
 assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=1 n=500 type='Uint8Array'                          -1.53 %       ±3.29%  ±4.41%  ±5.80%
 assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=1 n=500 type='Uint8ClampedArray'                    0.05 %       ±1.95%  ±2.60%  ±3.39%
 assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=1 n=500 type='Float32Array'             ***     37.63 %       ±4.42%  ±5.92%  ±7.78%
 assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=1 n=500 type='Float64Array'             ***     35.06 %       ±4.47%  ±5.99%  ±7.89%
 assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=1 n=500 type='Int8Array'                ***     41.41 %       ±2.58%  ±3.44%  ±4.52%
 assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=1 n=500 type='Uint8Array'               ***     39.14 %       ±4.10%  ±5.51%  ±7.28%
 assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=1 n=500 type='Uint8ClampedArray'        ***     41.83 %       ±2.61%  ±3.48%  ±4.53%
Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

@BridgeAR BridgeAR added the performance Issues and PRs related to the performance of Node.js. label Aug 11, 2018
@nodejs-github-bot nodejs-github-bot added the util Issues and PRs related to the built-in util module. label Aug 11, 2018
@BridgeAR BridgeAR added the assert Issues and PRs related to the assert subsystem. label Aug 11, 2018
@@ -5,30 +5,53 @@ const { isArrayBufferView } = require('internal/util/types');
const { internalBinding } = require('internal/bootstrap/loaders');
const { isDate, isMap, isRegExp, isSet } = internalBinding('types');

function objectToString(o) {
return Object.prototype.toString.call(o);
const ReflectApply = Reflect.apply;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW as a reviewer I'd still expect the "tamper safe"ing to be in a different PR. That said, changing it now here would be very frustrating to do likely so I'm fine with it here - just future personal preference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see if I move those out due to this becoming semver-major.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only changed the single part as it is indeed more work to pull these changes out.

// barrier including the Uint8Array operation takes the advantage of the faster
// binary compare otherwise. The break even point was at about 300 characters.
function areSimilarTypedArrays(a, b, max) {
function areSimilarFloatArrays(a, b) {
const len = a.byteLength;
Copy link
Member

@benjamingr benjamingr Aug 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the caching of a.byteLength still helpful/needed?

Copy link
Member Author

@BridgeAR BridgeAR Aug 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but it should not hurt either?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then can you remove it :)?

// Skip testing the part below and continue with the keyCheck.
return keyCheck(val1, val2, true, memos);
}
// Fast path for non sparse arrays (no key comparison for indices
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this if check assert this is a fast path and the array is not sparse?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell:

  • this just checks that all the keys are indexed properties and that the last key is the last index
  • it relies on the Object.keys key ordering in V8 - AFAIK there is no guarantee on keys but there is on the order of other stuff e.g. getOwnPropertyNames (I am fine with this and just tracking it in case it changes in V8).
  • It fails with setting negative indices, for example: var o = {}; o[-1] = 3; o[1] = 5; o.length = 2. We should add a test for this particular case. For what it's worth I'm fine with that case being slow since it's uncommon and sparse arrays are really confusing perf-wise anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we can see if we can check it directly from V8 - something like UseSparseVariant - or use %HasComplexElements

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The keys are ordered by spec as follows:

  1. The keys that are integer indices, in ascending numeric order.
  2. All string keys, in the order in which they were added to the object.
  3. All symbol keys, in the order in which they were added to the object.

The order makes this possible: Object.keys(arr)[arr.length - 1] === '' + (arr.length - 1). By checking the key at arr.length - 1 it should be the same key as the key length. Otherwise the key would have a position lower than that. If Object.keys(arr).length === arr.length on top of that, we also know that the array has no extra keys besides potentially symbols.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BridgeAR as I said above as far as I know OrdinaryOwnPropertyKeys is used in .getOwnPropertyNames but not in .keys - that said I don't particularly mind it since I don't see a future in which V8 changes the iteration order of objects anyway since it's so heavily relied on.

I think negative indices are not a problem since they're not technically indices from the spec PoV and would be enumerated elsewhere.

Upon further inspection - it looks like this works fine for all the cases - care to add a comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By spec Object.keys calls EnumerableOwnPropertyNames and that calls OwnPropertyKeys and that calls OdinaryOwnPropertyKeys :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat, I wonder when it changed - I remember this discussion when we did the io changes for ES2015 and it was guaranteed for one and not the other. Good to know it's a guarantee now - thanks.

}
if (isDate(val1)) {
if (val1.getTime() !== val2.getTime()) {
if (getTime(val1) !== getTime(val2)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone subclasses Date and they have an overridden getTime() would this work?

If not, I am still +1 for it, but it would require this to be a very explicit semver-major.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change it to a separate commit.

if (keys.length !== objectKeys(val2).length) {
return false;
}
if (keys.length === val1.length) {
Copy link
Member

@benjamingr benjamingr Aug 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice changes in isArrayBufferView!

return areSimilarTypedArrays(val1, val2);
}
if (isDate(val1) && isDate(val2)) {
return getTime(val1) === getTime(val2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto about getTime with a subclass (again +1 on the change, just making sure it's semver-major)

}

// Cheap key test
let i = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is i used outside of the loop here? It confused me since I expected it to be used at some capacity elsewhere. I don't think making i local to the loop would hurt performance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used in two loops. I could define a second variable but this seemed nicer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it a bit more confusing than defining it in the loop for the reason above but don't feel strongly about it - so if you think it's nicer I concede.

@@ -451,10 +526,7 @@ function mapHasEqualEntry(set, map, key1, item1, strict, memo) {
}

function mapEquiv(a, b, strict, memo) {
if (a.size !== b.size)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you inline the size checks rather than have them in mapEquiv (and setEquiv)?

Copy link
Member Author

@BridgeAR BridgeAR Aug 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved them to the beginning of the checks. Now, sets and maps will fail early in case the size does not match. Earlier they had significantly more work to reach this check.

@@ -489,35 +561,44 @@ function mapEquiv(a, b, strict, memo) {
return true;
}

function objEquiv(a, b, strict, keys, memos) {
function objEquiv(a, b, strict, keys, memos, iterator) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about naming it iterator but very much in favour of this change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am open to suggestions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I like iteratorKind or iterationType are much better to be honest.

@@ -890,3 +890,10 @@ assert.deepStrictEqual(obj1, obj2);
);
util.inspect.defaultOptions = tmp;
}

// Basic array out of bounds check.

This comment was marked as resolved.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already covered.

Copy link
Member

@benjamingr benjamingr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a bunch of nits I'd love to see resolved before landing but changes look good enough to not generate friction.

@benjamingr benjamingr added the semver-major PRs that contain breaking changes and should be released in the next major version. label Aug 11, 2018
Copy link
Member

@benjamingr benjamingr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually looking at my review again I want to make sure that sparse arrays with negative indices work. I just did a code search on GitHub and found some code using that pattern which made me nauseous but reconsider landing this without verification this case works.

@benjamingr
Copy link
Member

The semver-major is also preemptive, I'm fine with landing as semver-patch or semver-minor with a citgm run and a TSC member sign off.

Copy link
Member

@benjamingr benjamingr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I checked it out and am convinced this works + @BridgeAR explained the part I wasn't certain about with negative array indices.

I'm fine with this landing as-is as semver-major or with the getDate changes reverted as semver-patch.

(In case I haven't said it before - very nice changes!)

@BridgeAR
Copy link
Member Author

I just reverted the getTime change, so it should be a patch again.

@BridgeAR BridgeAR removed the semver-major PRs that contain breaking changes and should be released in the next major version. label Aug 11, 2018
@BridgeAR
Copy link
Member Author

@benjamingr thanks a lot for the thorough review!

CI https://ci.nodejs.org/job/node-test-pull-request/16367/

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Aug 11, 2018
@BridgeAR

This comment has been minimized.

This adds a smarter logic to compare object keys (including symbols)
and it also skips the object key comparison for (typed) arrays, if
possible.

Besides that it adds a fast path for empty objects, arrays, sets and
maps and fast paths for sets and maps with an unequal size.

On top of that a few functions are now safer to call by using
uncurryThis and by caching the actual function.

Overall, this is a significant performance boost for comparisons.
@BridgeAR
Copy link
Member Author

Rebased due to conflicts.

CI https://ci.nodejs.org/job/node-test-pull-request/16427/

@nodejs/util @nodejs/testing PTAL

@BridgeAR BridgeAR requested a review from mcollina August 13, 2018 20:54
Copy link
Member

@Trott Trott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM if CI is green

Copy link
Member

@benjamingr benjamingr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still LGTM as semver-patch with the getTime reverts.

@BridgeAR
Copy link
Member Author

BridgeAR added a commit to BridgeAR/node that referenced this pull request Aug 15, 2018
This adds a smarter logic to compare object keys (including symbols)
and it also skips the object key comparison for (typed) arrays, if
possible.

Besides that it adds a fast path for empty objects, arrays, sets and
maps and fast paths for sets and maps with an unequal size.

On top of that a few functions are now safer to call by using
uncurryThis and by caching the actual function.

Overall, this is a significant performance boost for comparisons.

PR-URL: nodejs#22258
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
@BridgeAR
Copy link
Member Author

Landed in d164d9d 🎉

@BridgeAR BridgeAR closed this Aug 15, 2018
targos pushed a commit that referenced this pull request Aug 19, 2018
This adds a smarter logic to compare object keys (including symbols)
and it also skips the object key comparison for (typed) arrays, if
possible.

Besides that it adds a fast path for empty objects, arrays, sets and
maps and fast paths for sets and maps with an unequal size.

On top of that a few functions are now safer to call by using
uncurryThis and by caching the actual function.

Overall, this is a significant performance boost for comparisons.

PR-URL: #22258
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
targos pushed a commit that referenced this pull request Sep 3, 2018
This adds a smarter logic to compare object keys (including symbols)
and it also skips the object key comparison for (typed) arrays, if
possible.

Besides that it adds a fast path for empty objects, arrays, sets and
maps and fast paths for sets and maps with an unequal size.

On top of that a few functions are now safer to call by using
uncurryThis and by caching the actual function.

Overall, this is a significant performance boost for comparisons.

PR-URL: #22258
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
@BridgeAR BridgeAR deleted the improve-comparison-perf branch January 20, 2020 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assert Issues and PRs related to the assert subsystem. author ready PRs that have at least one approval, no pending requests for changes, and a CI started. performance Issues and PRs related to the performance of Node.js. util Issues and PRs related to the built-in util module.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants