Skip to content

Commit 80d9d56

Browse files
BridgeARaduh95
authored andcommitted
assert,util: improve performance
This improves the performance for array comparison by making the sparse array detection simpler. On top of that it adds a fast path for sets and maps that only contain objects as key. PR-URL: #57370 Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
1 parent d52a71f commit 80d9d56

File tree

1 file changed

+83
-46
lines changed

1 file changed

+83
-46
lines changed

lib/internal/util/comparisons.js

+83-46
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,35 @@ function partialObjectSetEquiv(a, b, mode, set, memo) {
559559
}
560560
}
561561

562+
function setObjectEquiv(a, b, mode, set, memo) {
563+
if (mode === kPartial) {
564+
return partialObjectSetEquiv(a, b, mode, set, memo);
565+
}
566+
// Fast path for objects only
567+
if (mode === kStrict && set.size === a.size) {
568+
for (const val of a) {
569+
if (!setHasEqualElement(set, val, mode, memo)) {
570+
return false;
571+
}
572+
}
573+
return true;
574+
}
575+
576+
for (const val of a) {
577+
// Primitive values have already been handled above.
578+
if (typeof val === 'object') {
579+
if (!b.has(val) && !setHasEqualElement(set, val, mode, memo)) {
580+
return false;
581+
}
582+
} else if (mode === kLoose &&
583+
!b.has(val) &&
584+
!setHasEqualElement(set, val, mode, memo)) {
585+
return false;
586+
}
587+
}
588+
return set.size === 0;
589+
}
590+
562591
function setEquiv(a, b, mode, memo) {
563592
// This is a lazily initiated Set of entries which have to be compared
564593
// pairwise.
@@ -584,22 +613,7 @@ function setEquiv(a, b, mode, memo) {
584613
}
585614

586615
if (set !== null) {
587-
if (mode === kPartial) {
588-
return partialObjectSetEquiv(a, b, mode, set, memo);
589-
}
590-
for (const val of a) {
591-
// Primitive values have already been handled above.
592-
if (typeof val === 'object' && val !== null) {
593-
if (!b.has(val) && !setHasEqualElement(set, val, mode, memo)) {
594-
return false;
595-
}
596-
} else if (mode === kLoose &&
597-
!b.has(val) &&
598-
!setHasEqualElement(set, val, mode, memo)) {
599-
return false;
600-
}
601-
}
602-
return set.size === 0;
616+
return setObjectEquiv(a, b, mode, set, memo);
603617
}
604618

605619
return true;
@@ -640,6 +654,35 @@ function partialObjectMapEquiv(a, b, mode, set, memo) {
640654
}
641655
}
642656

657+
function mapObjectEquivalence(a, b, mode, set, memo) {
658+
if (mode === kPartial) {
659+
return partialObjectMapEquiv(a, b, mode, set, memo);
660+
}
661+
// Fast path for objects only
662+
if (mode === kStrict && set.size === a.size) {
663+
for (const { 0: key1, 1: item1 } of a) {
664+
if (!mapHasEqualEntry(set, b, key1, item1, mode, memo)) {
665+
return false;
666+
}
667+
}
668+
return true;
669+
}
670+
for (const { 0: key1, 1: item1 } of a) {
671+
if (typeof key1 === 'object' && key1 !== null) {
672+
if (!mapHasEqualEntry(set, b, key1, item1, mode, memo))
673+
return false;
674+
} else if (set.size === 0) {
675+
return true;
676+
} else if (mode === kLoose &&
677+
(!b.has(key1) ||
678+
!innerDeepEqual(item1, b.get(key1), mode, memo)) &&
679+
!mapHasEqualEntry(set, b, key1, item1, mode, memo)) {
680+
return false;
681+
}
682+
}
683+
return set.size === 0;
684+
}
685+
643686
function mapEquiv(a, b, mode, memo) {
644687
let set = null;
645688

@@ -675,21 +718,7 @@ function mapEquiv(a, b, mode, memo) {
675718
}
676719

677720
if (set !== null) {
678-
if (mode === kPartial) {
679-
return partialObjectMapEquiv(a, b, mode, set, memo);
680-
}
681-
for (const { 0: key1, 1: item1 } of a) {
682-
if (typeof key1 === 'object' && key1 !== null) {
683-
if (!mapHasEqualEntry(set, b, key1, item1, mode, memo))
684-
return false;
685-
} else if (mode === kLoose &&
686-
(!b.has(key1) ||
687-
!innerDeepEqual(item1, b.get(key1), mode, memo)) &&
688-
!mapHasEqualEntry(set, b, key1, item1, mode, memo)) {
689-
return false;
690-
}
691-
}
692-
return set.size === 0;
721+
return mapObjectEquivalence(a, b, mode, set, memo);
693722
}
694723

695724
return true;
@@ -737,6 +766,24 @@ function partialArrayEquiv(a, b, mode, memos) {
737766
return true;
738767
}
739768

769+
function sparseArrayEquiv(a, b, mode, memos, i) {
770+
// TODO(BridgeAR): Use internal method to only get index properties. The
771+
// same applies to the partial implementation.
772+
const keysA = ObjectKeys(a);
773+
const keysB = ObjectKeys(b);
774+
if (keysA.length !== keysB.length) {
775+
return false;
776+
}
777+
for (; i < keysA.length; i++) {
778+
const key = keysA[i];
779+
if (!ObjectPrototypeHasOwnProperty(b, key) ||
780+
!innerDeepEqual(a[key], b[key], mode, memos)) {
781+
return false;
782+
}
783+
}
784+
return true;
785+
}
786+
740787
function objEquiv(a, b, mode, keys2, memos, iterationType) {
741788
// The pair must have equivalent values for every corresponding key.
742789
if (keys2.length > 0) {
@@ -755,23 +802,13 @@ function objEquiv(a, b, mode, keys2, memos, iterationType) {
755802
if (!innerDeepEqual(a[i], b[i], mode, memos)) {
756803
return false;
757804
}
758-
const isOwnProperty = ObjectPrototypeHasOwnProperty(a, i);
759-
if (isOwnProperty !== ObjectPrototypeHasOwnProperty(b, i)) {
805+
const isSparseA = a[i] === undefined && !ObjectPrototypeHasOwnProperty(a, i);
806+
const isSparseB = b[i] === undefined && !ObjectPrototypeHasOwnProperty(b, i);
807+
if (isSparseA !== isSparseB) {
760808
return false;
761809
}
762-
if (!isOwnProperty) {
763-
// Array is sparse.
764-
// TODO(BridgeAR): Use internal method to only get index properties. The
765-
// same applies to the partial implementation.
766-
const keysA = ObjectKeys(a);
767-
for (; i < keysA.length; i++) {
768-
const key = keysA[i];
769-
if (!ObjectPrototypeHasOwnProperty(b, key) ||
770-
!innerDeepEqual(a[key], b[key], mode, memos)) {
771-
return false;
772-
}
773-
}
774-
return keysA.length === ObjectKeys(b).length;
810+
if (isSparseA) {
811+
return sparseArrayEquiv(a, b, mode, memos, i);
775812
}
776813
}
777814
} else if (iterationType === kIsSet) {

0 commit comments

Comments
 (0)