-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
meta: add benchmark and placeholder equal
- Loading branch information
1 parent
bb76a4b
commit 07c0ccf
Showing
7 changed files
with
448 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
type SimpleObject = { | ||
a: number | ||
b: number | ||
} | ||
|
||
type NestedObject = { | ||
a: number | ||
b: string | ||
c: { | ||
d: number | ||
e: Array<string> | ||
f: { | ||
g: boolean | ||
h: number | ||
} | ||
} | ||
} | ||
|
||
type ArrayObject = { | ||
numbers: Array<number> | ||
strings: Array<string> | ||
mixed: Array<number | string> | ||
} | ||
|
||
export const MOCK_SIMPLE_OBJECTS = { | ||
first: { a: 1, b: 2 }, | ||
second: { a: 1, b: 2 }, | ||
third: { a: 1, b: 3 }, | ||
} satisfies Record<string, SimpleObject> | ||
|
||
export const MOCK_NESTED_OBJECTS = { | ||
first: { | ||
a: 1, | ||
b: "test", | ||
c: { | ||
d: 123, | ||
e: ["a", "b", "c"], | ||
f: { | ||
g: true, | ||
h: 456, | ||
}, | ||
}, | ||
}, | ||
second: { | ||
a: 1, | ||
b: "test", | ||
c: { | ||
d: 123, | ||
e: ["a", "b", "c"], | ||
f: { | ||
g: true, | ||
h: 457, | ||
}, | ||
}, | ||
}, | ||
} satisfies Record<string, NestedObject> | ||
|
||
export const MOCK_ARRAY_OBJECTS = { | ||
first: { | ||
numbers: [1, 2, 3, 4, 5], | ||
strings: ["a", "b", "c"], | ||
mixed: [1, "two", 3, "four"], | ||
}, | ||
second: { | ||
numbers: [1, 2, 3, 4, 5], | ||
strings: ["a", "b", "c"], | ||
mixed: [1, "two", 3, "four"], | ||
}, | ||
} satisfies Record<string, ArrayObject> | ||
|
||
export const MOCK_LARGE_OBJECTS = { | ||
first: Array.from({ length: 1000 }, (_, i) => ({ | ||
id: i, | ||
value: Math.random(), | ||
text: `Item ${i}`, | ||
active: i % 2 === 0, | ||
metadata: { | ||
created: new Date().toISOString(), | ||
modified: new Date().toISOString(), | ||
tags: [`tag${i}`, `category${i % 10}`], | ||
}, | ||
})), | ||
second: Array.from({ length: 1000 }, (_, i) => ({ | ||
id: i, | ||
value: Math.random(), | ||
text: `Item ${i}`, | ||
active: i % 2 === 0, | ||
metadata: { | ||
created: new Date().toISOString(), | ||
modified: new Date().toISOString(), | ||
tags: [`tag${i}`, `category${i % 10}`], | ||
}, | ||
})), | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import benchmark from "benchmark" | ||
import { deepEqual } from "../utils/equals" | ||
import _ from "lodash" | ||
import { MOCK_ARRAY_OBJECTS, MOCK_LARGE_OBJECTS, MOCK_NESTED_OBJECTS, MOCK_SIMPLE_OBJECTS } from "./__mocks__/objects" | ||
|
||
type BenchmarkResult = { | ||
"Test Name": string | ||
"Operations/sec": string | ||
Margin: string | ||
Runs: number | ||
} | ||
|
||
const suite = new benchmark.Suite() | ||
|
||
const testNames: readonly string[] = [ | ||
"Simple Objects - Identical (Fractures)", | ||
"Simple Objects - Identical (Lodash)", | ||
"Simple Objects - Different (Fractures)", | ||
"Simple Objects - Different (Lodash)", | ||
"Nested Objects (Fractures)", | ||
"Nested Objects (Lodash)", | ||
"Array Objects (Fractures)", | ||
"Array Objects (Lodash)", | ||
"Large Objects (Fractures)", | ||
"Large Objects (Lodash)", | ||
] | ||
|
||
const maxNameLength: number = Math.max(...testNames.map((name) => name.length)) | ||
const _padName = (name: string): string => name.padEnd(maxNameLength, " ") | ||
const allResults: BenchmarkResult[] = [] | ||
|
||
suite | ||
.add("Simple Objects - Identical (Fractures)", () => { | ||
deepEqual(MOCK_SIMPLE_OBJECTS.first, MOCK_SIMPLE_OBJECTS.second) | ||
}) | ||
.add("Simple Objects - Identical (Lodash)", () => { | ||
_.isEqual(MOCK_SIMPLE_OBJECTS.first, MOCK_SIMPLE_OBJECTS.second) | ||
}) | ||
.add("Simple Objects - Different (Fractures)", () => { | ||
deepEqual(MOCK_SIMPLE_OBJECTS.first, MOCK_SIMPLE_OBJECTS.third) | ||
}) | ||
.add("Simple Objects - Different (Lodash)", () => { | ||
_.isEqual(MOCK_SIMPLE_OBJECTS.first, MOCK_SIMPLE_OBJECTS.third) | ||
}) | ||
.add("Nested Objects (Fractures)", () => { | ||
deepEqual(MOCK_NESTED_OBJECTS.first, MOCK_NESTED_OBJECTS.second) | ||
}) | ||
.add("Nested Objects (Lodash)", () => { | ||
_.isEqual(MOCK_NESTED_OBJECTS.first, MOCK_NESTED_OBJECTS.second) | ||
}) | ||
.add("Array Objects (Fractures)", () => { | ||
deepEqual(MOCK_ARRAY_OBJECTS.first, MOCK_ARRAY_OBJECTS.second) | ||
}) | ||
.add("Array Objects (Lodash)", () => { | ||
_.isEqual(MOCK_ARRAY_OBJECTS.first, MOCK_ARRAY_OBJECTS.second) | ||
}) | ||
.add("Large Objects (Fractures)", () => { | ||
deepEqual(MOCK_LARGE_OBJECTS.first, MOCK_LARGE_OBJECTS.second) | ||
}) | ||
.add("Large Objects (Lodash)", () => { | ||
_.isEqual(MOCK_LARGE_OBJECTS.first, MOCK_LARGE_OBJECTS.second) | ||
}) | ||
.on("cycle", function (event: { target: benchmark.Target }) { | ||
const benchmark: benchmark.Target = event.target | ||
|
||
const result: BenchmarkResult = { | ||
"Test Name": _padName(benchmark?.name || "a mistery function"), | ||
"Operations/sec": Math.floor(benchmark?.hz ?? 0).toLocaleString(), | ||
Margin: `±${benchmark?.stats?.rme.toFixed(2)}%`, | ||
Runs: benchmark?.stats?.sample?.length ?? 0, | ||
} | ||
|
||
allResults.push(result) | ||
}) | ||
.on("complete", function () { | ||
console.table(allResults) | ||
}) | ||
.run({ async: true }) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"name": "@heliosgraphics/benchmark", | ||
"version": "0.0.0", | ||
"type": "module", | ||
"author": "03b8 <[email protected]>", | ||
"license": "MIT", | ||
"private": false, | ||
"description": "benchmark", | ||
"scripts": { | ||
"bench": "bun run *.ts" | ||
}, | ||
"engines": { | ||
"node": ">=20.15.0" | ||
}, | ||
"devDependencies": { | ||
"@types/uuid": "latest", | ||
"@types/benchmark": "latest", | ||
"@types/lodash": "latest", | ||
"benchmark": "latest", | ||
"lodash": "latest" | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { deepEqual, areSetsEqual } from "./equals" | ||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest" | ||
|
||
describe("deepEqual", () => { | ||
it("primitives", () => { | ||
expect(deepEqual(1, 1)).toBe(true) | ||
expect(deepEqual("hello", "hello")).toBe(true) | ||
expect(deepEqual(true, true)).toBe(true) | ||
expect(deepEqual(null, null)).toBe(true) | ||
expect(deepEqual(undefined, undefined)).toBe(true) | ||
expect(deepEqual(1, 2)).toBe(false) | ||
expect(deepEqual("hello", "world")).toBe(false) | ||
expect(deepEqual(true, false)).toBe(false) | ||
expect(deepEqual(null, undefined)).toBe(false) | ||
}) | ||
|
||
it("arrays", () => { | ||
expect(deepEqual([], [])).toBe(true) | ||
expect(deepEqual([1, 2, 3], [1, 2, 3])).toBe(true) | ||
expect(deepEqual([1, [2, 3]], [1, [2, 3]])).toBe(true) | ||
expect(deepEqual([1, 2], [1, 2, 3])).toBe(false) | ||
expect(deepEqual([1, 2, 3], [1, 3, 2])).toBe(false) | ||
}) | ||
|
||
it("objects", () => { | ||
expect(deepEqual({}, {})).toBe(true) | ||
expect(deepEqual({ a: 1, b: 2 }, { a: 1, b: 2 })).toBe(true) | ||
expect(deepEqual({ a: { b: 2 } }, { a: { b: 2 } })).toBe(true) | ||
expect(deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 })).toBe(true) | ||
expect(deepEqual({ a: 1 }, { a: 1, b: 2 })).toBe(false) | ||
expect(deepEqual({ a: 1 }, { a: 2 })).toBe(false) | ||
}) | ||
|
||
it("dates", () => { | ||
const date1 = new Date("2024-01-01") | ||
const date2 = new Date("2024-01-01") | ||
const date3 = new Date("2024-01-02") | ||
|
||
expect(deepEqual(date1, date2)).toBe(true) | ||
expect(deepEqual(date1, date3)).toBe(false) | ||
}) | ||
|
||
it("sets", () => { | ||
const set1 = new Set([1, 2, 3]) | ||
const set2 = new Set([1, 2, 3]) | ||
const set3 = new Set([1, 2, 4]) | ||
|
||
expect(deepEqual(set1, set2)).toBe(true) | ||
expect(deepEqual(set1, set3)).toBe(false) | ||
}) | ||
|
||
it("maps", () => { | ||
const map1 = new Map([ | ||
["a", 1], | ||
["b", 2], | ||
]) | ||
const map2 = new Map([ | ||
["a", 1], | ||
["b", 2], | ||
]) | ||
const map3 = new Map([ | ||
["a", 1], | ||
["b", 3], | ||
]) | ||
|
||
expect(deepEqual(map1, map2)).toBe(true) | ||
expect(deepEqual(map1, map3)).toBe(false) | ||
}) | ||
|
||
it("circular references", () => { | ||
const obj1: any = { a: 1 } | ||
const obj2: any = { a: 1 } | ||
obj1.self = obj1 | ||
obj2.self = obj2 | ||
|
||
expect(deepEqual(obj1, obj2)).toBe(true) | ||
}) | ||
}) | ||
|
||
describe("areSetsEqual", () => { | ||
it("simple sets without deep comparison", () => { | ||
const set1 = new Set([1, 2, 3]) | ||
const set2 = new Set([1, 2, 3]) | ||
const set3 = new Set([1, 2, 4]) | ||
|
||
expect(areSetsEqual(set1, set2)).toBe(true) | ||
expect(areSetsEqual(set1, set3)).toBe(false) | ||
}) | ||
|
||
it("sets with objects using deep comparison", () => { | ||
const set1 = new Set([{ a: 1 }, { b: 2 }]) | ||
const set2 = new Set([{ a: 1 }, { b: 2 }]) | ||
const set3 = new Set([{ a: 1 }, { b: 3 }]) | ||
|
||
expect(areSetsEqual(set1, set2, true)).toBe(true) | ||
expect(areSetsEqual(set1, set3, true)).toBe(false) | ||
}) | ||
|
||
it("sets with different sizes", () => { | ||
const set1 = new Set([1, 2, 3]) | ||
const set2 = new Set([1, 2]) | ||
|
||
expect(areSetsEqual(set1, set2)).toBe(false) | ||
expect(areSetsEqual(set1, set2, true)).toBe(false) | ||
}) | ||
|
||
it("sets with nested structures", () => { | ||
const set1 = new Set([{ a: { b: 2 } }, [1, 2, 3]]) | ||
const set2 = new Set([{ a: { b: 2 } }, [1, 2, 3]]) | ||
const set3 = new Set([{ a: { b: 3 } }, [1, 2, 3]]) | ||
|
||
expect(areSetsEqual(set1, set2, true)).toBe(true) | ||
expect(areSetsEqual(set1, set3, true)).toBe(false) | ||
}) | ||
|
||
it("empty sets", () => { | ||
const set1 = new Set() | ||
const set2 = new Set() | ||
|
||
expect(areSetsEqual(set1, set2)).toBe(true) | ||
expect(areSetsEqual(set1, set2, true)).toBe(true) | ||
}) | ||
}) |
Oops, something went wrong.