Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

worker benchmarks #2059

Merged
merged 8 commits into from Apr 5, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion js/workers.ts
Expand Up @@ -150,23 +150,30 @@ export interface Worker {
onmessage?: (e: { data: any }) => void;
onmessageerror?: () => void;
postMessage(data: any): void;
closed: Promise<void>;
}

export class WorkerImpl implements Worker {
private readonly rid: number;
private isClosing: boolean = false;
private readonly isClosedPromise: Promise<void>;
public onerror?: () => void;
public onmessage?: (data: any) => void;
public onmessageerror?: () => void;

constructor(specifier: string) {
this.rid = createWorker(specifier);
this.run();
hostGetWorkerClosed(this.rid).then(() => {
this.isClosedPromise = hostGetWorkerClosed(this.rid);
this.isClosedPromise.then(() => {
this.isClosing = true;
});
}

get closed(): Promise<void> {
return this.isClosedPromise;
}

postMessage(data: any): void {
hostPostMessage(this.rid, data);
}
Expand Down
20 changes: 20 additions & 0 deletions tests/subdir/bench_worker.ts
@@ -0,0 +1,20 @@
onmessage = function(e) {
const { cmdId, action, data } = e.data;
switch (action) {
case 0: // Static response
postMessage({
cmdId,
data: "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n"
});
break;
case 1: // Respond with request data
postMessage({ cmdId, data });
break;
case 2: // Ping
postMessage({ cmdId });
break;
case 3: // Close
workerClose();
break;
}
};
72 changes: 72 additions & 0 deletions tests/workers_round_robin_bench.ts
@@ -0,0 +1,72 @@
const data = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n";
ry marked this conversation as resolved.
Show resolved Hide resolved
const workerCount = 4;
const cmdsPerWorker = 400;

export interface ResolvableMethods<T> {
resolve: (value?: T | PromiseLike<T>) => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reject: (reason?: any) => void;
}

export type Resolvable<T> = Promise<T> & ResolvableMethods<T>;
ry marked this conversation as resolved.
Show resolved Hide resolved

export function createResolvable<T>(): Resolvable<T> {
let methods: ResolvableMethods<T>;
const promise = new Promise<T>((resolve, reject) => {
methods = { resolve, reject };
});
// TypeScript doesn't know that the Promise callback occurs synchronously
// therefore use of not null assertion (`!`)
return Object.assign(promise, methods!) as Resolvable<T>;
}

function handleAsyncMsgFromWorker(
promiseTable: Map<number, Resolvable<string>>,
msg: { cmdId: number; data: string }
): void {
const promise = promiseTable.get(msg.cmdId);
if (promise === null) {
throw new Error(`Failed to find promise: cmdId: ${msg.cmdId}, msg: ${msg}`);
}
promise.resolve(data);
}

async function main(): Promise<void> {
const workers: Array<[Map<number, Resolvable<string>>, Worker]> = [];
for (var i = 1; i <= workerCount; ++i) {
const worker = new Worker("tests/subdir/bench_worker.ts");
const promise = new Promise(resolve => {
worker.onmessage = e => {
if (e.data.cmdId === 0) resolve();
};
});
worker.postMessage({ cmdId: 0, action: 2 });
await promise;
workers.push([new Map(), worker]);
}
// assign callback function
for (const [promiseTable, worker] of workers) {
worker.onmessage = e => {
handleAsyncMsgFromWorker(promiseTable, e.data);
};
}
for (const cmdId of Array(cmdsPerWorker).keys()) {
const promises: Array<Promise<string>> = [];
for (const [promiseTable, worker] of workers) {
const promise = createResolvable<string>();
promiseTable.set(cmdId, promise);
worker.postMessage({ cmdId: cmdId, action: 1, data });
promises.push(promise);
}
for (const promise of promises) {
await promise;
}
}
for (const [, worker] of workers) {
worker.postMessage({ action: 3 });
await worker.closed; // Required to avoid a cmdId not in table error.
}
console.log("Finished!");
}

main();
24 changes: 24 additions & 0 deletions tests/workers_startup_bench.ts
@@ -0,0 +1,24 @@
const workerCount = 50;

async function bench(): Promise<void> {
const workers: Worker[] = [];
for (var i = 1; i <= workerCount; ++i) {
const worker = new Worker("tests/subdir/bench_worker.ts");
const promise = new Promise(resolve => {
worker.onmessage = e => {
if (e.data.cmdId === 0) resolve();
};
});
worker.postMessage({ cmdId: 0, action: 2 });
await promise;
workers.push(worker);
}
console.log("Done creating workers closing workers!");
for (const worker of workers) {
worker.postMessage({ action: 3 });
await worker.closed; // Required to avoid a cmdId not in table error.
}
console.log("Finished!");
}

bench();
2 changes: 2 additions & 0 deletions tools/benchmark.py
Expand Up @@ -25,6 +25,8 @@
("error_001", ["tests/error_001.ts"]),
("cold_hello", ["tests/002_hello.ts", "--reload"]),
("cold_relative_import", ["tests/003_relative_import.ts", "--reload"]),
("workers", ["tests/workers_startup_bench.ts"]),
("workers_round_robin", ["tests/workers_round_robin_bench.ts"]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

]

gh_pages_data_file = "gh-pages/data.json"
Expand Down