Skip to content

Commit

Permalink
move benchmarks to a separated package.
Browse files Browse the repository at this point in the history
add external packages to benchmarks.
  • Loading branch information
kleinron committed Aug 15, 2024
1 parent 7d48c41 commit fba6755
Show file tree
Hide file tree
Showing 11 changed files with 539 additions and 45 deletions.
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@

# 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)
* throughput (ops/sec)

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
Expand Down Expand Up @@ -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
Expand All @@ -161,24 +169,23 @@ 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.

## Results
| 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
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added benchmark/images/enqueue-only.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added benchmark/images/enqueue-zig-zag-dequeue.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added benchmark/images/zig-zag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions benchmark/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
16 changes: 16 additions & 0 deletions benchmark/ramusage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
39 changes: 34 additions & 5 deletions benchmark/report-config.json
Original file line number Diff line number Diff line change
@@ -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
}
"verbose": true
}
Loading

0 comments on commit fba6755

Please sign in to comment.