diff --git a/.github/workflows/compare.yml b/.github/workflows/compare.yml new file mode 100644 index 00000000..59f7436a --- /dev/null +++ b/.github/workflows/compare.yml @@ -0,0 +1,20 @@ +name: Compare +on: workflow_dispatch +jobs: + compare: + name: 'Compare' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.6.0 + - name: Using Node v16.x + uses: actions/setup-node@v3.6.0 + with: + node-version: '16.x' + - name: Install dependencies + run: yarn install --immutable + - name: Install extra libraries + run: yarn add chance@1.1.10 @faker-js/faker@7.6.0 random-js@2.1.0 --exact --dev + - name: Build package + run: yarn build:prod + - name: Benchmark + run: node perf/compare.cjs diff --git a/perf/compare.cjs b/perf/compare.cjs new file mode 100644 index 00000000..0cab405a --- /dev/null +++ b/perf/compare.cjs @@ -0,0 +1,80 @@ +// @ts-check +const { Bench } = require('tinybench'); +const prand = require('../lib/pure-rand'); +const Chance = require('chance'); +const { faker } = require('@faker-js/faker'); +const { Random, MersenneTwister19937 } = require('random-js'); + +// Algorithms under tests +function fisherYates(data, rand) { + // for i from n−1 downto 1 do + //j ← random integer such that 0 ≤ j ≤ i + //exchange a[j] and a[i] + for (let i = data.length - 1; i >= 1; --i) { + const j = rand(0, i); // such that 0 ≤ j ≤ i + const tmp = data[j]; + data[j] = data[i]; + data[i] = tmp; + } +} + +async function run() { + // Global Setup + const numIterations = 1_000; + const seed = Date.now() | 0; + const bench = new Bench({ warmupIterations: Math.ceil(numIterations / 20), iterations: numIterations }); + const data = [...Array(1_000_000)].map((_, i) => i); + + // Add algorithms (shuffling 1M items) + bench.add('[native]', () => { + const rand = (min, max) => { + return min + Math.floor(Math.random() * (max - min + 1)); + }; + fisherYates(data, rand); + }); + bench.add('pure-rand', () => { + const g = prand.xorshift128plus(seed); + const rand = (min, max) => { + return prand.unsafeUniformIntDistribution(min, max, g); + }; + fisherYates(data, rand); + }); + bench.add('chance', () => { + const chance = new Chance(); + const rand = (min, max) => { + return chance.integer({ min, max }); + }; + fisherYates(data, rand); + }); + bench.add('faker', () => { + const rand = (min, max) => { + return faker.datatype.number({ min, max }); + }; + fisherYates(data, rand); + }); + bench.add('random-js', () => { + const random = new Random(MersenneTwister19937.seed(seed)); + const rand = (min, max) => { + return random.integer(min, max); + }; + fisherYates(data, rand); + }); + + // Run the benchmark + await bench.warmup(); + await bench.run(); + + // Log the results + console.table( + bench.tasks.map(({ name, result }) => { + return { + Library: name, + Mean: result?.mean, + P75: result?.p75, + P99: result?.p99, + RME: result?.rme, + }; + }) + ); +} +run(); diff --git a/postbuild/main.cjs b/postbuild/main.cjs index c7306ab9..b370a9bc 100644 --- a/postbuild/main.cjs +++ b/postbuild/main.cjs @@ -1,4 +1,6 @@ // eslint-disable-next-line +const { execSync } = require('child_process'); +// eslint-disable-next-line const fs = require('fs'); // eslint-disable-next-line const path = require('path');