-
Notifications
You must be signed in to change notification settings - Fork 0
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
Caw/improve array comparison performance #1
base: main
Are you sure you want to change the base?
Conversation
Sparse arrays and arrays containing undefined are now compared faster using assert.deepStrictEqual() or util.isDeepStrictEqual().
This improves the performance to compare unequal numbers while doing a deep equal comparison. Comparing for NaN is faster by checking `variable !== variable` than by using `Number.isNaN()`.
📝 OVERVIEW
🛠️ TECHNICAL DETAILS
🏗️ ARCHITECTURAL IMPACT
🚀 IMPLEMENTATION HIGHLIGHTS
|
File | Change Type | Changes | Summary |
---|---|---|---|
benchmark/assert/deepequal-simple-array-and-set.js |
Modified | +20/-6 | The changes introduce sparse array benchmarks by adding deepEqual_sparseArray /notDeepEqual_sparseArray methods, refactoring array initialization to use Array.from with undefined elements and sparse index assignment, and modifying test logic to handle sparse array comparisons via sliced copies and adjusted indices for inequality checks. Key updates include creating sparse arrays with new Array(len) and interval assignments, while retaining existing array/Set tests with modified setup code. |
lib/internal/assert/myers_diff.js |
Modified | +1/-0 | The change adds a TODO comment in the myersDiff function to address capping input lengths preventing overflow beyond 2^31 - 1 , specifically referencing the Int32Array initialization for diagonal trace storage. The modification highlights a potential safeguard for large input sizes exceeding 32-bit integer limits in the diff algorithm's dynamic programming array. |
lib/internal/util/comparisons.js |
Modified | +20/-23 | The changes replace NumberIsNaN with direct val !== val checks for NaN detection in innerDeepEqual , findLooseMatchingPrimitives , and related functions, streamline sparse array equivalence handling in sparseArrayEquiv and objEquiv by consolidating conditional checks and loop logic, and restructure keyCheck to align strict mode symbol key comparisons within a conditional block. |
const expected = []; | ||
let actual = Array.from({ length: len }, (_, i) => i); | ||
// Contain one undefined value to trigger a specific code path | ||
actual[0] = undefined; |
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.
Setting actual[0] to undefined here affects all test cases, including non-sparse ones. This changes the behavior of existing tests like 'deepEqual_Array' which should test dense arrays without undefined elements. Move this setup into the sparse test cases only.
SUGGESTION:
- actual[0] = undefined;
@@ -29,6 +29,7 @@ function myersDiff(actual, expected, checkCommaDisparity = false) { | |||
const actualLength = actual.length; | |||
const expectedLength = expected.length; | |||
const max = actualLength + expectedLength; | |||
// TODO(BridgeAR): Cap the input in case the values go beyond the limit of 2^31 - 1. |
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.
The TODO about capping input lengths is valid but unimplemented. Failing to cap could cause Int32Array overflow when actualLength + expectedLength exceeds ~2^31, leading to incorrect diffs or crashes. Must implement input length validation before proceeding.
SUGGESTION:
+ const MAX_SAFE_LENGTH = 0x7FFFFFF0; // ~2^31-16
+ if (actualLength > MAX_SAFE_LENGTH || expectedLength > MAX_SAFE_LENGTH) {
+ throw new Error('Input arrays too large for Myers diff');
+ }
const actualLength = actual.length;
const expectedLength = expected.length;
const max = actualLength + expectedLength;
- const v = new Int32Array(2 * max + 1);
+ if (max > MAX_SAFE_LENGTH) {
+ throw new Error('Combined lengths exceed safe limit');
+ }
+ const v = new Int32Array(2 * max + 1);
for (; i < keysB.length; i++) { | ||
const key = keysB[i]; | ||
if ((a[key] === undefined && !hasOwn(a, key)) || !innerDeepEqual(a[key], b[key], mode, memos)) { | ||
return false; |
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.
Looping over only keysB
fails to check keys present in a
but not in b
, leading to false positives. Both objects' keys must be fully compared to ensure equivalence.
SUGGESTION:
- for (; i < keysB.length; i++) {
+ for (const key of [...keysA, ...keysB].filter((key, index, self) => self.indexOf(key) === index))) {
{
"lastReviewedCommitId": "f46895a01ed5842c222eec9d8d9e0456c8f816d7",
"reviewedCommitIds": [
"f46895a01ed5842c222eec9d8d9e0456c8f816d7"
]
} |
nodejs#57619