Created
February 25, 2020 14:19
-
-
Save mdubourg001/dbbddaf80926b52cd61ad42b66f04514 to your computer and use it in GitHub Desktop.
Multiplication of huge array of number using waitable, inline workers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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