diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a9ec9a78..d08c14f1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - node_version: ['10', '12', '14', '15'] + node_version: ['10', '12', '14', '15', '16'] os: [ubuntu-latest] steps: diff --git a/packages/trie/src/tests.ts b/packages/trie/src/tests.ts index 1c6397e1..c765c6f7 100644 --- a/packages/trie/src/tests.ts +++ b/packages/trie/src/tests.ts @@ -87,4 +87,75 @@ describe("Trie", function () { // Verify that all Data objects are distinct. assert.strictEqual(new Set(datas).size, datas.length); }); + + describe("performance", () => { + const objectKeys: object[][] = []; + for (let k = 0; k < 2e4; ++k) { + const key: object[] = []; + for (let i = 0; i < 100; ++i) { + key.push({}); + } + objectKeys.push(key); + } + + const numberKeys: number[][] = []; + for (let k = 0; k < 2e4; ++k) { + const key: number[] = []; + for (let i = 0; i < 100; ++i) { + key.push(i * k); + } + numberKeys.push(key); + } + + const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10); + + (nodeMajorVersion > 10 ? it : it.skip) + ("lots of fresh object references added weakly", function () { + this.timeout(20000); + const trie = new Trie(true); + objectKeys.forEach(key => { + trie.lookupArray(key); + }); + }); + + it("lots of fresh object references added strongly", function () { + this.timeout(20000); + const trie = new Trie(false); + objectKeys.forEach(key => { + trie.lookupArray(key); + }); + }); + + it("lots of numeric key arrays added weakly", function () { + this.timeout(20000); + const trie = new Trie(true); + numberKeys.forEach(key => { + trie.lookupArray(key); + }); + }); + + it("lots of numeric key arrays added strongly", function () { + this.timeout(20000); + const trie = new Trie(false); + numberKeys.forEach(key => { + trie.lookupArray(key); + }); + }); + + it("looking up the same key lots of times, weakly", function () { + this.timeout(20000); + const trie = new Trie(true); + objectKeys.forEach(() => { + trie.lookupArray(objectKeys[0]); + }); + }); + + it("looking up the same key lots of times, strongly", function () { + this.timeout(20000); + const trie = new Trie(false); + objectKeys.forEach(() => { + trie.lookupArray(objectKeys[0]); + }); + }); + }); }); diff --git a/packages/trie/src/trie.ts b/packages/trie/src/trie.ts index c208e3ce..124af1dc 100644 --- a/packages/trie/src/trie.ts +++ b/packages/trie/src/trie.ts @@ -6,8 +6,7 @@ // null-prototype Object. const defaultMakeData = () => Object.create(null); -// Useful for processing arguments objects as well as arrays. -const { forEach, slice } = Array.prototype; +const { slice } = Array.prototype; export class Trie { // Since a `WeakMap` cannot hold primitive values as keys, we need a @@ -27,8 +26,11 @@ export class Trie { } public lookupArray(array: T): Data { + const { length } = array; let node: Trie = this; - forEach.call(array, key => node = node.getChildTrie(key)); + for (let i = 0; i < length; ++i) { + node = node.getChildTrie(array[i]); + } return node.data || (node.data = this.makeData(slice.call(array))); }