Skip to content

Commit

Permalink
Move computations in worker
Browse files Browse the repository at this point in the history
  • Loading branch information
Jumbub committed Mar 27, 2022
1 parent f01efd7 commit 13ce69f
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 23 deletions.
35 changes: 28 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,34 @@ http://localhost:8080/benchmark

## Log of benchmark improvements

### Initial benchmark (75s)
### First working benchmark (~75s)

Fairly much a manual transpilation from the original [C++ implementation](https://github.com/Jumbub/game-of-speed) to Typescript, but without using an advanced JS.
Naive and simple manual transpilation of the [C++ implementation](https://github.com/Jumbub/game-of-speed) to Typescript, without any advanced Javascript (typed arrays, workers, etc).

[c15cae4d00beb59f5e6e50f495a323eca3f24eb5](https://github.com/Jumbub/game-of-snails/commit/9227c6a55ede200a1b6fe827c93010963e704f3d)

### Use UInt8Array instead of regular Array for booleans (53s)
### Use smaller typed arrays for skip cells (~53s)

First step towards some advanced JS.
First attempt at using typed arrays, using Uint8Arrays to store the skip data.
(matching how the c++ implementation does it)

[01c5c850ccef4075d01441983a55cae6aa127c2a](https://github.com/Jumbub/game-of-snails/commit/01c5c850ccef4075d01441983a55cae6aa127c2a)

### Closer towards the C++ code, first "real" benchmark (36.86s)
### Use smaller typed arrays for data cells (~37s)

This benchmark achieves the 30 renders per second requirement, and also adds a check to ensure the average never dips below 29.97.
Use Uint8Array's for storing state of a cell, then create the Uint32Array required for rendering at render time.
(matching how the c++ implementation does it)

The code is also another couple steps closer to the C++ algorithm, because we're now storing our cells in the same data structure, and performing the same render calculations.
This benchmark also adds a requirement that the renders per second never drops below 29.97.

[1263580a75a6ac861616411fddc1a9605e995292](https://github.com/Jumbub/game-of-snails/commit/1263580a75a6ac861616411fddc1a9605e995292)

### Moving the computation into a worker (~28s)

Create a single worker which listens for computation requests from the primary thread.

[COMMIT](TODO)

<br/>

## Interesting findings
Expand All @@ -69,3 +77,16 @@ The code is also another couple steps closer to the C++ algorithm, because we're
### Casting from String 1s and 0s to Numbers

[Benchmark demonstrating ways of casting from an array of string 1s or 0s.](https://perf.link/#eyJpZCI6InUzeXptZW8zZWF1IiwidGl0bGUiOiJGaW5kaW5nIG51bWJlcnMgaW4gYW4gYXJyYXkgb2YgMTAwMCIsImJlZm9yZSI6ImNvbnN0IGRhdGEgPSBbLi4uQXJyYXkoMTAwMDApLmtleXMoKV0ubWFwKGkgPT4gU3RyaW5nKE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSkpKSIsInRlc3RzIjpbeyJuYW1lIjoiRmluZCBpdGVtIDEwMCIsImNvZGUiOiJkYXRhLm1hcCh4ID0%2BIHggPT09ICcxJyA%2FIDEgOiAwKSIsInJ1bnMiOlsyMTY2LDM4MzMsMzgzMyw1MzMzLDU4MzMsNTY2Niw1ODMzLDMxNjYsMzgzMyw0MzMzLDQ1MDAsNTUwMCw1ODMzLDMzMzMsNTMzMyw1NTAwLDU1MDAsNjAwMCw1MDAwLDQxNjYsNjUwMCw2MTY2LDU4MzMsNTMzMyw1MTY2LDYzMzMsNDgzMyw1NTAwLDUwMDAsNTAwMCwzMDAwLDUzMzMsNTY2NiwzNTAwLDM2NjYsNTgzMyw1MzMzLDUwMDAsNDUwMCw0MzMzLDU1MDAsNTY2Niw2NTAwLDI4MzMsNTgzMyw0MTY2LDYxNjYsNjMzMyw1MTY2LDM4MzMsNjUwMCwzMzMzLDM4MzMsNTAwMCwyODMzLDU1MDAsNDE2Niw2MTY2LDU1MDAsNTMzMyw1MTY2LDQwMDAsNDY2Niw1MTY2LDM1MDAsNTUwMCw2MzMzLDM2NjYsNDgzMyw1NjY2LDYwMDAsNDAwMCw0ODMzLDYzMzMsNTUwMCw2MTY2LDE1MDAsNTMzMywxNTAwLDU2NjYsNTAwMCw1MDAwLDUwMDAsNjAwMCw1MzMzLDUwMDAsNTUwMCw2MTY2LDM4MzMsMzY2Niw2NTAwLDY1MDAsNTAwMCw1NTAwLDQ1MDAsNTY2Niw0MDAwLDQ2NjYsNTE2Niw1NjY2XSwib3BzIjo0OTU0fSx7Im5hbWUiOiJGaW5kIGl0ZW0gMjAwIiwiY29kZSI6ImRhdGEubWFwKHggPT4gTnVtYmVyKHgpKSIsInJ1bnMiOlsyODMzLDQwMDAsNTY2Niw3MTY2LDY2NjYsNjMzMyw1ODMzLDYzMzMsNTY2Niw1MzMzLDYwMDAsNDgzMyw2MzMzLDQzMzMsNTY2Niw2MzMzLDU1MDAsNTE2Niw2MDAwLDUxNjYsNjMzMyw1NTAwLDQzMzMsNTY2Niw0MzMzLDQxNjYsNjE2Niw1MzMzLDUwMDAsNTUwMCw1NTAwLDU1MDAsNTAwMCw1NjY2LDY4MzMsNTMzMyw0ODMzLDU4MzMsNzAwMCw2MzMzLDY2NjYsNjMzMyw2MDAwLDUwMDAsNjE2Niw2ODMzLDY2NjYsNjUwMCw1MDAwLDYwMDAsNjAwMCw0MDAwLDU1MDAsNTUwMCw2MzMzLDYxNjYsNzAwMCw3MDAwLDUwMDAsNjAwMCw0ODMzLDYwMDAsNTY2Niw1MTY2LDYxNjYsNzE2Niw2MzMzLDI4MzMsNjMzMyw2MTY2LDc1MDAsMTE2Niw1MTY2LDU2NjYsNjE2Niw2MzMzLDUwMDAsNDE2Niw1MTY2LDY1MDAsNTE2Niw1NTAwLDY2NjYsNTUwMCw3MTY2LDUwMDAsNDY2Niw1NTAwLDYxNjYsNDAwMCw2MTY2LDE1MDAsNjgzMyw2MDAwLDYxNjYsNTgzMyw2MTY2LDYzMzMsNTAwMCw2ODMzXSwib3BzIjo1NjI2fSx7Im5hbWUiOiJGaW5kIGl0ZW0gNDAwIiwiY29kZSI6ImRhdGEubWFwKHggPT4gcGFyc2VJbnQoeCkpIiwicnVucyI6WzIxNjYsNTAwMCw2MzMzLDYwMDAsNjUwMCw1NjY2LDQ4MzMsNTMzMyw0MDAwLDUzMzMsNTUwMCw3MTY2LDU2NjYsNzAwMCwzNTAwLDY2NjYsNjY2Niw2MTY2LDYxNjYsNjUwMCw2MDAwLDUwMDAsNzY2Niw0ODMzLDQ1MDAsNTY2Niw2MzMzLDYwMDAsNTAwMCwzMzMzLDY2NjYsMTUwMCw1ODMzLDY4MzMsNjE2Niw0MzMzLDYxNjYsNjMzMyw3MTY2LDQ2NjYsNzE2Niw1NTAwLDUwMDAsNTMzMyw2NTAwLDcwMDAsNjgzMyw2NTAwLDUzMzMsNTY2Niw1MDAwLDYwMDAsNjMzMyw1MTY2LDUzMzMsNDgzMyw2MzMzLDY2NjYsNTMzMyw2ODMzLDYzMzMsNjgzMyw2NTAwLDQ4MzMsNjE2Niw1MzMzLDY1MDAsNTAwMCw2MDAwLDc1MDAsNzE2Niw0MzMzLDYzMzMsNTUwMCw1MTY2LDQwMDAsNzAwMCw2NTAwLDQ4MzMsNTgzMyw2ODMzLDYxNjYsNTMzMyw2NTAwLDY2NjYsNjAwMCw3MzMzLDQ1MDAsNDMzMyw2ODMzLDY1MDAsNjE2Niw1NTAwLDM4MzMsNTAwMCw3MTY2LDY1MDAsNTgzMyw2NjY2LDYwMDBdLCJvcHMiOjU3ODF9XSwidXBkYXRlZCI6IjIwMjItMDMtMjZUMTA6NTM6MjUuNDI3WiJ9)

### Having the same work done in a worker is slower

The speed at this [commit](TODO) went from 35.98 gens/sec to 3.7 seconds _just_ by moving the same work to a worker.

### SharedArrayBuffer restrictions

In Chrome, this feature requires the page is loaded with the following headers:

```
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
```
8 changes: 8 additions & 0 deletions src/benchmark/bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ generations/second: ${(meta.generations / seconds).toFixed(2)}
renders/second: ${rps.toFixed(2)}
${valid}`;

const result = document.createElement('div');
result.style.position = 'fixed';
result.style.padding = '10px';
result.style.backgroundColor = 'black';
result.style.color = 'white';
result.innerHTML = report.replace(/\n/g, '<br/>');
document.body.append(result);

console.log(report);
};

Expand Down
7 changes: 6 additions & 1 deletion src/graphics/loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { render } from './render.js';
export type Meta = {
board: Board;
context: CanvasRenderingContext2D;
workers: Worker[];
maxGenerations: number;
generations: number;
renders: number;
Expand All @@ -23,6 +24,9 @@ export const setup = (
): Meta => {
const board = newBoard(viewWidth, viewHeight);
const context = newContext(viewWidth, viewHeight);
const workers = Array(1)
.fill(0)
.map((_, i) => new Worker('/logic/worker.js', { name: 'worker', type: 'module' }));
const meta: Meta = {
board,
context,
Expand All @@ -31,6 +35,7 @@ export const setup = (
onDone,
generations: 0,
renders: 0,
workers,
};

window.addEventListener('blur', () => {
Expand All @@ -51,8 +56,8 @@ export const run = (meta: Meta) => {
meta.renders++;

if (meta.generations >= meta.maxGenerations) {
meta.onDone(meta);
clearInterval(interval);
meta.onDone(meta);
return;
}
}, meta.rendersMinimumMilliseconds);
Expand Down
14 changes: 8 additions & 6 deletions src/logic/board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@ export const newBoard = (viewWidth: number, viewHeight: number) => {
const width = viewWidth + 2;
const height = viewHeight + 2;

const newCells = () => new Uint8Array(width * height).fill(DEAD);
const newSkips = () => new Uint8Array(width * height).fill(DONT_SKIP);
const makeZeros = () => {
const buffer = new SharedArrayBuffer(width * height);
return new Uint8Array(buffer);
};

const board: Board = {
width,
height,
input: newCells(),
output: newCells(),
inSkip: newSkips(),
outSkip: newSkips(),
input: makeZeros(),
output: makeZeros(),
inSkip: makeZeros(),
outSkip: makeZeros(),
};

return board;
Expand Down
30 changes: 23 additions & 7 deletions src/logic/next.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Meta } from '../graphics/loop.js';
import { Board, Cells, DONT_SKIP, SKIP, Skips, SKIP_MULTIPLYER } from './board.js';
import { Board, Cells, DONT_SKIP, Skip, SKIP, Skips, SKIP_MULTIPLYER } from './board.js';
import { assignBoardPadding } from './padding.js';

export const LOOKUP = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0] as const;
Expand Down Expand Up @@ -53,13 +53,29 @@ export const next = (board: Board) => {
};

export const startNextBoardLoop = (meta: Meta & { loop?: () => void }) => {
// let endI = meta.board.width;
// const segmentSize = (meta.board.height / JOBS + meta.board.height % JOBS) * meta.board.width;
// const segments = Array(JOBS).fill(1).map(() => {
// const beginI = endI;
// endI = Math.min(meta.board.width*(meta.board.height-1), endI + segmentSize)
// return {
// beginI,
// endI,
// };
// })
const loop = () => {
next(meta.board);
meta.generations++;

if (meta.generations < meta.maxGenerations) {
setTimeout(loop, 0);
}
meta.workers.forEach(worker => {
const handleWorkerMessage = (event: MessageEvent<Board>) => {
worker.removeEventListener('message', handleWorkerMessage);
meta.board = event.data;
meta.generations++;
if (meta.generations < meta.maxGenerations) {
setTimeout(loop, 0);
}
};
worker.addEventListener('message', handleWorkerMessage);
worker.postMessage(meta.board);
});
};
setTimeout(loop, 0);
};
8 changes: 8 additions & 0 deletions src/logic/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Board } from './board.js';
import { next } from './next.js';

addEventListener('message', (event: MessageEvent<Board>) => {
const board = event.data;
next(board);
postMessage(board);
});
4 changes: 2 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */

/* Language and Environment */
"target": "es2015", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
Expand All @@ -24,7 +24,7 @@
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */

/* Modules */
"module": "es2015", /* Specify what module code is generated. */
"module": "esnext", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
Expand Down

0 comments on commit 13ce69f

Please sign in to comment.