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: https://github.com/nodejs/node/pull/57370
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
This commit is contained in:
Ruben Bridgewater
2025-03-08 18:02:42 +01:00
committed by Node.js GitHub Bot
parent 77607d5306
commit 64f56e4156

View File

@@ -559,6 +559,35 @@ function partialObjectSetEquiv(a, b, mode, set, memo) {
}
}
function setObjectEquiv(a, b, mode, set, memo) {
if (mode === kPartial) {
return partialObjectSetEquiv(a, b, mode, set, memo);
}
// Fast path for objects only
if (mode === kStrict && set.size === a.size) {
for (const val of a) {
if (!setHasEqualElement(set, val, mode, memo)) {
return false;
}
}
return true;
}
for (const val of a) {
// Primitive values have already been handled above.
if (typeof val === 'object') {
if (!b.has(val) && !setHasEqualElement(set, val, mode, memo)) {
return false;
}
} else if (mode === kLoose &&
!b.has(val) &&
!setHasEqualElement(set, val, mode, memo)) {
return false;
}
}
return set.size === 0;
}
function setEquiv(a, b, mode, memo) {
// This is a lazily initiated Set of entries which have to be compared
// pairwise.
@@ -584,22 +613,7 @@ function setEquiv(a, b, mode, memo) {
}
if (set !== null) {
if (mode === kPartial) {
return partialObjectSetEquiv(a, b, mode, set, memo);
}
for (const val of a) {
// Primitive values have already been handled above.
if (typeof val === 'object' && val !== null) {
if (!b.has(val) && !setHasEqualElement(set, val, mode, memo)) {
return false;
}
} else if (mode === kLoose &&
!b.has(val) &&
!setHasEqualElement(set, val, mode, memo)) {
return false;
}
}
return set.size === 0;
return setObjectEquiv(a, b, mode, set, memo);
}
return true;
@@ -640,6 +654,35 @@ function partialObjectMapEquiv(a, b, mode, set, memo) {
}
}
function mapObjectEquivalence(a, b, mode, set, memo) {
if (mode === kPartial) {
return partialObjectMapEquiv(a, b, mode, set, memo);
}
// Fast path for objects only
if (mode === kStrict && set.size === a.size) {
for (const { 0: key1, 1: item1 } of a) {
if (!mapHasEqualEntry(set, b, key1, item1, mode, memo)) {
return false;
}
}
return true;
}
for (const { 0: key1, 1: item1 } of a) {
if (typeof key1 === 'object' && key1 !== null) {
if (!mapHasEqualEntry(set, b, key1, item1, mode, memo))
return false;
} else if (set.size === 0) {
return true;
} else if (mode === kLoose &&
(!b.has(key1) ||
!innerDeepEqual(item1, b.get(key1), mode, memo)) &&
!mapHasEqualEntry(set, b, key1, item1, mode, memo)) {
return false;
}
}
return set.size === 0;
}
function mapEquiv(a, b, mode, memo) {
let set = null;
@@ -675,21 +718,7 @@ function mapEquiv(a, b, mode, memo) {
}
if (set !== null) {
if (mode === kPartial) {
return partialObjectMapEquiv(a, b, mode, set, memo);
}
for (const { 0: key1, 1: item1 } of a) {
if (typeof key1 === 'object' && key1 !== null) {
if (!mapHasEqualEntry(set, b, key1, item1, mode, memo))
return false;
} else if (mode === kLoose &&
(!b.has(key1) ||
!innerDeepEqual(item1, b.get(key1), mode, memo)) &&
!mapHasEqualEntry(set, b, key1, item1, mode, memo)) {
return false;
}
}
return set.size === 0;
return mapObjectEquivalence(a, b, mode, set, memo);
}
return true;
@@ -737,6 +766,24 @@ function partialArrayEquiv(a, b, mode, memos) {
return true;
}
function sparseArrayEquiv(a, b, mode, memos, i) {
// TODO(BridgeAR): Use internal method to only get index properties. The
// same applies to the partial implementation.
const keysA = ObjectKeys(a);
const keysB = ObjectKeys(b);
if (keysA.length !== keysB.length) {
return false;
}
for (; i < keysA.length; i++) {
const key = keysA[i];
if (!ObjectPrototypeHasOwnProperty(b, key) ||
!innerDeepEqual(a[key], b[key], mode, memos)) {
return false;
}
}
return true;
}
function objEquiv(a, b, mode, keys2, memos, iterationType) {
// The pair must have equivalent values for every corresponding key.
if (keys2.length > 0) {
@@ -755,23 +802,13 @@ function objEquiv(a, b, mode, keys2, memos, iterationType) {
if (!innerDeepEqual(a[i], b[i], mode, memos)) {
return false;
}
const isOwnProperty = ObjectPrototypeHasOwnProperty(a, i);
if (isOwnProperty !== ObjectPrototypeHasOwnProperty(b, i)) {
const isSparseA = a[i] === undefined && !ObjectPrototypeHasOwnProperty(a, i);
const isSparseB = b[i] === undefined && !ObjectPrototypeHasOwnProperty(b, i);
if (isSparseA !== isSparseB) {
return false;
}
if (!isOwnProperty) {
// Array is sparse.
// TODO(BridgeAR): Use internal method to only get index properties. The
// same applies to the partial implementation.
const keysA = ObjectKeys(a);
for (; i < keysA.length; i++) {
const key = keysA[i];
if (!ObjectPrototypeHasOwnProperty(b, key) ||
!innerDeepEqual(a[key], b[key], mode, memos)) {
return false;
}
}
return keysA.length === ObjectKeys(b).length;
if (isSparseA) {
return sparseArrayEquiv(a, b, mode, memos, i);
}
}
} else if (iterationType === kIsSet) {