Created
June 24, 2026 11:30
-
-
Save fabiospampinato/77d207abaf3a358081ccd65175a29137 to your computer and use it in GitHub Desktop.
are-deeply-equal vs object-identity bench
This file contains hidden or 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
| /* Mini benchmark: are-deeply-equal vs object-identity */ | |
| import areDeeplyEqual from 'are-deeply-equal'; | |
| import { identify } from 'object-identity'; | |
| /* CONTENDERS */ | |
| // are-deeply-equal: direct deep comparison (can short-circuit) | |
| const isEqualDirect = ( a, b ) => areDeeplyEqual ( a, b ); | |
| // object-identity: equality via a canonical identity string (no short-circuit) | |
| const isEqualIdentity = ( a, b ) => identify ( a ) === identify ( b ); | |
| /* FIXTURES */ | |
| const makeSimple = () => ({ | |
| id: 42, | |
| name: 'fixture', | |
| active: true, | |
| ratio: 3.14, | |
| tags: [ 'a', 'b', 'c' ], | |
| meta: { created: 1700000000, nested: { x: 1, y: 2, z: [ 4, 5, 6 ] } } | |
| }); | |
| const makeBig = () => { | |
| const obj = {}; | |
| for ( let i = 0; i < 300; i++ ) obj[`key_${i}`] = i; | |
| return obj; | |
| }; | |
| const makeDeep = () => { | |
| let node = { value: 0 }; | |
| for ( let i = 1; i < 200; i++ ) node = { value: i, child: node }; | |
| return node; | |
| }; | |
| const makeLeafy = () => { | |
| const arr = []; | |
| for ( let i = 0; i < 1000; i++ ) arr.push ( i % 2 ? `s${i}` : i ); | |
| return arr; | |
| }; | |
| /* HELPERS */ | |
| // Mutate the very last leaf, so a difference only shows up at the deepest point | |
| const breakLast = value => { | |
| if ( Array.isArray ( value ) ) { | |
| const out = value.slice (); | |
| out[out.length - 1] = '__DIFFERENT__'; | |
| return out; | |
| } | |
| if ( value && typeof value === 'object' ) { | |
| const keys = Object.keys ( value ); | |
| const lastKey = keys[keys.length - 1]; | |
| return { ...value, [lastKey]: '__DIFFERENT__' }; | |
| } | |
| return '__DIFFERENT__'; | |
| }; | |
| const bench = ( name, fn, iterations ) => { | |
| for ( let i = 0; i < Math.min ( iterations, 5000 ); i++ ) fn (); // warmup | |
| const start = process.hrtime.bigint (); | |
| for ( let i = 0; i < iterations; i++ ) fn (); | |
| const elapsedMs = Number ( process.hrtime.bigint () - start ) / 1e6; | |
| const opsPerSec = iterations / ( elapsedMs / 1000 ); | |
| return { name, elapsedMs, opsPerSec }; | |
| }; | |
| const fmt = n => n.toLocaleString ( 'en-US', { maximumFractionDigits: 0 } ); | |
| const run = ( title, a, b, expected, iterations ) => { | |
| if ( isEqualDirect ( a, b ) !== expected || isEqualIdentity ( a, b ) !== expected ) { | |
| console.log ( ` ⚠ ${title}: contenders disagree (expected ${expected}), skipping` ); | |
| return; | |
| } | |
| const r1 = bench ( 'are-deeply-equal', () => isEqualDirect ( a, b ), iterations ); | |
| const r2 = bench ( 'object-identity ', () => isEqualIdentity ( a, b ), iterations ); | |
| const winner = r1.opsPerSec > r2.opsPerSec ? r1 : r2; | |
| const loser = r1.opsPerSec > r2.opsPerSec ? r2 : r1; | |
| const factor = ( winner.opsPerSec / loser.opsPerSec ).toFixed ( 2 ); | |
| console.log ( `▸ ${title} (${fmt ( iterations )} iterations)` ); | |
| console.log ( ` ${r1.name} ${fmt ( r1.opsPerSec ).padStart ( 12 )} ops/s (${r1.elapsedMs.toFixed ( 1 )} ms)` ); | |
| console.log ( ` ${r2.name} ${fmt ( r2.opsPerSec ).padStart ( 12 )} ops/s (${r2.elapsedMs.toFixed ( 1 )} ms)` ); | |
| console.log ( ` → ${winner.name.trim ()} is ${factor}× faster\n` ); | |
| }; | |
| /* RUN */ | |
| const cases = [ | |
| { label: 'simple', make: makeSimple, iterations: 200000 }, | |
| { label: 'big (300 keys)', make: makeBig, iterations: 50000 }, | |
| { label: 'deep (200 levels)', make: makeDeep, iterations: 50000 }, | |
| { label: 'leafy (1000 items)', make: makeLeafy, iterations: 50000 } | |
| ]; | |
| console.log ( '\n=== EQUAL path (values are deeply equal) ===\n' ); | |
| for ( const { label, make, iterations } of cases ) { | |
| run ( label, make (), make (), true, iterations ); | |
| } | |
| console.log ( '=== NOT-EQUAL path (differ only at the deepest/last leaf) ===\n' ); | |
| for ( const { label, make, iterations } of cases ) { | |
| run ( label, make (), breakLast ( make () ), false, iterations ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment