From 4614773413e46c8a46226d3c8b9db6e6232095cd Mon Sep 17 00:00:00 2001 From: Revone Date: Thu, 2 Nov 2023 21:38:10 +0800 Subject: [PATCH] [binary-tree] Merge the operation of modifying the value into the insert query process to improve performance. [benchmark] Enhance performance test cases and move existing performance tests from unit tests to the 'performance' section. Improve the benchmark test report. Refine the logic of the report generator. --- README.md | 29 +++ package.json | 4 +- .../binary-tree/binary-tree.ts | 21 +- test/config.js | 4 - .../binary-tree/avl-tree.test.ts | 24 ++ .../binary-tree/binary-tree.test.ts | 29 ++- .../data-structures/binary-tree/bst.test.ts | 28 ++- .../data-structures/heap/heap.test.ts | 30 +++ .../linked-list/doubly-linked-list.test.ts | 40 ++++ .../linked-list/singly-linked-list.test.ts | 34 +++ .../priority-queue/max-priority-queue.test.ts | 19 ++ .../data-structures/queue/deque.test.ts | 10 +- .../data-structures/queue/queue.test.ts | 17 +- test/performance/reportor.ts | 71 +++++- test/performance/types/index.ts | 1 - test/performance/types/utils/index.ts | 1 - test/performance/utils/index.ts | 4 - test/performance/utils/number.ts | 9 - test/types/index.js | 29 --- test/types/utils/big-o.js | 2 - test/types/utils/index.js | 29 --- test/types/utils/index.ts | 1 + .../types/utils/json2html.ts | 0 .../binary-tree/rb-tree.test.ts | 8 +- .../linked-list/doubly-linked-list.test.ts | 23 -- .../linked-list/linked-list.test.ts | 33 +-- .../linked-list/singly-linked-list.test.ts | 21 -- .../data-structures/matrix/matrix2d.test.ts | 2 +- .../priority-queue/max-priority-queue.test.ts | 32 --- .../priority-queue/priority-queue.test.ts | 4 +- test/unit/data-structures/queue/queue.test.ts | 42 +--- test/utils/array.ts | 5 + test/utils/big-o.js | 222 ------------------ test/utils/big-o.ts | 14 +- test/{performance => }/utils/console.ts | 0 test/utils/index.js | 30 --- test/utils/index.ts | 4 + test/{performance => }/utils/is.ts | 0 test/{performance => }/utils/json2html.ts | 1 + test/utils/number.js | 14 -- test/utils/number.ts | 12 +- 41 files changed, 346 insertions(+), 557 deletions(-) delete mode 100644 test/config.js delete mode 100644 test/performance/types/utils/index.ts delete mode 100644 test/performance/utils/index.ts delete mode 100644 test/performance/utils/number.ts delete mode 100644 test/types/index.js delete mode 100644 test/types/utils/big-o.js delete mode 100644 test/types/utils/index.js rename test/{performance => }/types/utils/json2html.ts (100%) create mode 100644 test/utils/array.ts delete mode 100644 test/utils/big-o.js rename test/{performance => }/utils/console.ts (100%) delete mode 100644 test/utils/index.js rename test/{performance => }/utils/is.ts (100%) rename test/{performance => }/utils/json2html.ts (99%) delete mode 100644 test/utils/number.js diff --git a/README.md b/README.md index dc413a5..150c78d 100644 --- a/README.md +++ b/README.md @@ -916,3 +916,32 @@ optimal approach to data structure design. +## Benchmark +
+
avl-tree
+
test nametime taken (ms)executions per secexecuted timessample mean (secs)sample deviation
add 1000 randomly2.39418.91220.006.47e-5
delete 1000 randomly0.061.77e+49175.66e-51.90e-6
addMany 10003.32301.33170.005.90e-4
get 100055.0318.1710.060.00
+
+
binary-tree
+
test nametime taken (ms)executions per secexecuted timessample mean (secs)sample deviation
add 100066.4915.0430.070.00
delete 100024.0441.6011060.020.04
addMany 10009.05110.4460.012.88e-4
get 100032.1231.1320.036.75e-4
+
+
bst
+
test nametime taken (ms)executions per secexecuted timessample mean (secs)sample deviation
add 1000 randomly2.27440.62230.001.42e-4
delete 1000 randomly0.052.19e+411744.57e-51.97e-6
addMany 1000 balanced2.93341.38190.001.13e-4
get 100062.8515.9110.060.01
+
+
heap
+
test nametime taken (ms)executions per secexecuted timessample mean (secs)sample deviation
add & 10000.352878.391493.47e-41.93e-5
fib add & pop 10003.98251.35140.001.32e-4
+
+
doubly-linked-list
+
test nametime taken (ms)executions per secexecuted timessample mean (secs)sample deviation
unshift 1000000193.195.1810.190.04
unshift & shift 1000000170.355.8710.170.03
insertBefore 10000.033.43e+418882.91e-56.03e-6
+
+
singly-linked-list
+
test nametime taken (ms)executions per secexecuted timessample mean (secs)sample deviation
push & pop 10001.79560.08290.007.70e-5
insertBefore 10002.31433.45220.005.52e-5
+
+
max-priority-queue
+
test nametime taken (ms)executions per secexecuted timessample mean (secs)sample deviation
refill & poll 10000001859.400.5411.860.03
+
+
deque
+
test nametime taken (ms)executions per secexecuted timessample mean (secs)sample deviation
push 1000000215.004.6510.220.01
shift 100000025.0439.9430.030.00
+
+
queue
+
test nametime taken (ms)executions per secexecuted timessample mean (secs)sample deviation
push 100000041.8123.9220.040.00
push & shift 100000079.1712.6310.088.70e-4
+
\ No newline at end of file diff --git a/package.json b/package.json index 9ef4802..7705366 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-structure-typed", - "version": "1.41.3", + "version": "1.41.4", "description": "Data Structures of Javascript & TypeScript. Binary Tree, BST, Graph, Heap, Priority Queue, Linked List, Queue, Deque, Stack, AVL Tree, Tree Multiset, Trie, Directed Graph, Undirected Graph, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue.", "main": "dist/cjs/index.js", "module": "dist/mjs/index.js", @@ -31,7 +31,7 @@ "reformat": "npm run reformat:src && npm run reformat:test", "update:subs": "npm i avl-tree-typed binary-tree-typed bst-typed heap-typed --save-dev", "install:all-subs": "npm i avl-tree-typed binary-tree-typed bst-typed deque-typed directed-graph-typed doubly-linked-list-typed graph-typed heap-typed linked-list-typed max-heap-typed max-priority-queue-typed min-heap-typed min-priority-queue-typed priority-queue-typed singly-linked-list-typed stack-typed tree-multiset-typed trie-typed undirected-graph-typed queue-typed --save-dev", - "test": "jest", + "test": "jest --runInBand", "test:integration": "npm run update:subs && jest --config jest.integration.config.js", "benchmark": "ts-node test/performance/reportor.ts", "check:deps": "dependency-cruiser src", diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 1feb431..0372053 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -179,7 +179,10 @@ export class BinaryTree = BinaryTreeNode while (queue.size > 0) { const cur = queue.shift(); if (cur) { - if (newNode && cur.key === newNode.key) return; + if (newNode && cur.key === newNode.key) { + cur.value = newNode.value; + return; + } const inserted = this._addTo(newNode, cur); if (inserted !== undefined) return inserted; if (cur.left) queue.push(cur.left); @@ -201,16 +204,16 @@ export class BinaryTree = BinaryTreeNode return; } - const key = typeof keyOrNode === 'number' ? keyOrNode : keyOrNode ? keyOrNode.key : undefined; - const existNode = key !== undefined ? this.getNode(key, (node: N) => node.key) : undefined; + // const key = typeof keyOrNode === 'number' ? keyOrNode : keyOrNode ? keyOrNode.key : undefined; + // const existNode = key !== undefined ? this.getNode(key, (node: N) => node.key) : undefined; if (this.root) { - if (existNode) { - existNode.value = value; - inserted = existNode; - } else { - inserted = _bfs(this.root, needInsert); - } + // if (existNode) { + // existNode.value = value; + // inserted = existNode; + // } else { + inserted = _bfs(this.root, needInsert); + // } } else { this._setRoot(needInsert); if (needInsert !== null) { diff --git a/test/config.js b/test/config.js deleted file mode 100644 index 1b96349..0000000 --- a/test/config.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', {value: true}); -exports.isDebugTest = void 0; -exports.isDebugTest = false; diff --git a/test/performance/data-structures/binary-tree/avl-tree.test.ts b/test/performance/data-structures/binary-tree/avl-tree.test.ts index e69de29..0de0d75 100644 --- a/test/performance/data-structures/binary-tree/avl-tree.test.ts +++ b/test/performance/data-structures/binary-tree/avl-tree.test.ts @@ -0,0 +1,24 @@ +import {AVLTree} from '../../../../src'; +import * as Benchmark from 'benchmark'; +import {magnitude, randomInt, randomIntArray} from '../../../utils'; + +const suite = new Benchmark.Suite(); +const avl = new AVLTree(); +const {N_LOG_N} = magnitude; + +suite + .add(`add ${N_LOG_N} randomly`, () => { + for (let i = 0; i < N_LOG_N; i++) avl.add(randomInt(0, N_LOG_N)); + }) + .add(`delete ${N_LOG_N} randomly`, () => { + for (let i = 0; i < N_LOG_N; i++) avl.delete(randomInt(0, N_LOG_N)); + }) + .add(`addMany ${N_LOG_N}`, () => { + const arr = randomIntArray(N_LOG_N); + avl.addMany(arr); + }) + .add(`get ${N_LOG_N}`, () => { + for (let i = 0; i < N_LOG_N; i++) avl.get(randomInt(-N_LOG_N, N_LOG_N)); + }); + +export {suite}; diff --git a/test/performance/data-structures/binary-tree/binary-tree.test.ts b/test/performance/data-structures/binary-tree/binary-tree.test.ts index 8d7e0b5..6217aef 100644 --- a/test/performance/data-structures/binary-tree/binary-tree.test.ts +++ b/test/performance/data-structures/binary-tree/binary-tree.test.ts @@ -1,18 +1,25 @@ import {BinaryTree} from '../../../../src'; - import * as Benchmark from 'benchmark'; +import {magnitude, randomInt, randomIntArray} from '../../../utils'; -export const suite = new Benchmark.Suite(); -const bt = new BinaryTree(); +const suite = new Benchmark.Suite(); +const biTree = new BinaryTree(); +const {N_LOG_N} = magnitude; suite - .add('add 1000', () => { - for (let i = 0; i < 1000; i++) { - bt.add(i); - } + .add(`add ${N_LOG_N}`, () => { + for (let i = 0; i < N_LOG_N; i++) biTree.add(randomInt(-N_LOG_N, N_LOG_N)); }) - .add('add & delete 1000', () => { - for (let i = 0; i < 1000; i++) { - bt.delete(i); - } + .add(`delete ${N_LOG_N}`, () => { + for (let i = 0; i < N_LOG_N; i++) biTree.delete(randomInt(-N_LOG_N, N_LOG_N)); + }) + .add(`addMany ${N_LOG_N}`, () => { + biTree.clear(); + const arr = randomIntArray(N_LOG_N); + biTree.addMany(arr); + }) + .add(`get ${N_LOG_N}`, () => { + for (let i = 0; i < N_LOG_N; i++) biTree.get(randomInt(-N_LOG_N, N_LOG_N)); }); + +export {suite}; diff --git a/test/performance/data-structures/binary-tree/bst.test.ts b/test/performance/data-structures/binary-tree/bst.test.ts index f16440d..5c48beb 100644 --- a/test/performance/data-structures/binary-tree/bst.test.ts +++ b/test/performance/data-structures/binary-tree/bst.test.ts @@ -1,18 +1,24 @@ import {BST} from '../../../../src'; - import * as Benchmark from 'benchmark'; +import {magnitude, randomInt, randomIntArray} from '../../../utils'; -export const suite = new Benchmark.Suite(); -const bt = new BST(); +const suite = new Benchmark.Suite(); +const bst = new BST(); +const {N_LOG_N} = magnitude; suite - .add('add 1000', () => { - for (let i = 0; i < 1000; i++) { - bt.add(i); - } + .add(`add ${N_LOG_N} randomly`, () => { + for (let i = 0; i < N_LOG_N; i++) bst.add(randomInt(0, N_LOG_N)); }) - .add('add & delete 1000', () => { - for (let i = 0; i < 1000; i++) { - bt.delete(i); - } + .add(`delete ${N_LOG_N} randomly`, () => { + for (let i = 0; i < N_LOG_N; i++) bst.delete(randomInt(0, N_LOG_N)); + }) + .add(`addMany ${N_LOG_N} balanced`, () => { + const arr = randomIntArray(N_LOG_N); + bst.addMany(arr); + }) + .add(`get ${N_LOG_N}`, () => { + for (let i = 0; i < N_LOG_N; i++) bst.get(randomInt(-N_LOG_N, N_LOG_N)); }); + +export {suite}; diff --git a/test/performance/data-structures/heap/heap.test.ts b/test/performance/data-structures/heap/heap.test.ts index e69de29..0775c71 100644 --- a/test/performance/data-structures/heap/heap.test.ts +++ b/test/performance/data-structures/heap/heap.test.ts @@ -0,0 +1,30 @@ +import {FibonacciHeap, Heap} from '../../../../src'; +import * as Benchmark from 'benchmark'; +import {magnitude} from '../../../utils'; + +const suite = new Benchmark.Suite(); +const {N_LOG_N} = magnitude; + +suite + .add(`add & ${N_LOG_N}`, () => { + const heap = new Heap({comparator: (a, b) => b - a}); + + for (let i = 0; i < N_LOG_N; i++) { + heap.add(i); + } + + for (let i = 0; i < N_LOG_N; i++) { + heap.pop(); + } + }) + .add(`fib add & pop ${N_LOG_N}`, () => { + const fbHeap = new FibonacciHeap(); + for (let i = 1; i <= N_LOG_N; i++) { + fbHeap.push(i); + } + for (let i = 1; i <= N_LOG_N; i++) { + fbHeap.pop(); + } + }); + +export {suite}; diff --git a/test/performance/data-structures/linked-list/doubly-linked-list.test.ts b/test/performance/data-structures/linked-list/doubly-linked-list.test.ts index e69de29..7847c1b 100644 --- a/test/performance/data-structures/linked-list/doubly-linked-list.test.ts +++ b/test/performance/data-structures/linked-list/doubly-linked-list.test.ts @@ -0,0 +1,40 @@ +import {DoublyLinkedList, DoublyLinkedListNode} from '../../../../src'; +import * as Benchmark from 'benchmark'; +import {magnitude} from '../../../utils'; + +const suite = new Benchmark.Suite(); +const {LINEAR, N_LOG_N} = magnitude; + +suite + .add(`unshift ${LINEAR}`, () => { + const list = new DoublyLinkedList(); + + for (let i = 0; i < LINEAR; i++) { + list.unshift(i); + } + }) + .add(`unshift & shift ${LINEAR}`, () => { + const list = new DoublyLinkedList(); + + for (let i = 0; i < LINEAR; i++) { + list.unshift(i); + } + for (let i = 0; i < LINEAR; i++) { + list.shift(); + } + }) + .add(`insertBefore ${N_LOG_N}`, () => { + const doublyList = new DoublyLinkedList(); + let midNode: DoublyLinkedListNode | null = null; + const midIndex = Math.floor(N_LOG_N / 2); + for (let i = 0; i < N_LOG_N; i++) { + doublyList.push(i); + if (i === midIndex) { + midNode = doublyList.getNode(i); + } else if (i > midIndex && midNode) { + doublyList.insertBefore(midNode, i); + } + } + }); + +export {suite}; diff --git a/test/performance/data-structures/linked-list/singly-linked-list.test.ts b/test/performance/data-structures/linked-list/singly-linked-list.test.ts index e69de29..a0c491b 100644 --- a/test/performance/data-structures/linked-list/singly-linked-list.test.ts +++ b/test/performance/data-structures/linked-list/singly-linked-list.test.ts @@ -0,0 +1,34 @@ +import {SinglyLinkedList, SinglyLinkedListNode} from '../../../../src'; +import * as Benchmark from 'benchmark'; +import {magnitude} from '../../../utils'; + +const suite = new Benchmark.Suite(); +const {N_LOG_N} = magnitude; + +suite + .add(`push & pop ${N_LOG_N}`, () => { + const list = new SinglyLinkedList(); + + for (let i = 0; i < N_LOG_N; i++) { + list.push(i); + } + + for (let i = 0; i < N_LOG_N; i++) { + list.pop(); + } + }) + .add(`insertBefore ${N_LOG_N}`, () => { + const singlyList = new SinglyLinkedList(); + let midSinglyNode: SinglyLinkedListNode | null = null; + const midIndex = Math.floor(N_LOG_N / 2); + for (let i = 0; i < N_LOG_N; i++) { + singlyList.push(i); + if (i === midIndex) { + midSinglyNode = singlyList.getNode(i); + } else if (i > midIndex && midSinglyNode) { + singlyList.insertBefore(midSinglyNode.value, i); + } + } + }); + +export {suite}; diff --git a/test/performance/data-structures/priority-queue/max-priority-queue.test.ts b/test/performance/data-structures/priority-queue/max-priority-queue.test.ts index e69de29..b143987 100644 --- a/test/performance/data-structures/priority-queue/max-priority-queue.test.ts +++ b/test/performance/data-structures/priority-queue/max-priority-queue.test.ts @@ -0,0 +1,19 @@ +import {MaxPriorityQueue} from '../../../../src'; +import * as Benchmark from 'benchmark'; +import {magnitude} from '../../../utils'; + +const suite = new Benchmark.Suite(); +const {LINEAR} = magnitude; + +suite.add(`refill & poll ${LINEAR}`, () => { + const nodes = Array.from( + new Set(Array.from(new Array(LINEAR), () => Math.floor(Math.random() * LINEAR * 100))) + ); + const maxPQ = new MaxPriorityQueue(); + maxPQ.refill(nodes); + while (maxPQ.size > 0) { + maxPQ.poll(); + } +}); + +export {suite}; diff --git a/test/performance/data-structures/queue/deque.test.ts b/test/performance/data-structures/queue/deque.test.ts index bf76aec..6ea4266 100644 --- a/test/performance/data-structures/queue/deque.test.ts +++ b/test/performance/data-structures/queue/deque.test.ts @@ -1,18 +1,20 @@ import {Deque} from '../../../../src'; import * as Benchmark from 'benchmark'; +import {magnitude} from '../../../utils'; export const suite = new Benchmark.Suite(); +const {LINEAR} = magnitude; suite - .add('push', () => { + .add(`push ${LINEAR}`, () => { const deque = new Deque(); - for (let i = 0; i < 10; i++) { + for (let i = 0; i < LINEAR; i++) { deque.push(i); } }) - .add('shift', () => { + .add(`shift ${LINEAR}`, () => { const deque = new Deque(); - for (let i = 0; i < 10; i++) { + for (let i = 0; i < LINEAR; i++) { deque.push(i); deque.shift(); } diff --git a/test/performance/data-structures/queue/queue.test.ts b/test/performance/data-structures/queue/queue.test.ts index 24d21c0..f256e0b 100644 --- a/test/performance/data-structures/queue/queue.test.ts +++ b/test/performance/data-structures/queue/queue.test.ts @@ -1,20 +1,25 @@ import {Queue} from '../../../../src'; - import * as Benchmark from 'benchmark'; +import {magnitude} from '../../../utils'; -export const suite = new Benchmark.Suite(); +const suite = new Benchmark.Suite(); +const {LINEAR} = magnitude; suite - .add('push 1000000', () => { + .add(`push ${LINEAR}`, () => { const queue = new Queue(); - for (let i = 0; i < 1000000; i++) { + + for (let i = 0; i < LINEAR; i++) { queue.push(i); } }) - .add('push & shift 1000000', () => { + .add(`push & shift ${LINEAR}`, () => { const queue = new Queue(); - for (let i = 0; i < 1000000; i++) { + + for (let i = 0; i < LINEAR; i++) { queue.push(i); queue.shift(); } }); + +export {suite}; diff --git a/test/performance/reportor.ts b/test/performance/reportor.ts index 6f0841a..2bd0ab5 100644 --- a/test/performance/reportor.ts +++ b/test/performance/reportor.ts @@ -2,7 +2,7 @@ import * as Benchmark from 'benchmark'; import * as path from 'path'; import * as fs from 'fs'; import * as fastGlob from 'fast-glob'; -import {numberFix, render, Color} from './utils'; +import {Color, numberFix, render} from '../utils'; import {PerformanceTest} from './types'; const reportDistPath = 'benchmark'; @@ -15,7 +15,7 @@ let testFileCount = 0, completedCount = 0; const performanceTests: PerformanceTest[] = []; -const {GREEN, BOLD, END} = Color; +const {GREEN, BOLD, RED, END, YELLOW, CYAN, BG_YELLOW} = Color; testFiles.forEach((file: string) => { testFileCount++; @@ -37,6 +37,17 @@ const composeReport = () => { performance of data-structure-typed