Skip to content

Commit fcc8322

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 <[email protected]> Reviewed-By: Vinícius Lourenço Claro Cardoso <[email protected]>
1 parent 6b1d7cb commit fcc8322

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
@@ -555,6 +555,35 @@ function partialObjectSetEquiv(a, b, mode, set, memo) {
555555
}
556556
}
557557

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

582611
if (set !== null) {
583-
if (mode === kPartial) {
584-
return partialObjectSetEquiv(a, b, mode, set, memo);
585-
}
586-
for (const val of a) {
587-
// Primitive values have already been handled above.
588-
if (typeof val === 'object' && val !== null) {
589-
if (!b.has(val) && !setHasEqualElement(set, val, mode, memo)) {
590-
return false;
591-
}
592-
} else if (mode === kLoose &&
593-
!b.has(val) &&
594-
!setHasEqualElement(set, val, mode, memo)) {
595-
return false;
596-
}
597-
}
598-
return set.size === 0;
612+
return setObjectEquiv(a, b, mode, set, memo);
599613
}
600614

601615
return true;
@@ -636,6 +650,35 @@ function partialObjectMapEquiv(a, b, mode, set, memo) {
636650
}
637651
}
638652

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

@@ -671,21 +714,7 @@ function mapEquiv(a, b, mode, memo) {
671714
}
672715

673716
if (set !== null) {
674-
if (mode === kPartial) {
675-
return partialObjectMapEquiv(a, b, mode, set, memo);
676-
}
677-
for (const { 0: key1, 1: item1 } of a) {
678-
if (typeof key1 === 'object' && key1 !== null) {
679-
if (!mapHasEqualEntry(set, b, key1, item1, mode, memo))
680-
return false;
681-
} else if (mode === kLoose &&
682-
(!b.has(key1) ||
683-
!innerDeepEqual(item1, b.get(key1), mode, memo)) &&
684-
!mapHasEqualEntry(set, b, key1, item1, mode, memo)) {
685-
return false;
686-
}
687-
}
688-
return set.size === 0;
717+
return mapObjectEquivalence(a, b, mode, set, memo);
689718
}
690719

691720
return true;
@@ -733,6 +762,24 @@ function partialArrayEquiv(a, b, mode, memos) {
733762
return true;
734763
}
735764

765+
function sparseArrayEquiv(a, b, mode, memos, i) {
766+
// TODO(BridgeAR): Use internal method to only get index properties. The
767+
// same applies to the partial implementation.
768+
const keysA = ObjectKeys(a);
769+
const keysB = ObjectKeys(b);
770+
if (keysA.length !== keysB.length) {
771+
return false;
772+
}
773+
for (; i < keysA.length; i++) {
774+
const key = keysA[i];
775+
if (!ObjectPrototypeHasOwnProperty(b, key) ||
776+
!innerDeepEqual(a[key], b[key], mode, memos)) {
777+
return false;
778+
}
779+
}
780+
return true;
781+
}
782+
736783
function objEquiv(a, b, mode, keys2, memos, iterationType) {
737784
// The pair must have equivalent values for every corresponding key.
738785
if (keys2.length > 0) {
@@ -751,23 +798,13 @@ function objEquiv(a, b, mode, keys2, memos, iterationType) {
751798
if (!innerDeepEqual(a[i], b[i], mode, memos)) {
752799
return false;
753800
}
754-
const isOwnProperty = ObjectPrototypeHasOwnProperty(a, i);
755-
if (isOwnProperty !== ObjectPrototypeHasOwnProperty(b, i)) {
801+
const isSparseA = a[i] === undefined && !ObjectPrototypeHasOwnProperty(a, i);
802+
const isSparseB = b[i] === undefined && !ObjectPrototypeHasOwnProperty(b, i);
803+
if (isSparseA !== isSparseB) {
756804
return false;
757805
}
758-
if (!isOwnProperty) {
759-
// Array is sparse.
760-
// TODO(BridgeAR): Use internal method to only get index properties. The
761-
// same applies to the partial implementation.
762-
const keysA = ObjectKeys(a);
763-
for (; i < keysA.length; i++) {
764-
const key = keysA[i];
765-
if (!ObjectPrototypeHasOwnProperty(b, key) ||
766-
!innerDeepEqual(a[key], b[key], mode, memos)) {
767-
return false;
768-
}
769-
}
770-
return keysA.length === ObjectKeys(b).length;
806+
if (isSparseA) {
807+
return sparseArrayEquiv(a, b, mode, memos, i);
771808
}
772809
}
773810
} else if (iterationType === kIsSet) {

0 commit comments

Comments
 (0)