mirror of
https://github.com/denoland/std.git
synced 2024-11-22 04:59:05 +00:00
perf(assert): add fast path for primitive keyed collections in equal()
(#5913)
* feat(assert): add fast path for primitive keyed collections * remove fix for #5912 (to be redone as separate pr) * add extra-fast-path with Set#symmetricDifference * Changes from code review
This commit is contained in:
parent
70f981e605
commit
2fe19de48f
@ -1,7 +1,14 @@
|
|||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
// This module is browser compatible.
|
// This module is browser compatible.
|
||||||
function isKeyedCollection(x: unknown): x is Set<unknown> {
|
|
||||||
return [Symbol.iterator, "size"].every((k) => k in (x as Set<unknown>));
|
type KeyedCollection = Set<unknown> | Map<unknown, unknown>;
|
||||||
|
|
||||||
|
function isKeyedCollection(x: unknown): x is KeyedCollection {
|
||||||
|
return x instanceof Set || x instanceof Map;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValFromKeyedCollection(kc: KeyedCollection, key: unknown) {
|
||||||
|
return kc instanceof Map ? kc.get(key) : key;
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructorsEqual(a: object, b: object) {
|
function constructorsEqual(a: object, b: object) {
|
||||||
@ -82,12 +89,41 @@ export function equal(c: unknown, d: unknown): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const aKeys = [...a.keys()];
|
||||||
|
const primitiveKeysFastPath = aKeys.every((k) => {
|
||||||
|
return typeof k === "string" ||
|
||||||
|
typeof k === "number" ||
|
||||||
|
typeof k === "boolean" ||
|
||||||
|
typeof k === "bigint" ||
|
||||||
|
typeof k === "symbol" ||
|
||||||
|
k == null;
|
||||||
|
});
|
||||||
|
if (primitiveKeysFastPath) {
|
||||||
|
if (a instanceof Set) {
|
||||||
|
return a.symmetricDifference(b).size === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of aKeys) {
|
||||||
|
if (
|
||||||
|
!b.has(key) ||
|
||||||
|
!compare(
|
||||||
|
getValFromKeyedCollection(a, key),
|
||||||
|
getValFromKeyedCollection(b, key),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
let unmatchedEntries = a.size;
|
let unmatchedEntries = a.size;
|
||||||
|
|
||||||
for (const [aKey, aValue] of a.entries()) {
|
for (const [aKey, aValue] of a.entries()) {
|
||||||
for (const [bKey, bValue] of b.entries()) {
|
for (const [bKey, bValue] of b.entries()) {
|
||||||
/* Given that Map keys can be references, we need
|
/* Given that Map keys can be references, we need
|
||||||
* to ensure that they are also deeply equal */
|
* to ensure that they are also deeply equal */
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(aKey === aValue && bKey === bValue && compare(aKey, bKey)) ||
|
(aKey === aValue && bKey === bValue && compare(aKey, bKey)) ||
|
||||||
(compare(aKey, bKey) && compare(aValue, bValue))
|
(compare(aKey, bKey) && compare(aValue, bValue))
|
||||||
|
@ -276,3 +276,31 @@ Deno.test("equal() WeakMap, WeakRef and WeakSet", () => {
|
|||||||
assertFalse(equal(new WeakSet(), { constructor: WeakSet }));
|
assertFalse(equal(new WeakSet(), { constructor: WeakSet }));
|
||||||
assertFalse(equal(new WeakRef({}), { constructor: WeakRef }));
|
assertFalse(equal(new WeakRef({}), { constructor: WeakRef }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("equal() fast path for primitive keyed collections", () => {
|
||||||
|
const arr = Array.from({ length: 10_000 }, (_, i) => i);
|
||||||
|
|
||||||
|
const set1 = new Set(arr);
|
||||||
|
const set2 = new Set(arr);
|
||||||
|
assert(equal(set1, set2));
|
||||||
|
|
||||||
|
const map1 = new Map(arr.map((v) => [v, v]));
|
||||||
|
const map2 = new Map(arr.map((v) => [v, v]));
|
||||||
|
assert(equal(map1, map2));
|
||||||
|
|
||||||
|
const set3 = new Set(arr);
|
||||||
|
const set4 = new Set(arr.with(-1, -1));
|
||||||
|
assertFalse(equal(set3, set4));
|
||||||
|
|
||||||
|
// entries [...] 9998 => 9998, 9999 => 9999
|
||||||
|
const map3 = new Map(arr.map((v) => [v, v]));
|
||||||
|
// entries [...] 9998 => 9998, -1 => -1
|
||||||
|
const map4 = new Map(arr.with(-1, -1).map((v) => [v, v]));
|
||||||
|
assertFalse(equal(map3, map4));
|
||||||
|
|
||||||
|
// entries [...] 9998 => 9998, 9999 => 9999
|
||||||
|
const map5 = new Map(arr.map((v, i) => [i, v]));
|
||||||
|
// entries [...] 9998 => 9998, 9999 => -1
|
||||||
|
const map6 = new Map(arr.with(-1, -1).map((v, i) => [i, v]));
|
||||||
|
assertFalse(equal(map5, map6));
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user