Fix perf issues with lazy sets

This commit is contained in:
Mark Molinaro 2021-06-17 18:42:39 +00:00 committed by GitHub
parent e5ccd070f6
commit b58e0799e2
2 changed files with 49 additions and 17 deletions

View File

@ -24,21 +24,17 @@ const merge = (targetSet, toMerge) => {
/** /**
* @template T * @template T
* @param {Set<Iterable<T>>} targetSet set where iterables should be added * @param {Set<Iterable<T>>} targetSet set where iterables should be added
* @param {Array<Iterable<T> | LazySet<T>>} toDeepMerge iterables or lazy set to be flattened * @param {Array<LazySet<T>>} toDeepMerge lazy sets to be flattened
* @returns {void} * @returns {void}
*/ */
const flatten = (targetSet, toDeepMerge) => { const flatten = (targetSet, toDeepMerge) => {
for (const set of toDeepMerge) { for (const set of toDeepMerge) {
if (set instanceof LazySet) { if (set._set.size > 0) targetSet.add(set._set);
if (set._set.size > 0) targetSet.add(set._set); if (set._needMerge) {
if (set._needMerge) { for (const mergedSet of set._toMerge) {
for (const mergedSet of set._toMerge) { targetSet.add(mergedSet);
targetSet.add(mergedSet);
}
flatten(targetSet, set._toDeepMerge);
} }
} else { flatten(targetSet, set._toDeepMerge);
targetSet.add(set);
} }
} }
}; };
@ -58,7 +54,7 @@ class LazySet {
this._set = new Set(iterable); this._set = new Set(iterable);
/** @type {Set<Iterable<T>>} */ /** @type {Set<Iterable<T>>} */
this._toMerge = new Set(); this._toMerge = new Set();
/** @type {Array<Iterable<T> | LazySet<T>>} */ /** @type {Array<LazySet<T>>} */
this._toDeepMerge = []; this._toDeepMerge = [];
this._needMerge = false; this._needMerge = false;
this._deopt = false; this._deopt = false;
@ -76,6 +72,14 @@ class LazySet {
this._needMerge = false; this._needMerge = false;
} }
_isEmpty() {
return (
this._set.size === 0 &&
this._toMerge.size === 0 &&
this._toDeepMerge.length === 0
);
}
get size() { get size() {
if (this._needMerge) this._merge(); if (this._needMerge) this._merge();
return this._set.size; return this._set.size;
@ -101,13 +105,19 @@ class LazySet {
_set.add(item); _set.add(item);
} }
} else { } else {
this._toDeepMerge.push(iterable); if (iterable instanceof LazySet) {
this._needMerge = true; if (!iterable._isEmpty()) {
// Avoid being too memory hungry this._toDeepMerge.push(iterable);
if (this._toDeepMerge.length > 100000) { this._needMerge = true;
this._flatten(); if (this._toDeepMerge.length > 100000) {
if (this._toMerge.size > 100000) this._merge(); this._flatten();
}
}
} else {
this._toMerge.add(iterable);
this._needMerge = true;
} }
if (this._toMerge.size > 100000) this._merge();
} }
return this; return this;
} }

22
test/LazySet.unittest.js Normal file
View File

@ -0,0 +1,22 @@
const LazySet = require("../lib/util/LazySet");
describe("LazySet", () => {
it("addAll", () => {
const a = new Set(["a"]);
const sut = new LazySet(a);
const empty = new LazySet([]);
expect(sut.size).toBe(1);
sut.addAll(empty);
expect(sut._toDeepMerge).toStrictEqual([]);
expect(sut.size).toBe(1);
const b = new Set(["b"]);
sut.addAll(b);
expect(sut._toMerge).toContain(b);
expect(sut.size).toBe(2);
const c = new LazySet(["c"]);
sut.addAll(c);
expect(sut._toDeepMerge).toContain(c);
expect(sut.size).toBe(3);
expect(sut._toDeepMerge).toStrictEqual([]);
});
});