Skip to content

Instantly share code, notes, and snippets.

@mdubourg001
Created February 25, 2020 14:19
Show Gist options
  • Save mdubourg001/dbbddaf80926b52cd61ad42b66f04514 to your computer and use it in GitHub Desktop.
Save mdubourg001/dbbddaf80926b52cd61ad42b66f04514 to your computer and use it in GitHub Desktop.
Multiplication of huge array of number using waitable, inline workers.
/*
* Multiplication of a huge amount of numbers throught Web Workers
* The goal is:
* - to measure performance earnings
* - to implement and try 'Inline' and 'Waitable' Web Workers
*/
/* tsconfig.json
{
"target": "ESNext",
"compilerOptions": {
"lib": ["ESNext", "DOM"]
}
}
*/
// -----
// utils
// -----
type IWaitableWorker = Worker & {
receive: () => Promise<MessageEvent>;
};
/**
* An inline Worker that can be used with async / await
*/
const WaitableInlineWorker = (
fn: (...args: any[]) => void
): IWaitableWorker => {
if (!window.Worker) throw "Browser does not support the Worker API.";
const fnBody = fn
.toString()
.replace(/^[^{]*{\s*/, "")
.replace(/\s*}[^}]*$/, "");
const worker: IWaitableWorker = new Worker(
URL.createObjectURL(new Blob([fnBody], { type: "text/javascript" }))
) as IWaitableWorker;
worker.receive = async () =>
new Promise(resolve => {
worker.onmessage = (event: MessageEvent) => resolve(event);
});
return worker;
};
/**
* Utility function to measure execution time
*/
const startTimer = (start = new Date().getTime()) => ({
start,
stop: () => console.log(new Date().getTime() - start)
});
/**
* Returns an array of `size` random numbers between 1 and 10
*/
const getHugeNumberArray = (size: number) =>
Array.from({ length: size }, () => BigInt(Math.floor(Math.random() * 9) + 1));
/**
* Splits an array in `nb` more little arrays
*/
const splitDataset = (dataset: any[], nb: number) => {
const split = [];
const sliceSize: number = Math.floor(dataset.length / nb);
for (let i = 0; i < nb; i++) {
split.push(
dataset.slice(
i * sliceSize,
i === nb - 1 ? undefined : (i + 1) * sliceSize
)
);
}
return split;
};
// -----
// main
// -----
/**
* A function aiming to be used throught a WaitableWorker
*/
const arrayMul = () => {
onmessage = event => {
const data: bigint[] = event.data;
const mul = data.reduce((acc, cur) => acc * cur, BigInt(1));
postMessage(mul);
};
};
const main = async () => {
// generating a huge array of random BigInts
const hugeArray = getHugeNumberArray(100_000);
// -----------------
// case 1: using 1 single worker
// -----------------
const worker = WaitableInlineWorker(arrayMul);
let timer = startTimer();
worker.postMessage(hugeArray);
const { data: firstCaseData } = await worker.receive();
console.log(`=> ${firstCaseData}`);
timer.stop();
// -----------------
// case 1: using 10 parallel workers
// -----------------
// splitting the dataset in 10
const splittedHugeArray = splitDataset(hugeArray, 10);
// for each chunk, launching a parallel worker
const workers = splittedHugeArray.map(chunk => {
const w = WaitableInlineWorker(arrayMul);
w.postMessage(chunk);
return w;
});
timer = startTimer();
const secondCaseData = (
await Promise.all(workers.map(w => w.receive()))
).reduce((acc, cur) => acc * cur.data, BigInt(1));
console.log(`=> ${secondCaseData}`);
timer.stop();
// -----------------
// checking that two executions give the same result
console.log(`=> ${firstCaseData === secondCaseData}`);
};
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment