Skip to content

Commit 23338ae

Browse files
committed
Update benchmarks (WIP)
1 parent f7b20ea commit 23338ae

File tree

11 files changed

+891
-78
lines changed

11 files changed

+891
-78
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pnpm-lock.yaml
2+
bun.lockb
23
package-lock.json
34
node_modules
45
dist

bench.js

+53-73
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,62 @@
1-
import deepDiff from "deep-diff";
21
import { detailedDiff } from "deep-object-diff";
3-
import { diffJson } from "diff";
2+
import { run, bench } from "mitata";
43
import microdiff from "./dist/index.js";
5-
import { hrtime } from "node:process";
4+
import { diffJson } from "diff";
5+
import deepDiff from "deep-diff";
66
import colors from "picocolors";
7-
const characters = "abcdefghijklmnopqrstuvwxyz1234567890".split("");
7+
import { readdirSync } from "node:fs";
8+
import { resolve } from "node:path";
9+
import { argv } from "node:process";
10+
const benchmarkType = argv.includes("--theoretical")
11+
? "theoretical"
12+
: "applied";
13+
const avgs = [];
14+
const benchmarks = readdirSync(resolve("./benchmarks", benchmarkType)).filter(
15+
(file) => !file.startsWith("_"),
16+
);
17+
console.log(`Running ${benchmarks.length} ${benchmarkType} benchmarks`);
18+
for (const file of benchmarks) {
19+
const benchmark = await import(resolve("benchmarks", benchmarkType, file));
20+
const obj = benchmark.original;
21+
const newObj = benchmark.changed;
822

9-
async function benchmark(name, obj, newObj, exclude = []) {
10-
const benchmarks = {
11-
"deep-diff": () => deepDiff.diff(obj, newObj),
12-
"deep-object-diff": () => detailedDiff(obj, newObj),
13-
jsdiff: () => diffJson(obj, newObj),
14-
microdiff: () => microdiff(obj, newObj),
15-
};
16-
let times = {};
17-
for (let benchmark in benchmarks) {
18-
if (exclude.includes(benchmark)) {
19-
continue;
20-
}
21-
times[benchmark] = [];
22-
for (let i = 1; i < 10000; i++) {
23-
let time = hrtime();
24-
benchmarks[benchmark]();
25-
times[benchmark].push(hrtime(time)[1]);
26-
}
27-
times[benchmark] =
28-
times[benchmark].reduce((pv, nv) => pv + nv) / times[benchmark].length;
29-
}
30-
let output = [];
31-
let fastest = "";
32-
for (let time in times) {
33-
if (!fastest || times[time] < times[fastest]) {
34-
fastest = time;
35-
}
36-
}
37-
for (let time in times) {
38-
output.push(
39-
`${time}: ${Math.round(times[time])}ns - ${
40-
fastest === time
41-
? colors.bold(colors.green("Fastest"))
42-
: `${Math.round((times[time] / times[fastest] - 1) * 100)}% slower`
43-
}`
44-
);
45-
}
46-
console.log(
47-
colors.bold(colors.green(`Benchmarks: ${name}\n`)) + output.join("\n")
23+
bench("microdiff (no cycles)", () =>
24+
microdiff(obj, newObj, { cyclesFix: false }),
25+
);
26+
bench("microdiff", () => microdiff(obj, newObj));
27+
bench("deep-diff", () => deepDiff.diff(obj, newObj));
28+
bench("deep-object-diff", () => detailedDiff(obj, newObj));
29+
bench("jsDiff", () => diffJson(obj, newObj));
30+
31+
console.log(colors.green(colors.bold(benchmark.name)));
32+
const res = await run();
33+
const baselineAvg = res.benchmarks.find(
34+
(subres) => subres.alias == "microdiff (no cycles)",
35+
).runs[0].stats.avg;
36+
avgs.push(
37+
res.benchmarks.map((subres) => ({
38+
alias: subres.alias,
39+
avg: subres.runs[0].stats.avg / baselineAvg,
40+
})),
4841
);
4942
}
50-
console.log(colors.bold("Starting Benchmark"));
51-
benchmark(
52-
"Small object (baseline)",
53-
{
54-
name: "Testing",
55-
propertyTwo: "Still testing...",
56-
},
57-
{
58-
name: "TestingChanged",
59-
propertyThree: "Still testing...",
60-
}
61-
);
62-
let largeObj = {};
63-
let i = 0;
64-
while (i < 300) {
65-
let randomString = "";
66-
for (let characterCount = 0; characterCount < 5; characterCount++) {
67-
randomString += characters[Math.round(Math.random() * characters.length)];
68-
}
69-
if (!largeObj[randomString]) {
70-
largeObj[randomString] = Math.random() * 100;
71-
i++;
43+
const mean = {};
44+
for (const ben of avgs) {
45+
for (const algo of ben) {
46+
if (!mean[algo.alias]) {
47+
mean[algo.alias] = algo.avg;
48+
} else mean[algo.alias] *= algo.avg;
7249
}
7350
}
74-
let newLargeObj = {};
75-
for (let randomProperty in largeObj) {
76-
if (Math.random() > 0.95) {
77-
newLargeObj[randomProperty] = Math.random() * 100;
78-
} else if (!Math.random() < 0.975) {
79-
newLargeObj[randomProperty] = largeObj[randomProperty];
80-
}
51+
console.log(
52+
colors.bold(
53+
colors.green(
54+
"Geometric mean of time per operation relative to Microdiff (no cycles) (100%==equal time, lower is better)",
55+
),
56+
),
57+
);
58+
for (const algo in mean) {
59+
console.log(
60+
`${algo}: ${Math.round(Math.pow(mean[algo], 1 / benchmarks.length) * 100)}%`,
61+
);
8162
}
82-
benchmark("Large Object (300 properties)", largeObj, newLargeObj);

benchmarks/_utils.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const characters = "abcdefghijklmnopqrstuvwxyz1234567890".split("");
2+
export function generateRandomString(len = 5) {
3+
let randomString = "";
4+
for (let characterCount = 0; characterCount < len; characterCount++) {
5+
randomString += characters[Math.round(Math.random() * characters.length)];
6+
}
7+
return randomString;
8+
}

0 commit comments

Comments
 (0)