diff --git a/README.md b/README.md index fe5fadc..f6f8558 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ # lite-fifo +When you're short on RAM but still want a decent FIFO implementation... + ## Lightweight and efficient Queue implementations This package aims to provide zero-dependency implementations of a queue, focusing on: * memory footprint (RAM) @@ -11,6 +13,9 @@ This package aims to provide zero-dependency implementations of a queue, focusin The production code is dependency free. The only dependencies are for testing. +## Benchmarks +After running several scenarios and comparing to several known implementations, +it's clear that this project's flagship `ChunkedQueue` has the lowest RAM usage, with a reasonable throughput (ops/sec). See [benchmarks.md](benchmarks.md) file for a deeper view and analysis. ## Installation ```bash @@ -152,6 +157,9 @@ How? when it's full, the next `enqueue` operation would trigger a re-order of th This process is *O(1) amortized*, and therefore this is a generic queue, and can be used in any scenario. # The Benchmark +For a full deep dive of the scenarios, measurement and comparison with implementations (also external to this project), +please read [benchmarks.md](benchmarks.md) file. + ## Methodology The scenario being checked is the following: set P = 100000 @@ -161,8 +169,8 @@ do 6 times:   enqueue 1P   dequeue 5P -Apply this scenario T times (set T=20) for every relevant class (see table below), measure RAM used and ops/sec. -Remove best and worst results (in terms of ops/sec), and take the average (mean) from the rest of the results. +Apply this scenario T times (set T=30) for every relevant class (see table below), measure RAM used and ops/sec. +Take the average (mean) of the results. Note: we took a very large value for P, otherwise complexity related issues won't come up. @@ -170,15 +178,14 @@ Note: we took a very large value for P, otherwise complexity related issues won' | Class Name | Ops/Sec | RAM used (MB) | |:-------------------|--------:|--------------:| | DynamicArrayQueue | **5** | 8 | -| ChunkedQueue | 31800 | **28** | -| DynamicCyclicQueue | 27100 | 102 | -| LinkedQueue | 29800 | 143 | +| ChunkedQueue | 36200 | **28** | +| DynamicCyclicQueue | 28200 | 89 | +| LinkedQueue | 21300 | 143 | ## Analysis 1. The naive implementation, `DynamicArrayQueue`, is so slow that it can't be considered as an option 2. The fastest implementation is `ChunkedQueue`, and has the lowest RAM usage 3. The common `LinkedQueue` implementation is not the fastest one, even with *O(1)* time complexity, and it's the most wasteful in terms of RAM usage -4. Classes `DynamicCyclicQueue` and `LinkedQueue` have quite similar results: the former has a lower RAM usage and the latter performs a bit better. ## Suggestions * Use the provided `ChunkedQueue` for a generic solution diff --git a/benchmark/images/buffer-then-gradually-decrease.png b/benchmark/images/buffer-then-gradually-decrease.png new file mode 100644 index 0000000..6ab2a85 Binary files /dev/null and b/benchmark/images/buffer-then-gradually-decrease.png differ diff --git a/benchmark/images/enqueue-only.png b/benchmark/images/enqueue-only.png new file mode 100644 index 0000000..1d24b04 Binary files /dev/null and b/benchmark/images/enqueue-only.png differ diff --git a/benchmark/images/enqueue-zig-zag-dequeue.png b/benchmark/images/enqueue-zig-zag-dequeue.png new file mode 100644 index 0000000..ef93832 Binary files /dev/null and b/benchmark/images/enqueue-zig-zag-dequeue.png differ diff --git a/benchmark/images/zig-zag.png b/benchmark/images/zig-zag.png new file mode 100644 index 0000000..1b6a0a1 Binary files /dev/null and b/benchmark/images/zig-zag.png differ diff --git a/benchmark/package.json b/benchmark/package.json new file mode 100644 index 0000000..6549568 --- /dev/null +++ b/benchmark/package.json @@ -0,0 +1,19 @@ +{ + "name": "lite-fifo-benchmark", + "version": "0.3.4", + "license": "MIT", + "scripts": { + "benchmark": "node ./report-generator.js", + "benchmark-dynamic-array": "node ./report-generator.js ./report-config-dynamic-array.json" + }, + "author": "", + "description": "", + "dependencies": { + "@datastructures-js/queue": "4.2.3", + "dsa.js": "2.7.6", + "efficient-data-structures": "0.1.310", + "markdown-table": "3.0.3", + "mnemonist": "0.39.8", + "shelljs": "^0.8.5" + } +} diff --git a/benchmark/ramusage.js b/benchmark/ramusage.js index 6b37ee6..a3a31c8 100644 --- a/benchmark/ramusage.js +++ b/benchmark/ramusage.js @@ -3,10 +3,26 @@ const { ChunkedQueue } = require('../src/ChunkedQueue'); const { CyclicQueue } = require('../src/CyclicQueue'); const { DynamicArrayQueue } = require('../src/DynamicArrayQueue'); const { DynamicCyclicQueue } = require('../src/DynamicCyclicQueue'); +const MnemonistQueue = require('mnemonist/queue'); +const { Queue: DsaJsQueue } = require('dsa.js'); +const { Queue: EfficientDataStructuresQueue } = require('efficient-data-structures'); +const { Queue: DatastructuresJsQueue } = require('@datastructures-js/queue'); function main (className, chunkSize, counts) { let cls; switch (className.toLowerCase()) { + case 'DatastructuresJsQueue'.toLowerCase(): + cls = () => new DatastructuresJsQueue(); + break; + case 'MnemonistQueue'.toLowerCase(): + cls = () => new MnemonistQueue(); + break; + case 'EfficientDataStructuresQueue'.toLowerCase(): + cls = () => new EfficientDataStructuresQueue(); + break; + case 'DsaJsQueue'.toLowerCase(): + cls = () => new DsaJsQueue(); + break; case 'LinkedQueue'.toLowerCase(): cls = () => new LinkedQueue(); break; diff --git a/benchmark/report-config.json b/benchmark/report-config.json index 2a17256..7b8ba5d 100644 --- a/benchmark/report-config.json +++ b/benchmark/report-config.json @@ -1,7 +1,36 @@ { - "operations": [3000000, 500000, 100000, 500000, 100000, 500000, 100000, 500000, 100000, 500000, 100000, 500000, 100000, 500000], - "classes": ["ChunkedQueue", "ChunkedQueue-128", "DynamicCyclicQueue", "LinkedQueue"], - "iterationsPerClass": 200, + "scenarios": [ + { + "name": "enqueue-zig-zag-dequeue", + "description": "massive enqueue, then repeated zig-zag with 1/3 of the size, then massive dequeue", + "operations": [3000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 3000000] + } + ], + "scenarios1": [ + { + "name": "buffer then gradually decrease", + "description": "massive enqueue, then gradual dequeue", + "operations": [3000000, 500000, 100000, 500000, 100000, 500000, 100000, 500000, 100000, 500000, 100000, 500000, 100000, 500000] + }, + { + "name": "zig-zag", + "description": "massive enqueue and massive dequeue repeatedly", + "operations": [3000000, 3000000, 3000000, 3000000, 3000000, 3000000, 3000000, 3000000, 3000000, 3000000, 3000000, 3000000] + }, + { + "name": "enqueue-zig-zag-dequeue", + "description": "massive enqueue, then repeated zig-zag with 1/3 of the size, then massive dequeue", + "operations": [3000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 3000000] + }, + { + "name": "enqueue only", + "description": "massive enqueue and then nothing", + "operations": [9000000] + } + + ], + "classes": ["ChunkedQueue", "MnemonistQueue", "EfficientDataStructuresQueue", "DatastructuresJsQueue", "DsaJsQueue", "DynamicCyclicQueue", "LinkedQueue"], + "iterationsPerClass": 30, "shuffle": true, - "removeMinMax": true -} \ No newline at end of file + "verbose": true +} diff --git a/benchmark/report-generator.js b/benchmark/report-generator.js index 6136bb8..3f84d7d 100644 --- a/benchmark/report-generator.js +++ b/benchmark/report-generator.js @@ -1,25 +1,27 @@ +// noinspection JSUnresolvedReference + const fs = require('fs'); const path = require('path'); const shell = require('shelljs'); const configFileName = process.argv.length === 2 ? path.join(__dirname, 'report-config.json') : process.argv[2]; const reportConfig = JSON.parse(fs.readFileSync(configFileName).toString()); -const myLog = reportConfig.debug ? console.log : (_) => {}; +const myLog = reportConfig.verbose ? console.log : (_) => {}; myLog(`config file = ${configFileName}`); const commandObjects = []; -const resultObjects = reportConfig.classes.reduce((agg, className) => { - agg[className] = []; - return agg; -}, {}); - -reportConfig.classes.forEach((className) => { - for (let i = 0; i < reportConfig.iterationsPerClass; i++) { - commandObjects.push({ - className, - command: `node ${path.join(__dirname, 'ramusage.js')} ${className} ${reportConfig.operations.join(' ')}` - }); - } +const resultObjects = []; + +reportConfig.scenarios.forEach(scenario => { + reportConfig.classes.forEach((className) => { + for (let i = 0; i < reportConfig.iterationsPerClass; i++) { + commandObjects.push({ + scenarioName: scenario.name, + className, + command: `node ${path.join(__dirname, 'ramusage.js')} ${className} ${scenario.operations.join(' ')}` + }); + } + }); }); function shuffle (array) { @@ -35,21 +37,33 @@ if (reportConfig.shuffle) { commandObjects.forEach(commandObject => { const command = commandObject.command; - myLog(`about to execute command: ${command}`); + const scenarioName = commandObject.scenarioName; + myLog(`scenario "${scenarioName}", about to execute command: ${command}`); const resp = shell.exec(command, { silent: true }); - resultObjects[commandObject.className].push(JSON.parse(resp.stdout)); + resultObjects.push({ + className: commandObject.className, + scenarioName: scenarioName, + result: JSON.parse(resp.stdout) + }); }); -Object.values(resultObjects).forEach((results) => { - results.sort((a, b) => a.heapUsed - b.heapUsed); +const resultsByScenario = reportConfig.scenarios.reduce((agg, current) => { + const m = new Map(); + reportConfig.classes.forEach((className) => { m.set(className, []); }); + agg.set(current.name, m); + return agg; +}, new Map()); + +resultObjects.forEach(result => { + const m = resultsByScenario.get(result.scenarioName); + m.get(result.className).push(result); }); -if (reportConfig.removeMinMax) { - Object.values(resultObjects).forEach((results) => { - results.shift(); - results.pop(); +resultsByScenario.values().forEach(rbcn => { + rbcn.values().forEach((results) => { + results.sort((a, b) => a.result.heapUsed - b.result.heapUsed); }); -} +}); function sum (arr) { return arr.reduce((a, b) => a + b, 0); @@ -59,14 +73,267 @@ function avg (arr) { return sum(arr) / arr.length; } -const stats = Object.entries(resultObjects).map(([k, v]) => { +const stats = resultsByScenario.entries().map(([scenarioName, resultsByClassName]) => { return { - className: k, - avgOpsPerSec: Math.trunc(avg(v.map(x => x.opsPerSec))), - avgHeapUsed: Math.trunc(avg(v.map(x => x.heapUsed))), - avgHeapUsedLog10: Math.trunc(100 * Math.log10(avg(v.map(x => x.heapUsed)))) / 100 + scenarioName, + stats: resultsByClassName.entries().map(([k, v]) => { + const avgOpsPerSec = Math.trunc(avg(v.map(x => x.result.opsPerSec))); + return { + className: k, + avgOpsPerSec: avgOpsPerSec, + avgHeapUsed: Math.trunc(avg(v.map(x => x.result.heapUsed))), + avgHeapUsedLog10: Math.trunc(100 * Math.log10(avg(v.map(x => x.result.heapUsed)))) / 100 + }; + }).toArray() }; +}).toArray(); + +stats.forEach(scenario => { + console.log(`scenario: ${scenario.scenarioName}`); + { + console.log(''); + console.log('RAM usage'); + const data = [['Implementation name', 'RAM usage']]; + data.push(...scenario.stats.sort((a, b) => a.avgHeapUsed - b.avgHeapUsed).map(item => [item.className, item.avgHeapUsed])); + console.log(markdownTable(data, { align: ['l', 'r'] })); + } + { + console.log(''); + console.log('Operations per second'); + const data = [['Implementation name', 'Ops/sec']]; + data.push(...scenario.stats.sort((a, b) => b.avgOpsPerSec - a.avgOpsPerSec).map(item => [item.className, item.avgOpsPerSec])); + console.log(markdownTable(data, { align: ['l', 'r'] })); + } + console.log(''); }); -myLog(JSON.stringify(resultObjects, null, ' ')); -console.log(JSON.stringify(stats, null, ' ')); +// The following code was taken from https://github.com/wooorm/markdown-table +// This code is "inlined" for simplicity reasons. +// The license of this code is MIT, so this is a legit action. +function markdownTable (table, options = {}) { + const align = (options.align || []).concat(); + const stringLength = options.stringLength || defaultStringLength; + /** @type {Array} Character codes as symbols for alignment per column. */ + const alignments = []; + /** @type {Array>} Cells per row. */ + const cellMatrix = []; + /** @type {Array>} Sizes of each cell per row. */ + const sizeMatrix = []; + /** @type {Array} */ + const longestCellByColumn = []; + let mostCellsPerRow = 0; + let rowIndex = -1; + + // This is a superfluous loop if we don’t align delimiters, but otherwise we’d + // do superfluous work when aligning, so optimize for aligning. + while (++rowIndex < table.length) { + /** @type {Array} */ + const row = []; + /** @type {Array} */ + const sizes = []; + let columnIndex = -1; + + if (table[rowIndex].length > mostCellsPerRow) { + mostCellsPerRow = table[rowIndex].length; + } + + while (++columnIndex < table[rowIndex].length) { + const cell = serialize(table[rowIndex][columnIndex]); + + if (options.alignDelimiters !== false) { + const size = stringLength(cell); + sizes[columnIndex] = size; + + if ( + longestCellByColumn[columnIndex] === undefined || + size > longestCellByColumn[columnIndex] + ) { + longestCellByColumn[columnIndex] = size; + } + } + + row.push(cell); + } + + cellMatrix[rowIndex] = row; + sizeMatrix[rowIndex] = sizes; + } + + // Figure out which alignments to use. + let columnIndex = -1; + + if (typeof align === 'object' && 'length' in align) { + while (++columnIndex < mostCellsPerRow) { + alignments[columnIndex] = toAlignment(align[columnIndex]); + } + } else { + // noinspection JSCheckFunctionSignatures + const code = toAlignment(align); + + while (++columnIndex < mostCellsPerRow) { + alignments[columnIndex] = code; + } + } + + // Inject the alignment row. + columnIndex = -1; + /** @type {Array} */ + const row = []; + /** @type {Array} */ + const sizes = []; + + while (++columnIndex < mostCellsPerRow) { + const code = alignments[columnIndex]; + let before = ''; + let after = ''; + + if (code === 99 /* `c` */) { + before = ':'; + after = ':'; + } else if (code === 108 /* `l` */) { + before = ':'; + } else if (code === 114 /* `r` */) { + after = ':'; + } + + // There *must* be at least one hyphen-minus in each alignment cell. + let size = + options.alignDelimiters === false + ? 1 + : Math.max( + 1, + longestCellByColumn[columnIndex] - before.length - after.length + ); + + const cell = before + '-'.repeat(size) + after; + + if (options.alignDelimiters !== false) { + size = before.length + size + after.length; + + if (size > longestCellByColumn[columnIndex]) { + longestCellByColumn[columnIndex] = size; + } + + sizes[columnIndex] = size; + } + + row[columnIndex] = cell; + } + + // Inject the alignment row. + cellMatrix.splice(1, 0, row); + sizeMatrix.splice(1, 0, sizes); + + rowIndex = -1; + /** @type {Array} */ + const lines = []; + + while (++rowIndex < cellMatrix.length) { + const row = cellMatrix[rowIndex]; + const sizes = sizeMatrix[rowIndex]; + columnIndex = -1; + /** @type {Array} */ + const line = []; + + while (++columnIndex < mostCellsPerRow) { + const cell = row[columnIndex] || ''; + let before = ''; + let after = ''; + + if (options.alignDelimiters !== false) { + const size = + longestCellByColumn[columnIndex] - (sizes[columnIndex] || 0); + const code = alignments[columnIndex]; + + if (code === 114 /* `r` */) { + before = ' '.repeat(size); + } else if (code === 99 /* `c` */) { + if (size % 2) { + before = ' '.repeat(size / 2 + 0.5); + after = ' '.repeat(size / 2 - 0.5); + } else { + before = ' '.repeat(size / 2); + after = before; + } + } else { + after = ' '.repeat(size); + } + } + + if (options.delimiterStart !== false && !columnIndex) { + line.push('|'); + } + + if ( + options.padding !== false && + // Don’t add the opening space if we’re not aligning and the cell is + // empty: there will be a closing space. + !(options.alignDelimiters === false && cell === '') && + (options.delimiterStart !== false || columnIndex) + ) { + line.push(' '); + } + + if (options.alignDelimiters !== false) { + line.push(before); + } + + line.push(cell); + + if (options.alignDelimiters !== false) { + line.push(after); + } + + if (options.padding !== false) { + line.push(' '); + } + + if ( + options.delimiterEnd !== false || + columnIndex !== mostCellsPerRow - 1 + ) { + line.push('|'); + } + } + + lines.push( + options.delimiterEnd === false + ? line.join('').replace(/ +$/, '') + : line.join('') + ); + } + + return lines.join('\n'); +} + +/** + * @param {string|null|undefined} [value] + * @returns {string} + */ +function serialize (value) { + return value === null || value === undefined ? '' : String(value); +} + +/** + * @param {string} value + * @returns {number} + */ +function defaultStringLength (value) { + return value.length; +} + +/** + * @param {string|null|undefined} value + * @returns {number} + */ +function toAlignment (value) { + const code = typeof value === 'string' ? value.codePointAt(0) : 0; + + return code === 67 /* `C` */ || code === 99 /* `c` */ + ? 99 /* `c` */ + : code === 76 /* `L` */ || code === 108 /* `l` */ + ? 108 /* `l` */ + : code === 82 /* `R` */ || code === 114 /* `r` */ + ? 114 /* `r` */ + : 0; +} diff --git a/benchmarks.md b/benchmarks.md new file mode 100644 index 0000000..d4855e4 --- /dev/null +++ b/benchmarks.md @@ -0,0 +1,159 @@ +# Benchmarks + +## Hardware +All benchmarks were executed on a [MacBook Pro A2251](https://support.apple.com/en-il/111339) + +## Implementations being tested + +The following implementations are tested in the benchmarks: + +| Project | Class | Symbol | +|----------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|------------------------------| +| lite-fifo 0.3.4 ([npm](https://www.npmjs.com/package/lite-fifo), [code](https://github.com/kleinron/lite-fifo)) | ChunkedQueue | ChunkedQueue | +| lite-fifo 0.3.4 ([npm](https://www.npmjs.com/package/lite-fifo), [code](https://github.com/kleinron/lite-fifo)) | DynamicCyclicQueue | DynamicCyclicQueue | +| lite-fifo 0.3.4 ([npm](https://www.npmjs.com/package/lite-fifo), [code](https://github.com/kleinron/lite-fifo)) | LinkedQueue | LinkedQueue | +| @datastructures-js/queue 4.2.3 ([npm](https://www.npmjs.com/package/@datastructures-js/queue), [code](https://github.com/datastructures-js/queue)) | Queue | DatastructuresJsQueue | +| mnemonist 0.39.8 ([npm](https://www.npmjs.com/package/mnemonist), [code](https://github.com/yomguithereal/mnemonist)) | Queue | MnemonistQueue | +| efficient-data-structures 0.1.310 ([npm](https://www.npmjs.com/package/efficient-data-structures), no repo) | Queue | EfficientDataStructuresQueue | +| dsa.js 2.7.6 ([npm](https://www.npmjs.com/package/dsa.js), [code](https://github.com/amejiarosario/dsa.js-data-structures-algorithms-javascript)) | Queue | DsaJsQueue | + +## Excluded implementations + +The following implementations are array based, using its `push` and `shift` methods. + +These implementations do not scale, as `shift` has an O(n) time complexity, and are therefore excluded. + +| Project | Class / Function | +|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------| +| @supercharge/queue-datastructure ([npm](https://www.npmjs.com/package/@supercharge/queue-datastructure), [code](https://github.com/supercharge/queue-datastructure)) | Queue | +| js-data-structs ([npm](https://www.npmjs.com/package/js-data-structs), [code](https://github.com/Aveek-Saha/js-data-structs)) | Queue | +| light-queue ([npm](https://www.npmjs.com/package/light-queue), [code](https://github.com/mmujic/light-queue)) | Queue | + +## Scenarios + +Each benchmark scenario has its own chart to easily visualize what it tests. + +### buffer then gradually decrease + +Enqueue a lot of items, then dequeue rate > enqueue rate, so gradually remove the items. + +![](/benchmark/images/buffer-then-gradually-decrease.png) + +RAM usage + +| Implementation name | RAM usage | +| :--------------------------- | --------: | +| ChunkedQueue | 28245214 | +| DynamicCyclicQueue | 89227384 | +| EfficientDataStructuresQueue | 100237005 | +| DsaJsQueue | 106247533 | +| MnemonistQueue | 118739909 | +| DatastructuresJsQueue | 124799171 | +| LinkedQueue | 143345455 | + +Operations per second + +| Implementation name | Ops/sec | +| :--------------------------- | ------: | +| MnemonistQueue | 54744 | +| DatastructuresJsQueue | 54383 | +| ChunkedQueue | 36275 | +| DynamicCyclicQueue | 28231 | +| LinkedQueue | 21294 | +| EfficientDataStructuresQueue | 17776 | +| DsaJsQueue | 17219 | + +### zigzag + +Enqueue a lot, then dequeue to zero, repeatedly. + +![](/benchmark/images/zig-zag.png) + +RAM usage + +| Implementation name | RAM usage | +| :--------------------------- | --------: | +| ChunkedQueue | 54243480 | +| MnemonistQueue | 92534230 | +| DatastructuresJsQueue | 104901206 | +| DynamicCyclicQueue | 118183078 | +| EfficientDataStructuresQueue | 150362982 | +| DsaJsQueue | 222575471 | +| LinkedQueue | 245446255 | + +Operations per second + +| Implementation name | Ops/sec | +| :--------------------------- | ------: | +| DatastructuresJsQueue | 61163 | +| MnemonistQueue | 60783 | +| LinkedQueue | 50293 | +| DynamicCyclicQueue | 39873 | +| ChunkedQueue | 39110 | +| EfficientDataStructuresQueue | 22809 | +| DsaJsQueue | 20189 | + +### enqueue-zigzag-dequeue + +Enqueue a lot, then [zigzag](#zigzag) with 1/3 of the size, then dequeue to zero. + +![](/benchmark/images/enqueue-zig-zag-dequeue.png) + +RAM usage + +| Implementation name | RAM usage | +| :--------------------------- | --------: | +| ChunkedQueue | 72945115 | +| DatastructuresJsQueue | 84031309 | +| DynamicCyclicQueue | 89289878 | +| MnemonistQueue | 92892741 | +| LinkedQueue | 279368176 | +| EfficientDataStructuresQueue | 381811473 | +| DsaJsQueue | 383411005 | + +Operations per second + +| Implementation name | Ops/sec | +| :--------------------------- | ------: | +| DynamicCyclicQueue | 84760 | +| DatastructuresJsQueue | 68971 | +| MnemonistQueue | 67296 | +| ChunkedQueue | 37734 | +| LinkedQueue | 21691 | +| EfficientDataStructuresQueue | 18619 | +| DsaJsQueue | 18510 | + +### enqueue only + +Simply enqueue a lot of items, without any dequeue. + +![](/benchmark/images/enqueue-only.png) + +RAM usage + +| Implementation name | RAM usage | +| :--------------------------- | --------: | +| ChunkedQueue | 81815777 | +| MnemonistQueue | 223175067 | +| DatastructuresJsQueue | 223232962 | +| DynamicCyclicQueue | 292281309 | +| LinkedQueue | 359370061 | +| DsaJsQueue | 431394786 | +| EfficientDataStructuresQueue | 431398306 | + +Operations per second + +| Implementation name | Ops/sec | +| :--------------------------- | ------: | +| DatastructuresJsQueue | 57158 | +| MnemonistQueue | 56508 | +| ChunkedQueue | 33323 | +| DynamicCyclicQueue | 30282 | +| LinkedQueue | 10025 | +| EfficientDataStructuresQueue | 9751 | +| DsaJsQueue | 9691 | + +# Analysis +- **In every scenario**, `ChunkedQueue`, introduced in this project, consumes the lowest amount of RAM +- Performance-wise, in most scenarios, the implementations of [@datastructures-js/queue](https://www.npmjs.com/package/@datastructures-js/queue) and [@datastructures-js/queue](https://www.npmjs.com/package/@datastructures-js/queue) have the highest throughput of operations per second +- Surprisingly, `DynamicCyclicQueue`, also introduced in this project, has the highest throughput in the scenario named [enqueue-zigzag-dequeue](#enqueue-zigzag-dequeue) diff --git a/package.json b/package.json index 71ae8c8..898a2fd 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,15 @@ { "name": "lite-fifo", - "version": "0.3.3", + "version": "0.3.4", "license": "MIT", "main": "src/index.js", "types": "types/index.d.ts", "homepage": "https://github.com/kleinron/lite-fifo.git#readme", "description": "Lightweight, optimized, and efficient implementations for FIFO (queue) data structure", "devDependencies": { - "mocha": "^9.2.2", + "mocha": "10.1.0", "seedrandom": "^3.0.5", "semistandard": "^16.0.1", - "shelljs": "^0.8.5", "typescript": "^4.6.4" }, "scripts": { @@ -18,8 +17,6 @@ "lint-with-fix": "semistandard --fix", "test": "mocha test/ --recursive --check-leaks --require test/src/setup.js", "test-stress": "export STRESS_TESTS_ENABLED=true && export STRESS_TESTS_COUNT=10000 && mocha test/ --recursive --check-leaks --require test/src/setup.js", - "benchmark": "node ./benchmark/report-generator.js", - "benchmark-dynamic-array": "node ./benchmark/report-generator.js ./benchmark/report-config-dynamic-array.json", "types": "node ./node_modules/typescript/bin/tsc" }, "author": {