The Computer Language
22.03 Benchmarks Game

mandelbrot TypeScript #3 program

source code

/* The Computer Language Benchmarks Game
   https://salsa.debian.org/benchmarksgame-team/benchmarksgame/

   contributed by Andrey Filatkin
   based on mandelbrot.go-4.go contributed by Martin Koistinen and others
   converted by Isaac Gouy
*/

import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';
const os = require('os');

const limit = 4;
const maxIter = 50;
const bytesPerFloat = Float64Array.BYTES_PER_ELEMENT;

if (isMainThread) {
    mainThread(+process.argv[2]);
} else {
    workerThread(workerData);
}

async function mainThread(size: number) {
    const bytesPerRow = size >> 3;

    const nextYSize = bytesPerFloat;
    const initialSize = size * bytesPerFloat;
    const rowsStart = nextYSize + 2 * initialSize;
    const rowsSize = size * bytesPerRow;

    const sab = new SharedArrayBuffer(nextYSize + 2 * initialSize + rowsSize);
    const initialR = new Float64Array(sab, nextYSize, size);
    const initialI = new Float64Array(sab, nextYSize + initialSize, size);
    const inv = 2 / size;
    for (let xy = 0; xy < size; xy++) {
        const i = inv * xy;
        initialR[xy] = i - 1.5;
        initialI[xy] = i - 1.0;
    }

    await work();

    process.stdout.write(`P4\n${size} ${size}\n`);
    process.stdout.write(new Uint8Array(sab, rowsStart, rowsSize));

    async function work() {
        return new Promise(resolve => {
            const cpus = os.cpus().length;
            let wait = 0;
            for (let i = 0; i < cpus; i++) {
                const worker = new Worker(__filename, {workerData: {size, bytesPerRow}});
                worker.postMessage({name: 'sab', data: sab});
                worker.on('exit', () => {
                    wait--;
                    if (wait === 0) {
                        resolve();
                    }
                });
                wait++;
            }
        });
    }
}

function workerThread({size, bytesPerRow}: Data) {
    const nextYSize = bytesPerFloat;
    const initialSize = size * bytesPerFloat;
    const rowsStart = nextYSize + 2 * initialSize;

    let sab: SharedArrayBuffer;
    let nextY: Int32Array;
    let initialR: Float64Array;
    let initialI: Float64Array;

    parentPort?.on('message', message => {
        if (message.name === 'sab') {
            sab = message.data;
            nextY = new Int32Array(sab, 0, 1);
            initialR = new Float64Array(sab, nextYSize, size);
            initialI = new Float64Array(sab, nextYSize + initialSize, size);

            renderRows();
        }
    });

    function renderRows() {
        let y;
        while ((y = Atomics.load(nextY, 0)) < size) {
            if (Atomics.compareExchange(nextY, 0, y, y + 1) === y) {
                const row = new Uint8Array(sab, rowsStart + y * bytesPerRow, bytesPerRow);
                renderRow(row, y);
            }
        }
        process.exit();
    }

    function renderRow(row: Uint8Array, y0: number) {
        for (let xByte = 0; xByte < bytesPerRow; xByte++) {
            const ci = initialI[y0];
            let res = 0;
            for (let i = 0; i < 8; i += 2) {
                const x = xByte << 3;
                const cr1 = initialR[x + i];
                const cr2 = initialR[x + i + 1];

                let zr1 = cr1;
                let zi1 = ci;

                let zr2 = cr2;
                let zi2 = ci;

                let b = 0;

                for (let j = 0; j < maxIter; j++) {
                    const tr1 = zr1 * zr1;
                    const ti1 = zi1 * zi1;
                    zi1 = 2 * zr1 * zi1 + ci;
                    zr1 = tr1 - ti1 + cr1;

                    if (tr1 + ti1 > limit) {
                        b |= 2;
                        if (b === 3) {
                            break;
                        }
                    }

                    const tr2 = zr2 * zr2;
                    const ti2 = zi2 * zi2;
                    zi2 = 2 * zr2 * zi2 + ci;
                    zr2 = tr2 - ti2 + cr2;

                    if (tr2 + ti2 > limit) {
                        b |= 1;
                        if (b === 3) {
                            break;
                        }
                    }
                }
                res = (res << 2) | b;
            }
            row[xByte] = ~res;
        }
    }
}

interface Data {
    size: number;
    bytesPerRow: number;
}
    

notes, command-line, and program output

NOTES:
64-bit Ubuntu quad core
Version 4.6.2
node.js v17.8.0


Wed, 23 Mar 2022 21:10:57 GMT

MAKE:
mv mandelbrot.typescript-3.typescript mandelbrot.typescript-3.ts
/opt/src/node-v17.8.0/bin/tsc --target es2022 --strict --noEmitOnError --removeComments  mandelbrot.typescript-3.ts
mandelbrot.typescript-3.ts(55,25): error TS2794: Expected 1 arguments, but got 0. Did you forget to include 'void' in your type argument to 'Promise'?
make: [/home/dunham/all-benchmarksgame/2000-benchmarksgame/nanobench/makefiles/u64q.programs.Makefile:429: mandelbrot.typescript-3.typescript_run] Error 1 (ignored)

4.23s to complete and log all make actions

COMMAND LINE:
/opt/src/node-v17.8.0/bin/node --use_strict  mandelbrot.typescript-3.js 1000

PROGRAM FAILED 


(BINARY) PROGRAM OUTPUT NOT SHOWN

node:internal/modules/cjs/loader:936
  throw err;
  ^

Error: Cannot find module '/home/dunham/all-benchmarksgame/benchmarksgame_i53330/mandelbrot/tmp/mandelbrot.typescript-3.js'
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
    at node:internal/main/run_main_module:17:47 {
  code: 'MODULE_NOT_FOUND',
  requireStack: []
}

Node.js v17.8.0