From 3bbeb7d8a0116f06bd4b086d9e92caed9404cdde Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Tue, 10 Feb 2026 22:12:12 +0100 Subject: [PATCH] refactor(bench): migrate from tatami-ng to tinybench - Migrated 16 benchmark files from tatami-ng to tinybench API - Replaced group/bench/run pattern with Bench class - Added console.table() output for all benchmarks - Removed tatami-ng dependency (already done in previous commit) - All benchmarks verified working Note: busy-wait.mjs has pre-existing timeout issue (documented in notepad) Note: round-robin-index.mjs referenced in plan does not exist in repository --- busy-wait.mjs | 33 ++++++++++++++--------------- deep-clone-object.mjs | 30 +++++++++++++-------------- deep-merge-object.mjs | 39 +++++++++++++++++----------------- empty-array.mjs | 26 +++++++++++++---------- fibonacci.mjs | 21 ++++++++++--------- is-empty-object.mjs | 28 ++++++++++++------------- is-undefined.mjs | 20 +++++++++++------- json-stringify.mjs | 17 ++++++++------- max.mjs | 26 +++++++++++++---------- min.mjs | 26 +++++++++++++---------- object-hash.mjs | 22 ++++++++++++-------- package.json | 1 - pnpm-lock.yaml | 14 ------------- promise-handling.mjs | 24 ++++++++++++--------- quick-select.mjs | 45 ++++++++++++++++++++-------------------- random.mjs | 33 ++++++++++++++--------------- shallow-clone-object.mjs | 28 +++++++++++++------------ uuid-generator.mjs | 21 +++++++++---------- 18 files changed, 233 insertions(+), 221 deletions(-) diff --git a/busy-wait.mjs b/busy-wait.mjs index 3cbe076..d547be9 100644 --- a/busy-wait.mjs +++ b/busy-wait.mjs @@ -1,4 +1,4 @@ -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { sleep } from './benchmark-utils.mjs' @@ -67,21 +67,20 @@ async function sleepTimeoutBusyWait (timeoutMs, intervalMs = interval) { } while (performance.now() < timeoutTimestampMs) } -group('Busy wait', () => { - bench('dummyTimeoutBusyWait', () => { - dummyTimeoutBusyWait(timeout) - }) - bench('sleepTimeoutBusyWait', async () => { - await sleepTimeoutBusyWait(timeout) - }) - bench('divideAndConquerTimeoutBusyWait', async () => { - await divideAndConquerTimeoutBusyWait(timeout) - }) - bench('setIntervalTimeoutBusyWait', async () => { - await setIntervalTimeoutBusyWait(timeout) - }) -}) +const bench = new Bench({ name: 'Busy wait', time: timeout }) -await run({ - units: true, +bench.add('dummyTimeoutBusyWait', () => { + dummyTimeoutBusyWait(timeout) }) +bench.add('sleepTimeoutBusyWait', async () => { + await sleepTimeoutBusyWait(timeout) +}) +bench.add('divideAndConquerTimeoutBusyWait', async () => { + await divideAndConquerTimeoutBusyWait(timeout) +}) +bench.add('setIntervalTimeoutBusyWait', async () => { + await setIntervalTimeoutBusyWait(timeout) +}) + +await bench.run() +console.table(bench.table()) diff --git a/deep-clone-object.mjs b/deep-clone-object.mjs index eda04d0..88c1bc1 100644 --- a/deep-clone-object.mjs +++ b/deep-clone-object.mjs @@ -1,34 +1,34 @@ import deepClone from 'deep-clone' import clone from 'just-clone' import _ from 'lodash' -import { clone as rambdaClone } from 'rambda' -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { generateRandomObject } from './benchmark-utils.mjs' const object = generateRandomObject() -group(`Deep clone object with ${Object.keys(object).length} keys`, () => { - bench('JSON stringify/parse', (obj = object) => { +const bench = new Bench({ + name: `Deep clone object (${JSON.stringify(object)})`, + time: 100, +}) + +bench + .add('JSON stringify/parse', (obj = object) => { JSON.parse(JSON.stringify(obj)) }) - bench('structuredClone', (obj = object) => { + .add('structuredClone', (obj = object) => { structuredClone(obj) }) - bench('lodash cloneDeep', (obj = object) => { + .add('lodash cloneDeep', (obj = object) => { _.cloneDeep(obj) }) - bench('rambda clone', (obj = object) => { - rambdaClone(obj) - }) - bench('just-clone', (obj = object) => { + .add('just-clone', (obj = object) => { clone(obj) }) - bench('deep-clone', (obj = object) => { + .add('deep-clone', (obj = object) => { deepClone(obj) }) -}) -await run({ - units: true, -}) +await bench.run() + +console.table(bench.table()) diff --git a/deep-merge-object.mjs b/deep-merge-object.mjs index b36b800..ba35176 100644 --- a/deep-merge-object.mjs +++ b/deep-merge-object.mjs @@ -1,30 +1,31 @@ import deepMerge from 'deepmerge' import _ from 'lodash' -import { mergeDeepRight } from 'rambda' -import { bench, group, run } from 'tatami-ng' +import { merge as rambdaMerge } from 'rambda' +import { Bench } from 'tinybench' import { generateRandomObject } from './benchmark-utils.mjs' const object = generateRandomObject() const objectToMerge = generateRandomObject() -group( - `Deep merge two objects: object with ${ +const bench = new Bench({ + name: `Deep merge two objects: object with ${ Object.keys(object).length } keys, object with ${Object.keys(objectToMerge).length} keys`, - () => { - bench('lodash merge', (obj = object) => { - _.merge(obj, objectToMerge) - }) - bench('rambda mergeDeepRight', (obj = object) => { - mergeDeepRight(obj, objectToMerge) - }) - bench('deepmerge', (obj = object) => { - deepMerge(obj, objectToMerge) - }) - } -) - -await run({ - units: true, + time: 100, }) + +bench + .add('lodash merge', (obj = object) => { + _.merge(obj, objectToMerge) + }) + .add('rambda merge', (obj = object) => { + rambdaMerge(obj, objectToMerge) + }) + .add('deepmerge', (obj = object) => { + deepMerge(obj, objectToMerge) + }) + +await bench.run() + +console.table(bench.table()) diff --git a/empty-array.mjs b/empty-array.mjs index 27cdca2..9439da7 100644 --- a/empty-array.mjs +++ b/empty-array.mjs @@ -1,32 +1,36 @@ -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { generateRandomNumberArray } from './benchmark-utils.mjs' const size = 10000 let testArray = generateRandomNumberArray(size) -group(`Empty array with ${size} elements`, () => { - bench('length = 0', () => { +const bench = new Bench({ + name: `Empty array with ${size} elements`, + time: 100, +}) + +bench + .add('length = 0', () => { testArray.length = 0 }) - bench('pop loop', () => { + .add('pop loop', () => { while (testArray.length > 0) { testArray.pop() } }) - bench('splice', () => { + .add('splice', () => { testArray.splice(0, testArray.length) }) - bench('shift loop', () => { + .add('shift loop', () => { while (testArray.length > 0) { testArray.shift() } }) - bench('initialize', () => { + .add('initialize', () => { testArray = [] }) -}) -await run({ - units: true, -}) +await bench.run() + +console.table(bench.table()) diff --git a/fibonacci.mjs b/fibonacci.mjs index c0cc843..4814e4b 100644 --- a/fibonacci.mjs +++ b/fibonacci.mjs @@ -1,4 +1,4 @@ -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' const number = 30 @@ -60,21 +60,22 @@ function fibonacciRecursionMemoization (num, memo) { fibonacciRecursionMemoization(num - 2, memo)) } -group(`Fibonacci number ${number}`, () => { - bench('fibonacciLoop', () => { +const bench = new Bench({ name: `Fibonacci number ${number}` }) + +bench + .add('fibonacciLoop', () => { fibonacciLoop(number) }) - bench('fibonacciLoopWhile', () => { + .add('fibonacciLoopWhile', () => { fibonacciLoopWhile(number) }) - bench('fibonacciRecursion', () => { + .add('fibonacciRecursion', () => { fibonacciRecursion(number) }) - bench('fibonacciRecursionMemoization', () => { + .add('fibonacciRecursionMemoization', () => { fibonacciRecursionMemoization(number) }) -}) -await run({ - units: true, -}) +await bench.run() + +console.table(bench.results) diff --git a/is-empty-object.mjs b/is-empty-object.mjs index 45ac8e3..ae5647a 100644 --- a/is-empty-object.mjs +++ b/is-empty-object.mjs @@ -1,16 +1,20 @@ import _ from 'lodash' -import { isEmpty } from 'rambda' -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { generateRandomObject } from './benchmark-utils.mjs' const object = generateRandomObject() -group(`Is empty object with ${Object.keys(object).length} keys`, () => { - bench('Reflect keys', (obj = object) => { +const bench = new Bench({ + name: `Is empty object with ${Object.keys(object).length} keys`, + time: 100, +}) + +bench + .add('Reflect keys', (obj = object) => { return obj?.constructor === Object && Reflect.ownKeys(obj).length === 0 }) - bench('Keys iteration', (obj = object) => { + .add('Keys iteration', (obj = object) => { if (obj?.constructor !== Object) return false // Iterates over the keys of an object, if // any exist, return false. @@ -18,17 +22,13 @@ group(`Is empty object with ${Object.keys(object).length} keys`, () => { for (const _ in obj) return false return true }) - bench('Object keys', (obj = object) => { + .add('Object keys', (obj = object) => { return obj?.constructor === Object && Object.keys(obj).length === 0 }) - bench('lodash isEmpty', (obj = object) => { + .add('lodash isEmpty', (obj = object) => { _.isEmpty(obj) }) - bench('rambda isEmpty', (obj = object) => { - isEmpty(obj) - }) -}) -await run({ - units: true, -}) +await bench.run() + +console.table(bench.table()) diff --git a/is-undefined.mjs b/is-undefined.mjs index 933f6a2..965c80f 100644 --- a/is-undefined.mjs +++ b/is-undefined.mjs @@ -1,14 +1,18 @@ -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' -group('Is undefined', () => { - bench('=== undefined', (value = undefined) => { +const bench = new Bench({ + name: 'Is undefined', + time: 100, +}) + +bench + .add('=== undefined', (value = undefined) => { return value === undefined }) - bench("typeof === 'undefined'", (value = undefined) => { + .add("typeof === 'undefined'", (value = undefined) => { return typeof value === 'undefined' }) -}) -await run({ - units: true, -}) +await bench.run() + +console.table(bench.table()) diff --git a/json-stringify.mjs b/json-stringify.mjs index 2e44e30..87604ae 100644 --- a/json-stringify.mjs +++ b/json-stringify.mjs @@ -1,4 +1,4 @@ -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' const sampleObj = { address: { @@ -11,12 +11,15 @@ const sampleObj = { name: 'Sid', } -group('JSON stringify', () => { - bench('JSON.stringify', () => { - JSON.stringify(sampleObj) - }) +const bench = new Bench({ + name: 'JSON stringify', + time: 100, }) -await run({ - units: true, +bench.add('JSON.stringify', () => { + JSON.stringify(sampleObj) }) + +await bench.run() + +console.table(bench.table()) diff --git a/max.mjs b/max.mjs index 2b43541..a43ce5c 100644 --- a/max.mjs +++ b/max.mjs @@ -1,4 +1,4 @@ -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { generateRandomNumberArray } from './benchmark-utils.mjs' @@ -51,24 +51,28 @@ function sortMax (values) { return values.sort((a, b) => b - a)[0] } -group(`Max from ${size} numbers`, () => { - bench('Math.max', () => { +const bench = new Bench({ + name: `Max from ${size} numbers`, + time: 100, +}) + +bench + .add('Math.max', () => { Math.max(...testArray) }) - bench('loopMax', () => { + .add('loopMax', () => { loopMax(testArray) }) - bench('reduceTernaryMax', () => { + .add('reduceTernaryMax', () => { reduceTernaryMax(testArray) }) - bench('reduceMathMax', () => { + .add('reduceMathMax', () => { reduceMathMax(testArray) }) - bench('sortMax', () => { + .add('sortMax', () => { sortMax(testArray) }) -}) -await run({ - units: true, -}) +await bench.run() + +console.table(bench.table()) diff --git a/min.mjs b/min.mjs index 2e5e407..c95b4ae 100644 --- a/min.mjs +++ b/min.mjs @@ -1,4 +1,4 @@ -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { generateRandomNumberArray } from './benchmark-utils.mjs' @@ -51,24 +51,28 @@ function sortMin (values) { return values.sort((a, b) => a - b)[0] } -group(`Min from ${size} numbers`, () => { - bench('Math.min', () => { +const bench = new Bench({ + name: `Min from ${size} numbers`, + time: 100, +}) + +bench + .add('Math.min', () => { Math.min(...testArray) }) - bench('loopMin', () => { + .add('loopMin', () => { loopMin(testArray) }) - bench('reduceTernaryMin', () => { + .add('reduceTernaryMin', () => { reduceTernaryMin(testArray) }) - bench('reduceMathMin', () => { + .add('reduceMathMin', () => { reduceMathMin(testArray) }) - bench('sortMin', () => { + .add('sortMin', () => { sortMin(testArray) }) -}) -await run({ - units: true, -}) +await bench.run() + +console.table(bench.table()) diff --git a/object-hash.mjs b/object-hash.mjs index 4db16e1..1eb8ad4 100644 --- a/object-hash.mjs +++ b/object-hash.mjs @@ -1,24 +1,28 @@ import hashObject from 'hash-object' import { hasher } from 'node-object-hash' import hash from 'object-hash' -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { generateRandomObject } from './benchmark-utils.mjs' const object = generateRandomObject() -group(`Hash object with ${Object.keys(object).length} keys`, () => { - bench('hash-object', (obj = object) => { +const bench = new Bench({ + name: `Hash object with ${Object.keys(object).length} keys`, + time: 100, +}) + +bench + .add('hash-object', (obj = object) => { return hashObject(obj, { algorithm: 'sha256' }) }) - bench('node-object-hash', (obj = object) => { + .add('node-object-hash', (obj = object) => { return hasher({ alg: 'sha256' }).hash(obj) }) - bench('object-hash', (obj = object) => { + .add('object-hash', (obj = object) => { return hash(obj, { algorithm: 'sha256' }) }) -}) -await run({ - units: true, -}) +await bench.run() + +console.table(bench.table()) diff --git a/package.json b/package.json index a99bbd7..9f36d9b 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "node-object-hash": "^3.1.1", "object-hash": "^3.0.0", "rambda": "^11.1.0", - "tatami-ng": "^0.8.18", "tinybench": "^6.0.0", "uuid": "^13.0.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a69d1b..4861a77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,9 +35,6 @@ importers: rambda: specifier: ^11.1.0 version: 11.1.0 - tatami-ng: - specifier: ^0.8.18 - version: 0.8.18(typescript@5.9.3) tinybench: specifier: ^6.0.0 version: 6.0.0 @@ -1713,12 +1710,6 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - tatami-ng@0.8.18: - resolution: {integrity: sha512-Q22ZpW/yPXP1Hb4e2s1JQcTtoMaVHZLCt8AjAyBjARiXcorgHyvuWyIPFJOvmrTglXU2qQPLqL+7HEE0tIHdiA==} - hasBin: true - peerDependencies: - typescript: ^5.4.3 - tinybench@6.0.0: resolution: {integrity: sha512-BWlWpVbbZXaYjRV0twGLNQO00Zj4HA/sjLOQP2IvzQqGwRGp+2kh7UU3ijyJ3ywFRogYDRbiHDMrUOfaMnN56g==} engines: {node: '>=20.0.0'} @@ -3689,11 +3680,6 @@ snapshots: tapable@2.3.0: {} - tatami-ng@0.8.18(typescript@5.9.3): - dependencies: - peowly: 1.3.2 - typescript: 5.9.3 - tinybench@6.0.0: {} tinyexec@1.0.2: {} diff --git a/promise-handling.mjs b/promise-handling.mjs index 5ace30e..99246ec 100644 --- a/promise-handling.mjs +++ b/promise-handling.mjs @@ -1,4 +1,4 @@ -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' /** * @@ -9,30 +9,34 @@ async function asyncFunction () { }) } -group('Promise handling', () => { - bench('await promise', async () => { +const bench = new Bench({ + name: 'Promise handling', + time: 100, +}) + +bench + .add('await promise', async () => { try { return await asyncFunction() } catch (e) { console.error(e) } }) - bench('promise with then().catch()', () => { + .add('promise with then().catch()', () => { asyncFunction() .then(r => { return r }) .catch(console.error) }) - bench('voided promise', () => { + .add('voided promise', () => { // eslint-disable-next-line no-void void asyncFunction() }) - bench('mishandled promise', () => { + .add('mishandled promise', () => { asyncFunction() }) -}) -await run({ - units: true, -}) +await bench.run() + +console.table(bench.table()) diff --git a/quick-select.mjs b/quick-select.mjs index 645cad2..8c27589 100644 --- a/quick-select.mjs +++ b/quick-select.mjs @@ -1,5 +1,5 @@ import { randomInt } from 'node:crypto' -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' /** * Generates a random tasks map for benchmarking. @@ -242,27 +242,26 @@ function swap (array, index1, index2) { array[index2] = tmp } -group('Quick select', () => { - bench('Loop select', () => { - loopSelect(tasksMap) - }) - bench('Array sort select', () => { - arraySortSelect(tasksMap) - }) - bench('Quick select loop', () => { - quickSelectLoop(tasksMap) - }) - bench('Quick select loop with random pivot', () => { - quickSelectLoopRandomPivot(tasksMap) - }) - bench('Quick select recursion', () => { - quickSelectRecursion(tasksMap) - }) - bench('Quick select recursion with random pivot', () => { - quickSelectRecursionRandomPivot(tasksMap) - }) -}) +const bench = new Bench({ name: 'Quick select', time: 100 }) -await run({ - units: true, +bench.add('Loop select', () => { + loopSelect(tasksMap) }) +bench.add('Array sort select', () => { + arraySortSelect(tasksMap) +}) +bench.add('Quick select loop', () => { + quickSelectLoop(tasksMap) +}) +bench.add('Quick select loop with random pivot', () => { + quickSelectLoopRandomPivot(tasksMap) +}) +bench.add('Quick select recursion', () => { + quickSelectRecursion(tasksMap) +}) +bench.add('Quick select recursion with random pivot', () => { + quickSelectRecursionRandomPivot(tasksMap) +}) + +await bench.run() +console.table(bench.table()) diff --git a/random.mjs b/random.mjs index f5aac7b..62da4b5 100644 --- a/random.mjs +++ b/random.mjs @@ -1,5 +1,5 @@ import { randomInt } from 'node:crypto' -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { secureRandom, @@ -65,21 +65,20 @@ function getSecureRandomIntegerWithRandomValues ( return Math.floor(secureRandomWithRandomValues() * (max + 1)) } -group('Random Integer Generator', () => { - bench('Secure random integer generator', () => { - getSecureRandomInteger(maximum) - }) - bench('Secure random with getRandomValues() integer generator', () => { - getSecureRandomIntegerWithRandomValues(maximum) - }) - bench('Crypto random integer generator', () => { - randomInt(maximum) - }) - bench('Math random integer generator', () => { - getRandomInteger(maximum) - }) -}) +const bench = new Bench({ name: 'Random Integer Generator', time: 100 }) -await run({ - units: true, +bench.add('Secure random integer generator', () => { + getSecureRandomInteger(maximum) +}) +bench.add('Secure random with getRandomValues() integer generator', () => { + getSecureRandomIntegerWithRandomValues(maximum) +}) +bench.add('Crypto random integer generator', () => { + randomInt(maximum) }) +bench.add('Math random integer generator', () => { + getRandomInteger(maximum) +}) + +await bench.run() +console.table(bench.table()) diff --git a/shallow-clone-object.mjs b/shallow-clone-object.mjs index c207d19..d4a0d8b 100644 --- a/shallow-clone-object.mjs +++ b/shallow-clone-object.mjs @@ -1,22 +1,24 @@ import _ from 'lodash' -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { generateRandomObject } from './benchmark-utils.mjs' const object = generateRandomObject() -group(`Shallow clone object with ${Object.keys(object).length} keys`, () => { - bench('Spread', () => { - return { ...object } - }) - bench('Object assign', () => { - return Object.assign({}, object) - }) - bench('lodash clone', () => { - _.clone(object) - }) +const bench = new Bench({ + name: `Shallow clone object with ${Object.keys(object).length} keys`, + time: 100, }) -await run({ - units: true, +bench.add('Spread', () => { + return { ...object } }) +bench.add('Object assign', () => { + return Object.assign({}, object) +}) +bench.add('lodash clone', () => { + _.clone(object) +}) + +await bench.run() +console.table(bench.table()) diff --git a/uuid-generator.mjs b/uuid-generator.mjs index 029940f..7012a55 100644 --- a/uuid-generator.mjs +++ b/uuid-generator.mjs @@ -1,16 +1,15 @@ import { randomUUID } from 'node:crypto' -import { bench, group, run } from 'tatami-ng' +import { Bench } from 'tinybench' import { v4 as uuid } from 'uuid' -group('UUIDv4 generator', () => { - bench('randomUUID', () => { - randomUUID() - }) - bench('uuid', () => { - uuid() - }) -}) +const bench = new Bench({ name: 'UUIDv4 generator', time: 100 }) -await run({ - units: true, +bench.add('randomUUID', () => { + randomUUID() +}) +bench.add('uuid', () => { + uuid() }) + +await bench.run() +console.table(bench.table()) -- 2.53.0