Improve performance (#244) · sindresorhus/p-queue@87077d0 · GitHub
Skip to content

Commit 87077d0

Browse files
authored
Improve performance (#244)
1 parent 5384861 commit 87077d0

3 files changed

Lines changed: 262 additions & 45 deletions

File tree

‎bench.ts‎

Lines changed: 77 additions & 39 deletions

‎source/priority-queue.ts‎

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,50 @@ import {type Queue, type RunFunction} from './queue.js';
22
import lowerBound from './lower-bound.js';
33
import {type QueueAddOptions} from './options.js';
44

5+
const compactionThreshold = 100;
6+
57
export type PriorityQueueOptions = {
68
priority?: number;
79
} & QueueAddOptions;
810

911
export default class PriorityQueue implements Queue<RunFunction, PriorityQueueOptions> {
1012
readonly #queue: Array<PriorityQueueOptions & {run: RunFunction}> = [];
1113

14+
// Index of the next item to dequeue. Old items are compacted lazily so dequeue stays O(1).
15+
#head = 0;
16+
1217
enqueue(run: RunFunction, options?: Partial<PriorityQueueOptions>): void {
1318
const {
1419
priority = 0,
1520
id,
1621
} = options ?? {};
1722

23+
const {size} = this;
1824
const element = {
1925
priority,
2026
id,
2127
run,
2228
};
2329

24-
if (this.size === 0 || this.#queue[this.size - 1]!.priority! >= priority) {
30+
if (size === 0) {
31+
this.#queue.length = 0;
32+
this.#head = 0;
33+
this.#queue.push(element);
34+
return;
35+
}
36+
37+
if (this.#queue.at(-1)!.priority! >= priority) {
2538
this.#queue.push(element);
2639
return;
2740
}
2841

42+
this.#compact();
2943
const index = lowerBound(this.#queue, element, (a: Readonly<PriorityQueueOptions>, b: Readonly<PriorityQueueOptions>) => b.priority! - a.priority!);
3044
this.#queue.splice(index, 0, element);
3145
}
3246

3347
setPriority(id: string, priority: number) {
34-
const index: number = this.#queue.findIndex((element: Readonly<PriorityQueueOptions>) => element.id === id);
48+
const index = this.#queue.findIndex((element: Readonly<PriorityQueueOptions>, index) => index >= this.#head && element.id === id);
3549
if (index === -1) {
3650
throw new ReferenceError(`No promise function with the id "${id}" exists in the queue.`);
3751
}
@@ -43,7 +57,11 @@ export default class PriorityQueue implements Queue<RunFunction, PriorityQueueOp
4357
remove(id: string): void;
4458
remove(run: RunFunction): void;
4559
remove(idOrRun: string | RunFunction): void {
46-
const index = this.#queue.findIndex((element: Readonly<PriorityQueueOptions & {run: RunFunction}>) => {
60+
const index = this.#queue.findIndex((element: Readonly<PriorityQueueOptions & {run: RunFunction}>, index) => {
61+
if (index < this.#head) {
62+
return false;
63+
}
64+
4765
if (typeof idOrRun === 'string') {
4866
return element.id === idOrRun;
4967
}
@@ -57,15 +75,46 @@ export default class PriorityQueue implements Queue<RunFunction, PriorityQueueOp
5775
}
5876

5977
dequeue(): RunFunction | undefined {
60-
const item = this.#queue.shift();
78+
if (this.#head === this.#queue.length) {
79+
return undefined;
80+
}
81+
82+
const item = this.#queue[this.#head];
83+
this.#head++;
84+
85+
if (this.#head === this.#queue.length) {
86+
this.#queue.length = 0;
87+
this.#head = 0;
88+
} else if (this.#head > compactionThreshold && this.#head > this.#queue.length / 2) {
89+
this.#compact();
90+
}
91+
6192
return item?.run;
6293
}
6394

6495
filter(options: Readonly<Partial<PriorityQueueOptions>>): RunFunction[] {
65-
return this.#queue.filter((element: Readonly<PriorityQueueOptions>) => element.priority === options.priority).map((element: Readonly<{run: RunFunction}>) => element.run);
96+
const result: RunFunction[] = [];
97+
98+
for (let index = this.#head; index < this.#queue.length; index++) {
99+
const element = this.#queue[index]!;
100+
if (element.priority === options.priority) {
101+
result.push(element.run);
102+
}
103+
}
104+
105+
return result;
66106
}
67107

68108
get size(): number {
69-
return this.#queue.length;
109+
return this.#queue.length - this.#head;
110+
}
111+
112+
#compact(): void {
113+
if (this.#head === 0) {
114+
return;
115+
}
116+
117+
this.#queue.splice(0, this.#head);
118+
this.#head = 0;
70119
}
71120
}

‎test/priority-queue.ts‎

Lines changed: 130 additions & 0 deletions

0 commit comments

Comments
 (0)