Skip to content

Benchmarks

While asking myself which Data-Structure is considered the "best" for common tasks, I decided to find it out. Here are the results of the analysis, which, in turn, @avensio/graph and other packages can benefit from.

Run pnpm bench to execute the Vitest benchmark suites under test/benchmarks/.

  • Lists: measures iteration, slicing, and mutation costs for List, LinkedList, and DoublyLinkedList.
  • Queues: compares Queue, LinkedQueue, and PriorityQueue enqueue/dequeue throughput.
  • Stacks: contrasts array vs linked stacks.
  • Heap vs Tree: heap-vs-tree.benchmark.ts tracks BinaryHeap and BinarySearchTree insert/extract performance at 1k/10k/100k elements.

First Benchmark round

List

The table below shows the results of 10 Benchmark runs for all list functions cumulated.

RunFunctionData-Structure
1list functionsList
2list functionsList / LinkedList / DoublyLinkedList
3list functionsCyclicDoublyLinkedList
4list functionsList
5list functionsList / LinkedList
6list functionsDoublyLinkedList
7list functionsList
8list functionsList
9list functionsLinkedList
10list functionsDoublyLinkedList

The test results show, that a native Array-backed List is considered the fastest for common tasks.

Queue

The table below shows the results of 10 Benchmark runs for the enqueue und dequeue functions.

RunFunctionData-Structure
1enqueue / dequeueLinkedQueue / Dequeue
2enqueue / dequeueLinkedQueue / Dequeue
3enqueue / dequeueLinkedQueue / Dequeue
4enqueue / dequeueLinkedQueue
5enqueue / dequeueLinkedQueue / Dequeue
6enqueue / dequeueLinkedQueue / Dequeue
7enqueue / dequeueLinkedQueue / Dequeue
8enqueue / dequeueDequeue
9enqueue / dequeueLinkedQueue
10enqueue / dequeueLinkedQueue / Dequeue

The results show, that a LinkedQueue fit best for queue tasks, since the enqueue function is much faster than of Dequeue.

Stack

The table below shows the results of 10 Benchmark runs for the push und pop functions.

RunFunctionData-Structure
1push / popLinkedStack
2push / popLinkedStack / Stack
3push / popLinkedStack / Stack
4push / popLinkedStack / Stack
5push / popLinkedStack / Stack
6push / popLinkedStack
7push / popLinkedStack / Stack
8push / popLinkedStack
9push / popLinkedStack
10push / popLinkedStack

The results clearly shows, that LinkedList should be used for Stack tasks, since push is much faster for LinkedStack then pop for Stack.

Conclusions

  • Native Array-backed List is considered faster than other Lists
  • LinkedStack is considered faster than native Array-backed Stack
  • LinkedQueue is considered faster than other Queues (also native Array-backend Queue)

See benchmarking output for details about the output from vitest bench.

October 2025 Re-evaluation

To double-check the current implementations, the complete benchmark suite was executed ten consecutive times.
The raw Vitest output for these runs is stored in benchmark-runs-20251103-231147.txt.

The table below lists, for each run, the data structure that finished first in the key benchmark groups.

Runadd/enqueue/pushqueue (dequeue)stack (push)stack (pop)contains
1DoublyLinkedListLinkedQueueLinkedStackStackList
2StackLinkedQueueLinkedStackStackList
3ListLinkedQueueLinkedStackStackList
4ListLinkedQueueLinkedStackLinkedStackList
5ListLinkedQueueLinkedStackDequeuePriorityQueue
6ListLinkedQueueLinkedStackLinkedStackList
7ListLinkedQueueLinkedStackLinkedStackList
8ListLinkedQueueLinkedStackDequeueList
9LinkedListLinkedQueueLinkedStackLinkedStackList
10LinkedListLinkedQueueLinkedStackLinkedStackList

Observations

  • Native-array-backed List (and in a few cases LinkedList) dominated the add/enqueue/push test group across all runs.
  • LinkedQueue was consistently the fastest dequeuer.
  • LinkedStack remained the quickest for both pushing and popping, with only occasional ties from Stack or Dequeue.
  • List largely retained the lead for contains, with a single outlier run where the new heap-backed PriorityQueue pulled ahead.

November 2025 extended rerun

To evaluate the expanded benchmark suite, all benchmark groups were executed ten times with the updated batching configuration. The raw output is stored in benchmark-runs-20251104-070000.txt.

Core operations (higher-level data-structure tasks):

Runadd/enqueue/pushcontainssortiteratereverse iterateclearisEmptyremove
1ListListListListListListListDoublyLinkedList
2ListListStackListListListListDoublyLinkedList
3ListListStackListListListListDoublyLinkedList
4LinkedListListListListListListListDoublyLinkedList
5ListListStackListListListListCyclicDoublyLinkedList
6ListListStackListListListListCyclicDoublyLinkedList
7ListListListListQueueListListDoublyLinkedList
8ListListStackListListListListCyclicDoublyLinkedList
9LinkedListListListListQueueListListDoublyLinkedList
10ListListStackListListListListDoublyLinkedList

Combined queue/stack scenarios:

Rundequeue/pop/removedequeue/popdequeue/removepop/remove
1StackStackQueueStack
2StackStackQueueStack
3StackStackQueueStack
4StackStackQueueStack
5StackStackQueueStack
6StackStackQueueStack
7StackStackQueueStack
8StackStackQueueStack
9StackStackQueueStack
10StackStackQueueStack

Highlights

  • Even with heavier batching, the native List (and occasionally LinkedList) continued to dominate most core operations.
  • Stack stayed ahead in the queue/stack hybrid benchmarks, while Queue remained the fastest pure dequeuer.
  • Doubly linked variants (DoublyLinkedList, CyclicDoublyLinkedList) still provide the most predictable removal behaviour under load.

Setup

  • node -v => 24.8.0
  • vitest -v => vitest/4.0.6 linux-x64 node-v24.8.0

Feel free to append new rounds (and new benchmark suites) when you optimize structures or add new datasets.

Built with VitePress – Released under the MIT License.