Skip to content

Commit

Permalink
feat(core): Performance improvements with immutable rules
Browse files Browse the repository at this point in the history
  • Loading branch information
wadackel committed Nov 8, 2020
1 parent 361cd2b commit 572edef
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 42 deletions.
2 changes: 2 additions & 0 deletions packages/acot-preset-axe/src/create-rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export const createRule = (config: CreateRuleConfig): Rule<Options> => {
return createAcotRule<Options>({
type: 'global',

immutable: true,

meta: {
recommended: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Options = {};
export default createRule<Options>({
type: 'contextual',
selector: SELECTOR,
immutable: true,
schema: {},
meta: {
tags: ['wcag21aaa', '2.5.5 Target Size'],
Expand Down
1 change: 1 addition & 0 deletions packages/acot-preset-wcag/src/rules/page-has-title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type Options = {};

export default createRule<Options>({
type: 'global',
immutable: true,
meta: {
tags: ['wcag2.1', 'wcag2.4.2'],
recommended: true,
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"emittery": "^0.7.1",
"filenamify": "^4.1.0",
"lodash": "^4.17.19",
"p-series": "^2.1.0",
"promise.allsettled": "^1.0.2",
"puppeteer-core": "^5.4.1",
"puppeteer-element2selector": "^0.0.1"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/__tests__/browser-pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe('BrowserPool', () => {
await pool.bootstrap(2);

const mock = jest.fn().mockReturnValue(Promise.resolve());
await pool.execute(mock);
await pool.execute(1, mock);
expect(mock).toBeCalled();
});

Expand Down
27 changes: 27 additions & 0 deletions packages/core/src/__tests__/queue.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Queue } from '../queue';

describe('Queue', () => {
test('enqueue / dequeue / size', () => {
const queue = new Queue<string>();

expect(queue.size).toBe(0);

queue.enqueue(3, 'data1');
queue.enqueue(1, 'data2');
queue.enqueue(2, 'data3');

expect(queue.size).toBe(3);

expect(queue.dequeue()).toBe('data1');
expect(queue.size).toBe(2);

expect(queue.dequeue()).toBe('data3');
expect(queue.size).toBe(1);

expect(queue.dequeue()).toBe('data2');
expect(queue.size).toBe(0);

expect(queue.dequeue()).toBeNull();
expect(queue.size).toBe(0);
});
});
1 change: 1 addition & 0 deletions packages/core/src/acot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export class Acot implements Core {
this._testers.push(
new Tester({
...descriptor,
priority: (this._testers.length + 1) * -1,
workingDir: this._config.workingDir,
viewport: this._config.viewport,
readyTimeout,
Expand Down
19 changes: 13 additions & 6 deletions packages/core/src/browser-pool.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { LaunchOptions } from 'puppeteer-core';
import { Browser } from './browser';
import { Queue } from './queue';

const WORK_INTERVAL = 30;

Expand All @@ -22,7 +23,7 @@ export type BrowserPoolConfig = {

export class BrowserPool {
private _config: BrowserPoolConfig;
private _queue: BrowserJob[] = [];
private _queue: Queue<BrowserJob> = new Queue();
private _available: Set<Browser> = new Set();
private _busy: Set<Browser> = new Set();
private _interval: NodeJS.Timeout | null = null;
Expand Down Expand Up @@ -71,20 +72,26 @@ export class BrowserPool {
}

public execute<T extends any[] = any[]>(
priority: number,
fn: BrowserTask<T>,
...args: T
): Promise<T> {
): Promise<void> {
return new Promise((resolve, reject) => {
this._queue.push({ fn: fn as BrowserTask, args, resolve, reject });
this._queue.enqueue(priority, {
fn: fn as BrowserTask,
args,
resolve,
reject,
});
});
}

private async _work(): Promise<void> {
if (this._available.size === 0 || this._queue.length === 0) {
if (this._available.size === 0 || this._queue.size === 0) {
return;
}

const job = this._queue.shift()!;
const job = this._queue.dequeue()!;
const browser = [...this._available.values()].shift()!;

this._busy.add(browser);
Expand All @@ -99,7 +106,7 @@ export class BrowserPool {
this._busy.delete(browser);
this._available.add(browser);

if (this._queue.length > 0) {
if (this._queue.size > 0) {
this._work();
}
}
Expand Down
97 changes: 97 additions & 0 deletions packages/core/src/queue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Priority Queue (using pairing heap algorithm)
*/
type QueueNode<T> = {
priority: number;
data: T;
next: QueueNode<T> | null;
head: QueueNode<T> | null;
};

const merge = <T>(i: QueueNode<T> | null, j: QueueNode<T> | null) => {
if (i == null) {
return j;
}

if (j == null) {
return i;
}

let tmp: QueueNode<T> | null = null;

if (i.priority < j.priority) {
tmp = i;
i = j;
j = tmp;
}

j.next = i.head;
i.head = j;

return i;
};

const mergeList = <T>(s: QueueNode<T> | null) => {
let n: QueueNode<T> | null = null;
let a: QueueNode<T> | null = null;
let b: QueueNode<T> | null = null;
let j: QueueNode<T> | null = null;

while (s != null) {
a = s;
b = null;
s = s.next;
a.next = null;

if (s != null) {
b = s;
s = s.next;
b.next = null;
}

a = merge(a, b);
a!.next = n;
n = a;
}

while (n != null) {
j = n;
n = n.next;
s = merge(j, s);
}

return s;
};

export class Queue<T> {
private _root: QueueNode<T> | null = null;
private _size = 0;

public enqueue(priority: number, data: T): void {
this._root = merge(this._root, {
priority,
data,
next: null,
head: null,
});

this._size += 1;
}

public dequeue(): T | null {
if (this._root == null) {
return null;
}

const result = this._root.data;

this._root = mergeList(this._root.head);
this._size -= 1;

return result;
}

public get size(): number {
return this._size;
}
}

0 comments on commit 572edef

Please sign in to comment.